pub struct Odb { /* private fields */ }Expand description
A loose-object database rooted at a given objects/ directory.
Implementations§
Source§impl Odb
impl Odb
Sourcepub fn new(objects_dir: &Path) -> Self
pub fn new(objects_dir: &Path) -> Self
Create an Odb pointing at the given objects/ directory.
The directory does not need to exist yet; it will be created on the first write operation.
Sourcepub fn with_work_tree(objects_dir: &Path, work_tree: &Path) -> Self
pub fn with_work_tree(objects_dir: &Path, work_tree: &Path) -> Self
Create an Odb with a work tree for resolving relative alternate paths.
Sourcepub fn enable_mem_overlay(&self)
pub fn enable_mem_overlay(&self)
Enable the in-memory write overlay (Git’s tmp-objdir): subsequent Self::write/
Self::write_local calls keep objects only in memory, and Self::read consults that
overlay before the on-disk store. Used by merge-tree --quiet so a full merge can run
without persisting any new loose objects.
Sourcepub fn disable_mem_overlay(&self)
pub fn disable_mem_overlay(&self)
Disable the in-memory write overlay, discarding any objects accumulated in it.
Sourcepub fn register_submodule_object_directories_from_index(
&self,
work_tree: &Path,
index: &Index,
)
pub fn register_submodule_object_directories_from_index( &self, work_tree: &Path, index: &Index, )
Register <submodule-git-dir>/objects for every stage-0 gitlink in index that has a
checkout under work_tree, so reads can resolve submodule commits stored only in the
nested repository (matches Git’s odb_add_submodule_source_by_path / register_all_submodule_sources).
Sourcepub fn with_config_git_dir(self, git_dir: PathBuf) -> Self
pub fn with_config_git_dir(self, git_dir: PathBuf) -> Self
Attach a git directory so Self::read can honor core.multiPackIndex when resolving packed objects.
Sourcepub fn hash_algo(&self) -> HashAlgo
pub fn hash_algo(&self) -> HashAlgo
The repository’s object hash algorithm, detected from
extensions.objectformat and cached for the lifetime of this Odb.
The git directory is taken from the attached config_git_dir when set,
otherwise inferred as the parent of the objects/ directory. Defaults
to HashAlgo::Sha1 when no config can be read.
Sourcepub fn objects_dir(&self) -> &Path
pub fn objects_dir(&self) -> &Path
Return the path to the objects/ directory.
Sourcepub fn config_git_dir(&self) -> Option<&Path>
pub fn config_git_dir(&self) -> Option<&Path>
Return the attached git directory, if one was set with
Self::with_config_git_dir.
Used by config- and ref-aware operations (delta islands, on-disk delta reuse) that need the repository root rather than just the object store.
Sourcepub fn object_path(&self, oid: &ObjectId) -> PathBuf
pub fn object_path(&self, oid: &ObjectId) -> PathBuf
Return the filesystem path for a given object ID.
Sourcepub fn exists_local(&self, oid: &ObjectId) -> bool
pub fn exists_local(&self, oid: &ObjectId) -> bool
Whether the object exists under this database directory only (loose or local packs).
Unlike Self::exists, this ignores info/alternates and
GIT_ALTERNATE_OBJECT_DIRECTORIES. Used for partial-clone bookkeeping where
objects reachable via alternates are still treated as “missing” until copied locally.
Objects stored only in promisor packs (sibling .promisor marker next to the
.pack) are treated as absent: Git considers them fetchable on demand, and
rev-list --missing=print lists them until materialized as loose objects or a
non-promisor pack.
The empty tree object is treated as present without a loose file (matches Git).
Sourcepub fn exists(&self, oid: &ObjectId) -> bool
pub fn exists(&self, oid: &ObjectId) -> bool
Check whether an object exists in the loose store or any pack file.
Sourcepub fn freshen_object(&self, oid: &ObjectId) -> bool
pub fn freshen_object(&self, oid: &ObjectId) -> bool
Touch the loose object file or pack file containing oid, matching Git’s
odb_freshen_object (updates mtime so age-based prune keeps recently re-referenced objects).
Returns true if an on-disk object was found and touched.
Sourcepub fn read_loose_verify_oid(
path: &Path,
expected_oid: &ObjectId,
) -> Result<Object>
pub fn read_loose_verify_oid( path: &Path, expected_oid: &ObjectId, ) -> Result<Object>
Read a loose object file at path, verifying the uncompressed payload hashes to expected_oid.
Git stores loose objects under paths derived from the OID; if the file contents hash to a
different id (for example after a mistaken mv), this returns Error::LooseHashMismatch.
§Errors
Error::Zlib— decompression failed.Error::CorruptObject— header is malformed.Error::LooseHashMismatch— payload OID does not matchexpected_oid.
Sourcepub fn read(&self, oid: &ObjectId) -> Result<Object>
pub fn read(&self, oid: &ObjectId) -> Result<Object>
Read and decompress an object from the loose store.
§Errors
Error::ObjectNotFound— no file at the expected path.Error::Zlib— decompression failed.Error::CorruptObject— header is malformed.
Examples found in repository?
40fn tree_of_commit(
41 repo: &grit_lib::repo::Repository,
42 commit_oid: grit_lib::objects::ObjectId,
43) -> grit_lib::error::Result<grit_lib::objects::ObjectId> {
44 let obj = repo.odb.read(&commit_oid)?;
45 Ok(parse_commit(&obj.data)?.tree)
46}
47
48fn main() -> grit_lib::error::Result<()> {
49 let root = tempfile::tempdir()?;
50 let repo = init_repository(root.path(), false, "main", None, "files")?;
51
52 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
53
54 // Base commit on main: one file.
55 let blob_a = repo.odb.write(ObjectKind::Blob, b"base\n")?;
56 let mut index = Index::new();
57 index.add_or_replace(IndexEntry {
58 ctime_sec: 0,
59 ctime_nsec: 0,
60 mtime_sec: 0,
61 mtime_nsec: 0,
62 dev: 0,
63 ino: 0,
64 mode: MODE_REGULAR,
65 uid: 0,
66 gid: 0,
67 size: 0,
68 oid: blob_a,
69 flags: 7,
70 flags_extended: None,
71 path: b"base.txt".to_vec(),
72 base_index_pos: 0,
73 });
74 repo.write_index(&mut index)?;
75 let index = repo.load_index()?;
76 let tree_a = write_tree_from_index(&repo.odb, &index, "")?;
77 let commit_a = commit_from_tree(&repo, tree_a, &[], "initial\n")?;
78 refs::write_ref(&repo.git_dir, "refs/heads/main", &commit_a)?;
79
80 // Topic commit: parent A, adds picked.txt (not on main yet).
81 let blob_pick = repo.odb.write(ObjectKind::Blob, b"hello from topic\n")?;
82 let mut index = Index::new();
83 index.add_or_replace(IndexEntry {
84 ctime_sec: 0,
85 ctime_nsec: 0,
86 mtime_sec: 0,
87 mtime_nsec: 0,
88 dev: 0,
89 ino: 0,
90 mode: MODE_REGULAR,
91 uid: 0,
92 gid: 0,
93 size: 0,
94 oid: blob_a,
95 flags: 7,
96 flags_extended: None,
97 path: b"base.txt".to_vec(),
98 base_index_pos: 0,
99 });
100 index.add_or_replace(IndexEntry {
101 ctime_sec: 0,
102 ctime_nsec: 0,
103 mtime_sec: 0,
104 mtime_nsec: 0,
105 dev: 0,
106 ino: 0,
107 mode: MODE_REGULAR,
108 uid: 0,
109 gid: 0,
110 size: 0,
111 oid: blob_pick,
112 flags: 9,
113 flags_extended: None,
114 path: b"picked.txt".to_vec(),
115 base_index_pos: 0,
116 });
117 repo.write_index(&mut index)?;
118 let index = repo.load_index()?;
119 let tree_b = write_tree_from_index(&repo.odb, &index, "")?;
120 let commit_b = commit_from_tree(&repo, tree_b, &[commit_a], "add picked file\n")?;
121 refs::write_ref(&repo.git_dir, "refs/heads/topic", &commit_b)?;
122
123 // Cherry-pick `topic` onto `main` (still at A).
124 let head = resolve_revision(&repo, "main")?;
125 let picked = resolve_revision(&repo, "topic")?;
126 let picked_obj = repo.odb.read(&picked)?;
127 let picked_data = parse_commit(&picked_obj.data)?;
128 let parent = picked_data.parents.first().copied().ok_or_else(|| {
129 grit_lib::error::Error::CorruptObject("picked commit has no parent".into())
130 })?;
131
132 let base_tree = tree_of_commit(&repo, parent)?;
133 let ours_tree = tree_of_commit(&repo, head)?;
134 let theirs_tree = picked_data.tree;
135
136 let merged = merge_trees_three_way(
137 &repo,
138 base_tree,
139 ours_tree,
140 theirs_tree,
141 MergeFavor::default(),
142 WhitespaceMergeOptions::default(),
143 None,
144 grit_lib::merge_trees::TreeMergeConflictPresentation {
145 label_ours: "HEAD",
146 label_theirs: grit_lib::merge_trees::TheirsConflictLabel::Fixed("picked"),
147 label_base: "parent of picked commit",
148 style: grit_lib::merge_file::ConflictStyle::Merge,
149 checkout_merge: false,
150 },
151 )?;
152
153 if !merged.conflict_content.is_empty() {
154 return Err(grit_lib::error::Error::Message(format!(
155 "merge produced {} conflict path(s); this example expects a clean pick",
156 merged.conflict_content.len()
157 )));
158 }
159
160 let new_tree = write_tree_from_index(&repo.odb, &merged.index, "")?;
161 let config = ConfigSet::load_repo_local_only(&repo.git_dir)?;
162 let msg = commit_trailers::finalize_cherry_pick_message(
163 &picked_data.message,
164 true,
165 false,
166 "Example",
167 "example@example.com",
168 &config,
169 &picked.to_hex(),
170 );
171 let new_commit = commit_from_tree(&repo, new_tree, &[head], &msg)?;
172 refs::write_ref(&repo.git_dir, "refs/heads/main", &new_commit)?;
173
174 println!("cherry-picked {} onto {}", picked, head);
175 println!("new main: {new_commit}");
176 let out = repo.odb.read(&new_commit)?;
177 println!("message:\n{}", parse_commit(&out.data)?.message);
178
179 Ok(())
180}More examples
8fn main() -> grit_lib::error::Result<()> {
9 let root = tempfile::tempdir()?;
10 let repo = init_repository(root.path(), false, "main", None, "files")?;
11
12 let payload = b"hello, object database\n";
13 let oid = repo.odb.write(ObjectKind::Blob, payload)?;
14 println!("stored blob: {oid}");
15
16 let Object { kind, data } = repo.odb.read(&oid)?;
17 assert_eq!(kind, ObjectKind::Blob);
18 println!("round-trip: {}", String::from_utf8_lossy(&data));
19
20 Ok(())
21}12fn walk_tree(
13 repo: &grit_lib::repo::Repository,
14 tree_oid: grit_lib::objects::ObjectId,
15 prefix: &str,
16) -> grit_lib::error::Result<()> {
17 let obj = repo.odb.read(&tree_oid)?;
18 let entries = parse_tree(&obj.data)?;
19 for e in entries {
20 let name = String::from_utf8_lossy(&e.name);
21 let path = if prefix.is_empty() {
22 name.into_owned()
23 } else {
24 format!("{prefix}/{name}")
25 };
26 if e.mode == MODE_TREE {
27 println!("{path}/ (tree {})", e.oid);
28 walk_tree(repo, e.oid, &path)?;
29 } else {
30 println!("{path} -> {}", e.oid);
31 }
32 }
33 Ok(())
34}
35
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 hash_object_data(kind: ObjectKind, data: &[u8]) -> ObjectId
pub fn hash_object_data(kind: ObjectKind, data: &[u8]) -> ObjectId
Hash raw content of a given kind with SHA-1 and return the ObjectId.
This does not write anything to disk. Prefer Self::hash when a
repository hash algorithm is available, so SHA-256 repositories are
handled correctly.
Sourcepub fn hash(&self, kind: ObjectKind, data: &[u8]) -> ObjectId
pub fn hash(&self, kind: ObjectKind, data: &[u8]) -> ObjectId
Hash raw content of a given kind using this repository’s hash algorithm.
This does not write anything to disk.
Sourcepub fn write(&self, kind: ObjectKind, data: &[u8]) -> Result<ObjectId>
pub fn write(&self, kind: ObjectKind, data: &[u8]) -> Result<ObjectId>
Write an object to the loose store and return its ObjectId.
If the object already exists it is not overwritten (Git behaviour).
§Errors
Error::Io— could not create the directory or write the file.Error::Zlib— compression failed.
Examples found in repository?
8fn main() -> grit_lib::error::Result<()> {
9 let root = tempfile::tempdir()?;
10 let repo = init_repository(root.path(), false, "main", None, "files")?;
11
12 let payload = b"hello, object database\n";
13 let oid = repo.odb.write(ObjectKind::Blob, payload)?;
14 println!("stored blob: {oid}");
15
16 let Object { kind, data } = repo.odb.read(&oid)?;
17 assert_eq!(kind, ObjectKind::Blob);
18 println!("round-trip: {}", String::from_utf8_lossy(&data));
19
20 Ok(())
21}More examples
19fn commit_from_tree(
20 repo: &grit_lib::repo::Repository,
21 tree: grit_lib::objects::ObjectId,
22 parents: &[grit_lib::objects::ObjectId],
23 message: &str,
24) -> grit_lib::error::Result<grit_lib::objects::ObjectId> {
25 let commit = CommitData {
26 tree,
27 parents: parents.to_vec(),
28 author: "Example <example@example.com> 1700000000 +0000".to_owned(),
29 committer: "Example <example@example.com> 1700000000 +0000".to_owned(),
30 author_raw: Vec::new(),
31 committer_raw: Vec::new(),
32 encoding: None,
33 message: message.to_owned(),
34 raw_message: None,
35 };
36 repo.odb
37 .write(ObjectKind::Commit, &serialize_commit(&commit))
38}
39
40fn tree_of_commit(
41 repo: &grit_lib::repo::Repository,
42 commit_oid: grit_lib::objects::ObjectId,
43) -> grit_lib::error::Result<grit_lib::objects::ObjectId> {
44 let obj = repo.odb.read(&commit_oid)?;
45 Ok(parse_commit(&obj.data)?.tree)
46}
47
48fn main() -> grit_lib::error::Result<()> {
49 let root = tempfile::tempdir()?;
50 let repo = init_repository(root.path(), false, "main", None, "files")?;
51
52 use grit_lib::index::{Index, IndexEntry, MODE_REGULAR};
53
54 // Base commit on main: one file.
55 let blob_a = repo.odb.write(ObjectKind::Blob, b"base\n")?;
56 let mut index = Index::new();
57 index.add_or_replace(IndexEntry {
58 ctime_sec: 0,
59 ctime_nsec: 0,
60 mtime_sec: 0,
61 mtime_nsec: 0,
62 dev: 0,
63 ino: 0,
64 mode: MODE_REGULAR,
65 uid: 0,
66 gid: 0,
67 size: 0,
68 oid: blob_a,
69 flags: 7,
70 flags_extended: None,
71 path: b"base.txt".to_vec(),
72 base_index_pos: 0,
73 });
74 repo.write_index(&mut index)?;
75 let index = repo.load_index()?;
76 let tree_a = write_tree_from_index(&repo.odb, &index, "")?;
77 let commit_a = commit_from_tree(&repo, tree_a, &[], "initial\n")?;
78 refs::write_ref(&repo.git_dir, "refs/heads/main", &commit_a)?;
79
80 // Topic commit: parent A, adds picked.txt (not on main yet).
81 let blob_pick = repo.odb.write(ObjectKind::Blob, b"hello from topic\n")?;
82 let mut index = Index::new();
83 index.add_or_replace(IndexEntry {
84 ctime_sec: 0,
85 ctime_nsec: 0,
86 mtime_sec: 0,
87 mtime_nsec: 0,
88 dev: 0,
89 ino: 0,
90 mode: MODE_REGULAR,
91 uid: 0,
92 gid: 0,
93 size: 0,
94 oid: blob_a,
95 flags: 7,
96 flags_extended: None,
97 path: b"base.txt".to_vec(),
98 base_index_pos: 0,
99 });
100 index.add_or_replace(IndexEntry {
101 ctime_sec: 0,
102 ctime_nsec: 0,
103 mtime_sec: 0,
104 mtime_nsec: 0,
105 dev: 0,
106 ino: 0,
107 mode: MODE_REGULAR,
108 uid: 0,
109 gid: 0,
110 size: 0,
111 oid: blob_pick,
112 flags: 9,
113 flags_extended: None,
114 path: b"picked.txt".to_vec(),
115 base_index_pos: 0,
116 });
117 repo.write_index(&mut index)?;
118 let index = repo.load_index()?;
119 let tree_b = write_tree_from_index(&repo.odb, &index, "")?;
120 let commit_b = commit_from_tree(&repo, tree_b, &[commit_a], "add picked file\n")?;
121 refs::write_ref(&repo.git_dir, "refs/heads/topic", &commit_b)?;
122
123 // Cherry-pick `topic` onto `main` (still at A).
124 let head = resolve_revision(&repo, "main")?;
125 let picked = resolve_revision(&repo, "topic")?;
126 let picked_obj = repo.odb.read(&picked)?;
127 let picked_data = parse_commit(&picked_obj.data)?;
128 let parent = picked_data.parents.first().copied().ok_or_else(|| {
129 grit_lib::error::Error::CorruptObject("picked commit has no parent".into())
130 })?;
131
132 let base_tree = tree_of_commit(&repo, parent)?;
133 let ours_tree = tree_of_commit(&repo, head)?;
134 let theirs_tree = picked_data.tree;
135
136 let merged = merge_trees_three_way(
137 &repo,
138 base_tree,
139 ours_tree,
140 theirs_tree,
141 MergeFavor::default(),
142 WhitespaceMergeOptions::default(),
143 None,
144 grit_lib::merge_trees::TreeMergeConflictPresentation {
145 label_ours: "HEAD",
146 label_theirs: grit_lib::merge_trees::TheirsConflictLabel::Fixed("picked"),
147 label_base: "parent of picked commit",
148 style: grit_lib::merge_file::ConflictStyle::Merge,
149 checkout_merge: false,
150 },
151 )?;
152
153 if !merged.conflict_content.is_empty() {
154 return Err(grit_lib::error::Error::Message(format!(
155 "merge produced {} conflict path(s); this example expects a clean pick",
156 merged.conflict_content.len()
157 )));
158 }
159
160 let new_tree = write_tree_from_index(&repo.odb, &merged.index, "")?;
161 let config = ConfigSet::load_repo_local_only(&repo.git_dir)?;
162 let msg = commit_trailers::finalize_cherry_pick_message(
163 &picked_data.message,
164 true,
165 false,
166 "Example",
167 "example@example.com",
168 &config,
169 &picked.to_hex(),
170 );
171 let new_commit = commit_from_tree(&repo, new_tree, &[head], &msg)?;
172 refs::write_ref(&repo.git_dir, "refs/heads/main", &new_commit)?;
173
174 println!("cherry-picked {} onto {}", picked, head);
175 println!("new main: {new_commit}");
176 let out = repo.odb.read(&new_commit)?;
177 println!("message:\n{}", parse_commit(&out.data)?.message);
178
179 Ok(())
180}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}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}Sourcepub fn write_local(&self, kind: ObjectKind, data: &[u8]) -> Result<ObjectId>
pub fn write_local(&self, kind: ObjectKind, data: &[u8]) -> Result<ObjectId>
Write an object as a loose file in this object directory only.
Unlike Self::write, this ignores info/alternates and
GIT_ALTERNATE_OBJECT_DIRECTORIES: if the object exists only in an
alternate store, it is still written here. That matches how Git’s
unpack-objects materializes every packed object into the receiving
repository even when the same OID is already reachable via alternates
(see t5519-push-alternates).
The well-known empty tree is still written when no loose file exists yet,
even though Self::exists_local treats it as virtually present.
§Errors
Same as Self::write.
Sourcepub fn write_loose_materialize(
&self,
kind: ObjectKind,
data: &[u8],
) -> Result<ObjectId>
pub fn write_loose_materialize( &self, kind: ObjectKind, data: &[u8], ) -> Result<ObjectId>
Write a loose object file when it is missing, even if Self::exists is true because
the object lives only in a pack.
Used when materializing a partial-clone layout: objects must be duplicated as loose files
before local packs are removed. Unlike Self::write_local, objects present only in a
promisor pack are still written because Self::exists_local treats those as absent.
Sourcepub fn write_raw(&self, store_bytes: &[u8]) -> Result<ObjectId>
pub fn write_raw(&self, store_bytes: &[u8]) -> Result<ObjectId>
Write an already-serialized object (header + data) to the loose store.
Useful when the caller has the full store bytes (e.g. from stdin with
--literally).
§Errors
Error::CorruptObject— the provided bytes don’t form a valid header.Error::Io/Error::Zlib— storage errors.
Sourcepub fn write_raw_local(&self, store_bytes: &[u8]) -> Result<ObjectId>
pub fn write_raw_local(&self, store_bytes: &[u8]) -> Result<ObjectId>
Like Self::write_raw but only consults this object directory, not alternates.
See Self::write_local.
§Errors
Same as Self::write_raw.
Sourcepub fn loose_object_plumbing_ok(&self, oid: &ObjectId) -> bool
pub fn loose_object_plumbing_ok(&self, oid: &ObjectId) -> bool
Returns true when a loose object exists at oid’s path and zlib-decompresses to a
structurally valid <type> <size>\0<payload> object (type may be non-standard).
Used for git cat-file -e, which succeeds for hand-crafted loose objects that
Self::read rejects due to Error::UnknownObjectType.