pub struct ObjectId { /* private fields */ }Expand description
A Git object identifier: a SHA-1 (20-byte) or SHA-256 (32-byte) digest.
The digest is stored in a fixed 32-byte buffer with an explicit length;
bytes beyond len are always zero, so the derived Eq/Ord/Hash
remain correct. The hash algorithm is inferred from the length.
Implementations§
Source§impl ObjectId
impl ObjectId
Sourcepub const fn zero() -> Self
pub const fn zero() -> Self
The all-zero SHA-1 object id (Git’s “null” OID).
Used for index placeholders such as intent-to-add entries and for
special cases in plumbing output. For an algorithm-specific null OID
(e.g. 64 zeros in a SHA-256 repo) use ObjectId::null.
Sourcepub const fn null(algo: HashAlgo) -> Self
pub const fn null(algo: HashAlgo) -> Self
The all-zero (“null”) object id for a given hash algorithm.
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Self>
pub fn from_bytes(bytes: &[u8]) -> Result<Self>
Construct from a raw digest slice (20 bytes for SHA-1, 32 for SHA-256).
§Errors
Returns Error::InvalidObjectId when bytes is not a recognised
digest length.
Sourcepub fn as_bytes(&self) -> &[u8] ⓘ
pub fn as_bytes(&self) -> &[u8] ⓘ
Raw digest bytes (20 or 32 bytes depending on the hash algorithm).
Sourcepub fn to_hex(&self) -> String
pub fn to_hex(&self) -> String
Lowercase hex representation (40 or 64 characters).
Examples found in repository?
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}More examples
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}Sourcepub fn loose_prefix(&self) -> String
pub fn loose_prefix(&self) -> String
The two-character directory prefix used by the loose object store.
Returns the first two hex chars (e.g. "ab" for "ab3f…").
Sourcepub fn from_hex(s: &str) -> Result<Self>
pub fn from_hex(s: &str) -> Result<Self>
Parse an object ID from a hex string (40 chars for SHA-1, 64 for SHA-256).
§Errors
Returns Error::InvalidObjectId if the string is not a valid hex OID.
Sourcepub fn loose_suffix(&self) -> String
pub fn loose_suffix(&self) -> String
The suffix used as the filename inside the loose prefix dir (the digest minus its first byte: 38 hex chars for SHA-1, 62 for SHA-256).
Sourcepub fn is_full_hex(s: &str) -> bool
pub fn is_full_hex(s: &str) -> bool
Whether s is a full-length hex OID for a supported hash algorithm
(40 hex chars for SHA-1 or 64 for SHA-256), case-insensitive.
Sourcepub const fn is_hex_len(len: usize) -> bool
pub const fn is_hex_len(len: usize) -> bool
Whether len is a valid full hex-OID length (40 or 64).
Sourcepub const fn is_loose_suffix_len(len: usize) -> bool
pub const fn is_loose_suffix_len(len: usize) -> bool
Whether len is a valid loose-object filename suffix length, i.e. a full
hex OID minus its first byte (38 for SHA-1, 62 for SHA-256).