Skip to main content

Index

Struct Index 

Source
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: u32

Index format version (2 or 3).

§entries: Vec<IndexEntry>

Index entries, sorted by (path, stage).

§sparse_directories: bool

When 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

Source

pub fn new() -> Self

Create a new, empty index.

Respects GIT_INDEX_VERSION if set, otherwise defaults to version 2.

Examples found in repository?
examples/index_add.rs (line 34)
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
Hide additional examples
examples/rev_list.rs (line 34)
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}
examples/merge_base.rs (line 37)
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}
examples/commit_tree.rs (line 38)
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}
examples/rev_parse.rs (line 35)
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}
examples/walk_tree.rs (line 45)
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}
Source

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.

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn has_sparse_directory_placeholders(&self) -> bool

Returns true if the index contains sparse-index tree placeholders (MODE_TREE + skip-worktree).

Source

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.

Source

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.

Source

pub fn parse(data: &[u8]) -> Result<Self>

Parse index bytes (the whole file including trailing SHA-1).

§Errors

Returns Error::IndexError on structural problems.

Source

pub fn write(&self, path: &Path) -> Result<()>

Write the index to a file, computing and appending the trailing SHA-1.

§Errors

Returns Error::Io on filesystem errors.

Source

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).

Source

pub fn add_or_replace(&mut self, entry: IndexEntry)

Add or replace an entry (matched by path + stage).

Examples found in repository?
examples/index_add.rs (line 35)
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
Hide additional examples
examples/rev_list.rs (line 35)
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}
examples/merge_base.rs (line 38)
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}
examples/commit_tree.rs (line 39)
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}
examples/rev_parse.rs (line 36)
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}
examples/walk_tree.rs (line 67)
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}
Source

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.

Source

pub fn clear_resolve_undo(&mut self)

Drop all resolve-undo records (matches Git resolve_undo_clear_index).

Source

pub fn take_resolve_undo_record( &mut self, path: &[u8], ) -> Option<ResolveUndoRecord>

Remove and return the resolve-undo record for path, if any.

Source

pub fn install_unmerged_from_resolve_undo( &mut self, path: &[u8], record: &ResolveUndoRecord, )

Replace all index entries for path with unmerged stages from record.

Source

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).

Source

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.

Source

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.

Source

pub fn invalidate_untracked_cache_for_path(&mut self, path: &str)

Invalidate UNTR nodes affected by an index change (Git untracked_cache_*_index).

Source

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.

Source

pub fn set_cache_tree(&mut self, cache_tree: CacheTreeNode)

Replace the cache-tree extension with a valid tree.

Source

pub fn clear_cache_tree(&mut self)

Remove the cache-tree extension.

Source

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”.

Source

pub fn format_cache_tree_dump(&self) -> String

Format the parsed cache-tree extension like Git’s test-tool dump-cache-tree.

Source

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).

Source

pub fn sort(&mut self)

Sort entries in Git’s canonical order: by path, then by stage.

Source

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.

Source

pub fn split_index_base_oid(&self) -> Option<ObjectId>

OID of the shared index when this index uses split-index mode (link extension).

Source

pub fn get(&self, path: &[u8], stage: u8) -> Option<&IndexEntry>

Find an entry by path and stage (0 for normal entries).

Source

pub fn get_mut(&mut self, path: &[u8], stage: u8) -> Option<&mut IndexEntry>

Find a mutable entry by path and stage.

Source

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.

Trait Implementations§

Source§

impl Clone for Index

Source§

fn clone(&self) -> Index

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Index

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Index

Source§

fn default() -> Index

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Index

§

impl RefUnwindSafe for Index

§

impl Send for Index

§

impl Sync for Index

§

impl Unpin for Index

§

impl UnsafeUnpin for Index

§

impl UnwindSafe for Index

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.