pub struct Index {
pub version: u32,
pub entries: Vec<IndexEntry>,
pub sparse_directories: bool,
pub untracked_cache: Option<UntrackedCache>,
pub fsmonitor_last_update: Option<String>,
pub resolve_undo: Option<BTreeMap<Vec<u8>, ResolveUndoRecord>>,
pub cache_tree_root: Option<ObjectId>,
pub cache_tree: Option<CacheTreeNode>,
/* private fields */
}Expand description
The in-memory representation of the Git index file.
Fields§
§version: u32Index format version (2 or 3).
entries: Vec<IndexEntry>Index entries, sorted by (path, stage).
sparse_directories: boolWhen true, the on-disk index includes the sdir extension (sparse index).
untracked_cache: Option<UntrackedCache>Optional untracked-cache extension (UNTR), matching Git’s istate->untracked.
fsmonitor_last_update: Option<String>Optional fsmonitor token extension (FSMN).
resolve_undo: Option<BTreeMap<Vec<u8>, ResolveUndoRecord>>Optional REUC resolve-undo extension (paths that were unmerged before a resolution).
cache_tree_root: Option<ObjectId>Root tree OID from a valid TREE index extension (cache_tree), when present.
cache_tree: Option<CacheTreeNode>Parsed TREE index extension (cache-tree) preserving invalid and subtree nodes.
Implementations§
Source§impl Index
impl Index
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new, empty index.
Respects GIT_INDEX_VERSION if set, otherwise defaults to version 2.
Examples found in repository?
9fn main() -> grit_lib::error::Result<()> {
10 let root = tempfile::tempdir()?;
11 let repo = init_repository(root.path(), false, "main", None, "files")?;
12
13 let blob_oid = repo.odb.write(ObjectKind::Blob, b"staged content\n")?;
14
15 let path = b"notes.txt".to_vec();
16 let entry = IndexEntry {
17 ctime_sec: 0,
18 ctime_nsec: 0,
19 mtime_sec: 0,
20 mtime_nsec: 0,
21 dev: 0,
22 ino: 0,
23 mode: MODE_REGULAR,
24 uid: 0,
25 gid: 0,
26 size: 0,
27 oid: blob_oid,
28 flags: (path.len().min(0xfff)) as u16,
29 flags_extended: None,
30 path,
31 base_index_pos: 0,
32 };
33
34 let mut index = Index::new();
35 index.add_or_replace(entry);
36 repo.write_index(&mut index)?;
37
38 let round_trip = repo.load_index()?;
39 println!("index entries: {}", round_trip.entries.len());
40 let first = &round_trip.entries[0];
41 println!(
42 "first path: {}, oid: {}",
43 String::from_utf8_lossy(&first.path),
44 first.oid
45 );
46
47 Ok(())
48}More examples
12fn make_initial_commit(repo: &Repository) -> grit_lib::error::Result<grit_lib::objects::ObjectId> {
13 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
14
15 let blob_oid = repo.odb.write(ObjectKind::Blob, b"log line\n")?;
16 let path = b"file.txt".to_vec();
17 let entry = IndexEntry {
18 ctime_sec: 0,
19 ctime_nsec: 0,
20 mtime_sec: 0,
21 mtime_nsec: 0,
22 dev: 0,
23 ino: 0,
24 mode: MODE_REGULAR,
25 uid: 0,
26 gid: 0,
27 size: 0,
28 oid: blob_oid,
29 flags: (path.len().min(0xfff)) as u16,
30 flags_extended: None,
31 path,
32 base_index_pos: 0,
33 };
34 let mut index = Index::new();
35 index.add_or_replace(entry);
36 repo.write_index(&mut index)?;
37 let index = repo.load_index()?;
38 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
39
40 let commit = CommitData {
41 tree: tree_oid,
42 parents: Vec::new(),
43 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
44 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
45 author_raw: Vec::new(),
46 committer_raw: Vec::new(),
47 encoding: None,
48 message: "root\n".to_owned(),
49 raw_message: None,
50 };
51 let oid = repo
52 .odb
53 .write(ObjectKind::Commit, &serialize_commit(&commit))?;
54 refs::write_ref(&repo.git_dir, "refs/heads/main", &oid)?;
55 Ok(oid)
56}11fn commit_tree(
12 repo: &grit_lib::repo::Repository,
13 parent: Option<grit_lib::objects::ObjectId>,
14 message: &str,
15) -> grit_lib::error::Result<grit_lib::objects::ObjectId> {
16 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
17
18 let blob_oid = repo.odb.write(ObjectKind::Blob, b"content\n")?;
19 let path = b"file.txt".to_vec();
20 let entry = IndexEntry {
21 ctime_sec: 0,
22 ctime_nsec: 0,
23 mtime_sec: 0,
24 mtime_nsec: 0,
25 dev: 0,
26 ino: 0,
27 mode: MODE_REGULAR,
28 uid: 0,
29 gid: 0,
30 size: 0,
31 oid: blob_oid,
32 flags: (path.len().min(0xfff)) as u16,
33 flags_extended: None,
34 path,
35 base_index_pos: 0,
36 };
37 let mut index = Index::new();
38 index.add_or_replace(entry);
39 repo.write_index(&mut index)?;
40 let index = repo.load_index()?;
41 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
42
43 let mut parents = Vec::new();
44 if let Some(p) = parent {
45 parents.push(p);
46 }
47 let commit = CommitData {
48 tree: tree_oid,
49 parents,
50 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
51 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
52 author_raw: Vec::new(),
53 committer_raw: Vec::new(),
54 encoding: None,
55 message: format!("{message}\n"),
56 raw_message: None,
57 };
58 repo.odb
59 .write(ObjectKind::Commit, &serialize_commit(&commit))
60}12fn main() -> grit_lib::error::Result<()> {
13 let root = tempfile::tempdir()?;
14 let repo = init_repository(root.path(), false, "main", None, "files")?;
15
16 let blob_oid = repo
17 .odb
18 .write(ObjectKind::Blob, b"hello from grit-lib examples\n")?;
19 let path = b"README".to_vec();
20 let entry = IndexEntry {
21 ctime_sec: 0,
22 ctime_nsec: 0,
23 mtime_sec: 0,
24 mtime_nsec: 0,
25 dev: 0,
26 ino: 0,
27 mode: MODE_REGULAR,
28 uid: 0,
29 gid: 0,
30 size: 0,
31 oid: blob_oid,
32 flags: (path.len().min(0xfff)) as u16,
33 flags_extended: None,
34 path,
35 base_index_pos: 0,
36 };
37
38 let mut index = Index::new();
39 index.add_or_replace(entry);
40 repo.write_index(&mut index)?;
41
42 let index = repo.load_index()?;
43 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
44 println!("tree: {tree_oid}");
45
46 let commit = CommitData {
47 tree: tree_oid,
48 parents: Vec::new(),
49 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
50 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
51 author_raw: Vec::new(),
52 committer_raw: Vec::new(),
53 encoding: None,
54 message: "initial example commit\n".to_owned(),
55 raw_message: None,
56 };
57 let raw = serialize_commit(&commit);
58 let commit_oid = repo.odb.write(ObjectKind::Commit, &raw)?;
59 println!("commit: {commit_oid}");
60
61 refs::write_ref(&repo.git_dir, "refs/heads/main", &commit_oid)?;
62 println!("updated refs/heads/main -> {commit_oid}");
63
64 Ok(())
65}11fn main() -> grit_lib::error::Result<()> {
12 let root = tempfile::tempdir()?;
13 let repo = init_repository(root.path(), false, "main", None, "files")?;
14
15 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
16 let blob_oid = repo.odb.write(ObjectKind::Blob, b"x\n")?;
17 let path = b"a".to_vec();
18 let entry = IndexEntry {
19 ctime_sec: 0,
20 ctime_nsec: 0,
21 mtime_sec: 0,
22 mtime_nsec: 0,
23 dev: 0,
24 ino: 0,
25 mode: MODE_REGULAR,
26 uid: 0,
27 gid: 0,
28 size: 0,
29 oid: blob_oid,
30 flags: 1,
31 flags_extended: None,
32 path,
33 base_index_pos: 0,
34 };
35 let mut index = Index::new();
36 index.add_or_replace(entry);
37 repo.write_index(&mut index)?;
38 let index = repo.load_index()?;
39 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
40 let commit = CommitData {
41 tree: tree_oid,
42 parents: Vec::new(),
43 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
44 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
45 author_raw: Vec::new(),
46 committer_raw: Vec::new(),
47 encoding: None,
48 message: "r\n".to_owned(),
49 raw_message: None,
50 };
51 let oid = repo
52 .odb
53 .write(ObjectKind::Commit, &serialize_commit(&commit))?;
54 refs::write_ref(&repo.git_dir, "refs/heads/main", &oid)?;
55
56 let head = resolve_revision(&repo, "HEAD")?;
57 let full = resolve_revision(&repo, &oid.to_hex())?;
58 println!("HEAD resolves to {head}");
59 println!("full hex resolves to {full}");
60 assert_eq!(head, full);
61
62 Ok(())
63}36fn main() -> grit_lib::error::Result<()> {
37 let root = tempfile::tempdir()?;
38 let repo = init_repository(root.path(), false, "main", None, "files")?;
39
40 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
41
42 let blob_a = repo.odb.write(ObjectKind::Blob, b"a\n")?;
43 let blob_b = repo.odb.write(ObjectKind::Blob, b"b\n")?;
44
45 let mut index = Index::new();
46 for (rel, oid) in [
47 (b"a.txt".as_slice(), blob_a),
48 (b"sub/b.txt".as_slice(), blob_b),
49 ] {
50 let entry = IndexEntry {
51 ctime_sec: 0,
52 ctime_nsec: 0,
53 mtime_sec: 0,
54 mtime_nsec: 0,
55 dev: 0,
56 ino: 0,
57 mode: MODE_REGULAR,
58 uid: 0,
59 gid: 0,
60 size: 0,
61 oid,
62 flags: (rel.len().min(0xfff)) as u16,
63 flags_extended: None,
64 path: rel.to_vec(),
65 base_index_pos: 0,
66 };
67 index.add_or_replace(entry);
68 }
69 repo.write_index(&mut index)?;
70 let index = repo.load_index()?;
71 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
72
73 let commit = CommitData {
74 tree: tree_oid,
75 parents: Vec::new(),
76 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
77 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
78 author_raw: Vec::new(),
79 committer_raw: Vec::new(),
80 encoding: None,
81 message: "tree walk\n".to_owned(),
82 raw_message: None,
83 };
84 let commit_oid = repo
85 .odb
86 .write(ObjectKind::Commit, &serialize_commit(&commit))?;
87 refs::write_ref(&repo.git_dir, "refs/heads/main", &commit_oid)?;
88
89 let head_commit = resolve_revision(&repo, "HEAD")?;
90 let commit_obj = repo.odb.read(&head_commit)?;
91 let parsed = grit_lib::objects::parse_commit(&commit_obj.data)?;
92 println!("walking tree at {}", parsed.tree);
93 walk_tree(&repo, parsed.tree, "")?;
94
95 Ok(())
96}Sourcepub fn empty(version: u32) -> Self
pub fn empty(version: u32) -> Self
Create a new, empty index at a fixed version without consulting
GIT_INDEX_VERSION or config.
Unlike Self::new, this never reads the environment and therefore never
emits the “GIT_INDEX_VERSION set, but the value is invalid” warning. Use it
for throwaway baseline indexes (e.g. sparse-index diff references) where the
version is irrelevant and Git resolves the format only once per operation.
Sourcepub fn new_with_config(
config_index_version: Option<&str>,
config_many_files: Option<&str>,
) -> Self
pub fn new_with_config( config_index_version: Option<&str>, config_many_files: Option<&str>, ) -> Self
Create a new empty index, respecting config values for version.
Priority matches Git’s prepare_repo_settings: GIT_INDEX_VERSION env, then
feature.manyFiles (implies version 4), then index.version (overrides version).
Sourcepub fn new_from_config(config: &ConfigSet) -> Self
pub fn new_from_config(config: &ConfigSet) -> Self
New empty index using a loaded ConfigSet (includes -c / GIT_CONFIG_PARAMETERS).
Same precedence as Self::new_with_config, but reads feature.manyFiles and
index.version from config.
Sourcepub fn load(path: &Path) -> Result<Self>
pub fn load(path: &Path) -> Result<Self>
Load an index from the given file path without expanding sparse-directory placeholders.
Returns an empty index if the file does not exist.
§Errors
Returns Error::IndexError if the file is present but corrupt.
Sourcepub fn load_expand_sparse(path: &Path, odb: &Odb) -> Result<Self>
pub fn load_expand_sparse(path: &Path, odb: &Odb) -> Result<Self>
Load an index and expand sparse-directory placeholders using the object database.
After a successful return, Index::sparse_directories is cleared and every
placeholder is replaced by the blob entries from the referenced tree.
Sourcepub fn load_expand_sparse_optional(path: &Path, odb: &Odb) -> Result<Self>
pub fn load_expand_sparse_optional(path: &Path, odb: &Odb) -> Result<Self>
Like Index::load_expand_sparse, but treats a missing index or Git’s
"file too short" placeholder as an empty index.
Sourcepub fn has_sparse_directory_placeholders(&self) -> bool
pub fn has_sparse_directory_placeholders(&self) -> bool
Returns true if the index contains sparse-index tree placeholders (MODE_TREE + skip-worktree).
Sourcepub fn expand_sparse_directory_placeholders(&mut self, odb: &Odb) -> Result<()>
pub fn expand_sparse_directory_placeholders(&mut self, odb: &Odb) -> Result<()>
Replace sparse-directory placeholder entries with all blob paths from their trees.
Each placeholder must reference a tree object. New entries are marked skip-worktree like Git’s
expanded index, except we keep sparse_directories false in memory after expansion.
Sourcepub fn try_collapse_sparse_directories(
&mut self,
odb: &Odb,
head_tree: &ObjectId,
patterns: &[String],
cone_mode: bool,
enable_sparse_index: bool,
) -> Result<()>
pub fn try_collapse_sparse_directories( &mut self, odb: &Odb, head_tree: &ObjectId, patterns: &[String], cone_mode: bool, enable_sparse_index: bool, ) -> Result<()>
Collapse consecutive skip-worktree subtrees into sparse-directory placeholders when
cone_mode is true and each directory is outside the sparse cone.
head_tree is the tree OID at HEAD. When enable_sparse_index is false, clears
Index::sparse_directories and returns without collapsing.
Sourcepub fn parse(data: &[u8]) -> Result<Self>
pub fn parse(data: &[u8]) -> Result<Self>
Parse index bytes (the whole file including trailing SHA-1).
§Errors
Returns Error::IndexError on structural problems.
Sourcepub fn write_to_path(&self, path: &Path, skip_hash: bool) -> Result<()>
pub fn write_to_path(&self, path: &Path, skip_hash: bool) -> Result<()>
Write this index to path with an explicit trailing-checksum policy.
When skip_hash is true, the trailing SHA-1 is written as all zeros (Git index.skipHash).
Sourcepub fn add_or_replace(&mut self, entry: IndexEntry)
pub fn add_or_replace(&mut self, entry: IndexEntry)
Add or replace an entry (matched by path + stage).
Examples found in repository?
9fn main() -> grit_lib::error::Result<()> {
10 let root = tempfile::tempdir()?;
11 let repo = init_repository(root.path(), false, "main", None, "files")?;
12
13 let blob_oid = repo.odb.write(ObjectKind::Blob, b"staged content\n")?;
14
15 let path = b"notes.txt".to_vec();
16 let entry = IndexEntry {
17 ctime_sec: 0,
18 ctime_nsec: 0,
19 mtime_sec: 0,
20 mtime_nsec: 0,
21 dev: 0,
22 ino: 0,
23 mode: MODE_REGULAR,
24 uid: 0,
25 gid: 0,
26 size: 0,
27 oid: blob_oid,
28 flags: (path.len().min(0xfff)) as u16,
29 flags_extended: None,
30 path,
31 base_index_pos: 0,
32 };
33
34 let mut index = Index::new();
35 index.add_or_replace(entry);
36 repo.write_index(&mut index)?;
37
38 let round_trip = repo.load_index()?;
39 println!("index entries: {}", round_trip.entries.len());
40 let first = &round_trip.entries[0];
41 println!(
42 "first path: {}, oid: {}",
43 String::from_utf8_lossy(&first.path),
44 first.oid
45 );
46
47 Ok(())
48}More examples
12fn make_initial_commit(repo: &Repository) -> grit_lib::error::Result<grit_lib::objects::ObjectId> {
13 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
14
15 let blob_oid = repo.odb.write(ObjectKind::Blob, b"log line\n")?;
16 let path = b"file.txt".to_vec();
17 let entry = IndexEntry {
18 ctime_sec: 0,
19 ctime_nsec: 0,
20 mtime_sec: 0,
21 mtime_nsec: 0,
22 dev: 0,
23 ino: 0,
24 mode: MODE_REGULAR,
25 uid: 0,
26 gid: 0,
27 size: 0,
28 oid: blob_oid,
29 flags: (path.len().min(0xfff)) as u16,
30 flags_extended: None,
31 path,
32 base_index_pos: 0,
33 };
34 let mut index = Index::new();
35 index.add_or_replace(entry);
36 repo.write_index(&mut index)?;
37 let index = repo.load_index()?;
38 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
39
40 let commit = CommitData {
41 tree: tree_oid,
42 parents: Vec::new(),
43 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
44 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
45 author_raw: Vec::new(),
46 committer_raw: Vec::new(),
47 encoding: None,
48 message: "root\n".to_owned(),
49 raw_message: None,
50 };
51 let oid = repo
52 .odb
53 .write(ObjectKind::Commit, &serialize_commit(&commit))?;
54 refs::write_ref(&repo.git_dir, "refs/heads/main", &oid)?;
55 Ok(oid)
56}11fn commit_tree(
12 repo: &grit_lib::repo::Repository,
13 parent: Option<grit_lib::objects::ObjectId>,
14 message: &str,
15) -> grit_lib::error::Result<grit_lib::objects::ObjectId> {
16 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
17
18 let blob_oid = repo.odb.write(ObjectKind::Blob, b"content\n")?;
19 let path = b"file.txt".to_vec();
20 let entry = IndexEntry {
21 ctime_sec: 0,
22 ctime_nsec: 0,
23 mtime_sec: 0,
24 mtime_nsec: 0,
25 dev: 0,
26 ino: 0,
27 mode: MODE_REGULAR,
28 uid: 0,
29 gid: 0,
30 size: 0,
31 oid: blob_oid,
32 flags: (path.len().min(0xfff)) as u16,
33 flags_extended: None,
34 path,
35 base_index_pos: 0,
36 };
37 let mut index = Index::new();
38 index.add_or_replace(entry);
39 repo.write_index(&mut index)?;
40 let index = repo.load_index()?;
41 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
42
43 let mut parents = Vec::new();
44 if let Some(p) = parent {
45 parents.push(p);
46 }
47 let commit = CommitData {
48 tree: tree_oid,
49 parents,
50 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
51 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
52 author_raw: Vec::new(),
53 committer_raw: Vec::new(),
54 encoding: None,
55 message: format!("{message}\n"),
56 raw_message: None,
57 };
58 repo.odb
59 .write(ObjectKind::Commit, &serialize_commit(&commit))
60}12fn main() -> grit_lib::error::Result<()> {
13 let root = tempfile::tempdir()?;
14 let repo = init_repository(root.path(), false, "main", None, "files")?;
15
16 let blob_oid = repo
17 .odb
18 .write(ObjectKind::Blob, b"hello from grit-lib examples\n")?;
19 let path = b"README".to_vec();
20 let entry = IndexEntry {
21 ctime_sec: 0,
22 ctime_nsec: 0,
23 mtime_sec: 0,
24 mtime_nsec: 0,
25 dev: 0,
26 ino: 0,
27 mode: MODE_REGULAR,
28 uid: 0,
29 gid: 0,
30 size: 0,
31 oid: blob_oid,
32 flags: (path.len().min(0xfff)) as u16,
33 flags_extended: None,
34 path,
35 base_index_pos: 0,
36 };
37
38 let mut index = Index::new();
39 index.add_or_replace(entry);
40 repo.write_index(&mut index)?;
41
42 let index = repo.load_index()?;
43 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
44 println!("tree: {tree_oid}");
45
46 let commit = CommitData {
47 tree: tree_oid,
48 parents: Vec::new(),
49 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
50 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
51 author_raw: Vec::new(),
52 committer_raw: Vec::new(),
53 encoding: None,
54 message: "initial example commit\n".to_owned(),
55 raw_message: None,
56 };
57 let raw = serialize_commit(&commit);
58 let commit_oid = repo.odb.write(ObjectKind::Commit, &raw)?;
59 println!("commit: {commit_oid}");
60
61 refs::write_ref(&repo.git_dir, "refs/heads/main", &commit_oid)?;
62 println!("updated refs/heads/main -> {commit_oid}");
63
64 Ok(())
65}11fn main() -> grit_lib::error::Result<()> {
12 let root = tempfile::tempdir()?;
13 let repo = init_repository(root.path(), false, "main", None, "files")?;
14
15 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
16 let blob_oid = repo.odb.write(ObjectKind::Blob, b"x\n")?;
17 let path = b"a".to_vec();
18 let entry = IndexEntry {
19 ctime_sec: 0,
20 ctime_nsec: 0,
21 mtime_sec: 0,
22 mtime_nsec: 0,
23 dev: 0,
24 ino: 0,
25 mode: MODE_REGULAR,
26 uid: 0,
27 gid: 0,
28 size: 0,
29 oid: blob_oid,
30 flags: 1,
31 flags_extended: None,
32 path,
33 base_index_pos: 0,
34 };
35 let mut index = Index::new();
36 index.add_or_replace(entry);
37 repo.write_index(&mut index)?;
38 let index = repo.load_index()?;
39 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
40 let commit = CommitData {
41 tree: tree_oid,
42 parents: Vec::new(),
43 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
44 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
45 author_raw: Vec::new(),
46 committer_raw: Vec::new(),
47 encoding: None,
48 message: "r\n".to_owned(),
49 raw_message: None,
50 };
51 let oid = repo
52 .odb
53 .write(ObjectKind::Commit, &serialize_commit(&commit))?;
54 refs::write_ref(&repo.git_dir, "refs/heads/main", &oid)?;
55
56 let head = resolve_revision(&repo, "HEAD")?;
57 let full = resolve_revision(&repo, &oid.to_hex())?;
58 println!("HEAD resolves to {head}");
59 println!("full hex resolves to {full}");
60 assert_eq!(head, full);
61
62 Ok(())
63}36fn main() -> grit_lib::error::Result<()> {
37 let root = tempfile::tempdir()?;
38 let repo = init_repository(root.path(), false, "main", None, "files")?;
39
40 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
41
42 let blob_a = repo.odb.write(ObjectKind::Blob, b"a\n")?;
43 let blob_b = repo.odb.write(ObjectKind::Blob, b"b\n")?;
44
45 let mut index = Index::new();
46 for (rel, oid) in [
47 (b"a.txt".as_slice(), blob_a),
48 (b"sub/b.txt".as_slice(), blob_b),
49 ] {
50 let entry = IndexEntry {
51 ctime_sec: 0,
52 ctime_nsec: 0,
53 mtime_sec: 0,
54 mtime_nsec: 0,
55 dev: 0,
56 ino: 0,
57 mode: MODE_REGULAR,
58 uid: 0,
59 gid: 0,
60 size: 0,
61 oid,
62 flags: (rel.len().min(0xfff)) as u16,
63 flags_extended: None,
64 path: rel.to_vec(),
65 base_index_pos: 0,
66 };
67 index.add_or_replace(entry);
68 }
69 repo.write_index(&mut index)?;
70 let index = repo.load_index()?;
71 let tree_oid = write_tree_from_index(&repo.odb, &index, "")?;
72
73 let commit = CommitData {
74 tree: tree_oid,
75 parents: Vec::new(),
76 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
77 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
78 author_raw: Vec::new(),
79 committer_raw: Vec::new(),
80 encoding: None,
81 message: "tree walk\n".to_owned(),
82 raw_message: None,
83 };
84 let commit_oid = repo
85 .odb
86 .write(ObjectKind::Commit, &serialize_commit(&commit))?;
87 refs::write_ref(&repo.git_dir, "refs/heads/main", &commit_oid)?;
88
89 let head_commit = resolve_revision(&repo, "HEAD")?;
90 let commit_obj = repo.odb.read(&head_commit)?;
91 let parsed = grit_lib::objects::parse_commit(&commit_obj.data)?;
92 println!("walking tree at {}", parsed.tree);
93 walk_tree(&repo, parsed.tree, "")?;
94
95 Ok(())
96}Sourcepub fn stage_file(&mut self, entry: IndexEntry)
pub fn stage_file(&mut self, entry: IndexEntry)
Stage a file at stage 0, removing any conflict stage entries (1, 2, 3)
for the same path. This is the correct behavior for git add on a
conflicted file during merge/cherry-pick resolution.
Sourcepub fn clear_resolve_undo(&mut self)
pub fn clear_resolve_undo(&mut self)
Drop all resolve-undo records (matches Git resolve_undo_clear_index).
Sourcepub fn take_resolve_undo_record(
&mut self,
path: &[u8],
) -> Option<ResolveUndoRecord>
pub fn take_resolve_undo_record( &mut self, path: &[u8], ) -> Option<ResolveUndoRecord>
Remove and return the resolve-undo record for path, if any.
Sourcepub fn install_unmerged_from_resolve_undo(
&mut self,
path: &[u8],
record: &ResolveUndoRecord,
)
pub fn install_unmerged_from_resolve_undo( &mut self, path: &[u8], record: &ResolveUndoRecord, )
Replace all index entries for path with unmerged stages from record.
Sourcepub fn unmerge_path_from_resolve_undo(&mut self, path: &[u8]) -> bool
pub fn unmerge_path_from_resolve_undo(&mut self, path: &[u8]) -> bool
Re-create unmerged index entries for path from the resolve-undo extension.
Returns true when a resolve-undo record existed and was consumed (Git unmerge_one).
Sourcepub fn remove(&mut self, path: &[u8]) -> bool
pub fn remove(&mut self, path: &[u8]) -> bool
Remove all entries matching the given path (all stages).
Returns true if at least one entry was removed.
Sourcepub fn remove_path_all_stages(&mut self, path: &[u8]) -> bool
pub fn remove_path_all_stages(&mut self, path: &[u8]) -> bool
Remove every index entry for path (all merge stages), like remove_file_from_index.
Returns whether any entry was removed.
Sourcepub fn invalidate_untracked_cache_for_path(&mut self, path: &str)
pub fn invalidate_untracked_cache_for_path(&mut self, path: &str)
Invalidate UNTR nodes affected by an index change (Git untracked_cache_*_index).
Sourcepub fn remove_descendants_under_path(&mut self, path: &str)
pub fn remove_descendants_under_path(&mut self, path: &str)
Remove every index entry whose path lies strictly under path (all stages).
Used when staging a file at path that replaces a former directory: Git removes
tracked paths like path/child from the index so they do not remain alongside
the new blob entry.
Sourcepub fn set_cache_tree(&mut self, cache_tree: CacheTreeNode)
pub fn set_cache_tree(&mut self, cache_tree: CacheTreeNode)
Replace the cache-tree extension with a valid tree.
Sourcepub fn clear_cache_tree(&mut self)
pub fn clear_cache_tree(&mut self)
Remove the cache-tree extension.
Sourcepub fn invalidate_cache_tree_for_path(&mut self, path: &[u8])
pub fn invalidate_cache_tree_for_path(&mut self, path: &[u8])
Mark cache-tree nodes affected by an index path change as invalid.
Mirrors Git’s do_invalidate_path (cache-tree.c): each ancestor node along path
is invalidated (entry_count = -1), and when the final path component names an existing
subtree node, that subtree is removed entirely. The removal is essential for the
directory→file transition (e.g. tracked dir a/b/ replaced by file a/b): without it,
stale descendant nodes (a/b/c, …) keep their positive entry_count and later trip
crate::write_tree::verify_cache_tree with “corrupted cache-tree has entries not present
in index”.
Sourcepub fn format_cache_tree_dump(&self) -> String
pub fn format_cache_tree_dump(&self) -> String
Format the parsed cache-tree extension like Git’s test-tool dump-cache-tree.
Sourcepub fn dump_cache_tree(&self, odb: &Odb) -> Result<String>
pub fn dump_cache_tree(&self, odb: &Odb) -> Result<String>
Produce test-tool dump-cache-tree output for this index.
Mirrors Git’s cmd__dump_cache_tree: the stored cache-tree (it) is
compared against a freshly computed reference (ref) built from the
current index entries with WRITE_TREE_DRY_RUN. Only nodes present in
both trees are dumped; the #(ref) divergence lines that Git would emit
are filtered out by the harness, so they are intentionally omitted here.
If the index has no stored cache-tree, output is empty (Git dumps nothing
because it is NULL).
§Errors
Returns an error if the reference cache-tree cannot be built (for example, if a tree object cannot be written to the object database).
Sourcepub fn dedup_paths_keep_last(&mut self)
pub fn dedup_paths_keep_last(&mut self)
Collapse duplicate (path, stage) entries, keeping the last one in current order.
A valid Git index never holds two entries with the same path and stage; add_index_entry
replaces an existing same-name entry in place. When grit builds an index by flattening a
tree that has duplicate path entries (see t4058-diff-duplicates), the naive flatten yields
several identical-path entries. This restores Git’s invariant by keeping the last entry for
each (path, stage) — matching add_index_entry’s replace-on-collision semantics where the
final tree entry for a path wins.
Sourcepub fn split_index_base_oid(&self) -> Option<ObjectId>
pub fn split_index_base_oid(&self) -> Option<ObjectId>
OID of the shared index when this index uses split-index mode (link extension).
Sourcepub fn get(&self, path: &[u8], stage: u8) -> Option<&IndexEntry>
pub fn get(&self, path: &[u8], stage: u8) -> Option<&IndexEntry>
Find an entry by path and stage (0 for normal entries).
Sourcepub fn get_mut(&mut self, path: &[u8], stage: u8) -> Option<&mut IndexEntry>
pub fn get_mut(&mut self, path: &[u8], stage: u8) -> Option<&mut IndexEntry>
Find a mutable entry by path and stage.
Sourcepub fn overlay_tree_on_index(
&mut self,
repo: &Repository,
treeish: &str,
prefix: &[u8],
) -> Result<()>
pub fn overlay_tree_on_index( &mut self, repo: &Repository, treeish: &str, prefix: &[u8], ) -> Result<()>
Merge tree contents from treeish into this index as virtual stage-1 entries, matching
Git’s overlay_tree_on_index used by git ls-files --with-tree.
Existing unmerged entries (stages 1–3) are shifted to stage 3 so stage 1 is free for the
overlay. Stage-1 paths that already exist at stage 0 are marked so ls-files can skip
them (Git’s CE_UPDATE on the stage-1 entry).
§Parameters
repo— repository whose object database is used to read the tree.treeish— revision or tree OID string (HEAD,HEAD~1, full SHA, etc.).prefix— optional path prefix (bytes, no trailing slash except empty); only paths under this prefix are considered from the tree. Pass empty slice for the full tree.
§Errors
Returns Error if treeish cannot be resolved, the tree cannot be read, or an object is
missing from the ODB.