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 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 objects_dir(&self) -> &Path
pub fn objects_dir(&self) -> &Path
Return the path to the objects/ directory.
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 grit_lib::merge_trees::TreeMergeConflictPresentation {
144 label_ours: "HEAD",
145 label_theirs: grit_lib::merge_trees::TheirsConflictLabel::Fixed("picked"),
146 label_base: "parent of picked commit",
147 style: grit_lib::merge_file::ConflictStyle::Merge,
148 checkout_merge: false,
149 },
150 )?;
151
152 if !merged.conflict_content.is_empty() {
153 return Err(grit_lib::error::Error::Message(format!(
154 "merge produced {} conflict path(s); this example expects a clean pick",
155 merged.conflict_content.len()
156 )));
157 }
158
159 let new_tree = write_tree_from_index(&repo.odb, &merged.index, "")?;
160 let config = ConfigSet::load_repo_local_only(&repo.git_dir)?;
161 let msg = commit_trailers::finalize_cherry_pick_message(
162 &picked_data.message,
163 true,
164 false,
165 "Example",
166 "example@example.com",
167 &config,
168 &picked.to_hex(),
169 );
170 let new_commit = commit_from_tree(&repo, new_tree, &[head], &msg)?;
171 refs::write_ref(&repo.git_dir, "refs/heads/main", &new_commit)?;
172
173 println!("cherry-picked {} onto {}", picked, head);
174 println!("new main: {new_commit}");
175 let out = repo.odb.read(&new_commit)?;
176 println!("message:\n{}", parse_commit(&out.data)?.message);
177
178 Ok(())
179}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 and return the ObjectId.
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 grit_lib::merge_trees::TreeMergeConflictPresentation {
144 label_ours: "HEAD",
145 label_theirs: grit_lib::merge_trees::TheirsConflictLabel::Fixed("picked"),
146 label_base: "parent of picked commit",
147 style: grit_lib::merge_file::ConflictStyle::Merge,
148 checkout_merge: false,
149 },
150 )?;
151
152 if !merged.conflict_content.is_empty() {
153 return Err(grit_lib::error::Error::Message(format!(
154 "merge produced {} conflict path(s); this example expects a clean pick",
155 merged.conflict_content.len()
156 )));
157 }
158
159 let new_tree = write_tree_from_index(&repo.odb, &merged.index, "")?;
160 let config = ConfigSet::load_repo_local_only(&repo.git_dir)?;
161 let msg = commit_trailers::finalize_cherry_pick_message(
162 &picked_data.message,
163 true,
164 false,
165 "Example",
166 "example@example.com",
167 &config,
168 &picked.to_hex(),
169 );
170 let new_commit = commit_from_tree(&repo, new_tree, &[head], &msg)?;
171 refs::write_ref(&repo.git_dir, "refs/heads/main", &new_commit)?;
172
173 println!("cherry-picked {} onto {}", picked, head);
174 println!("new main: {new_commit}");
175 let out = repo.odb.read(&new_commit)?;
176 println!("message:\n{}", parse_commit(&out.data)?.message);
177
178 Ok(())
179}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).
§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.