···33use std::path::PathBuf;
4455use anyhow::{Context, Result};
66+use either::*;
67use git2::{ObjectType, Oid, Repository};
7889use crate::utils;
···2425 .context("Failed to look-up git object id")?;
25262627 let object_ptr = object.id();
2727- let object_mode = match object
2828+ let object_mode_or_commit = match object
2829 .kind()
2930 .context("Failed to determine object kind to sign")?
3031 {
3131- ObjectType::Blob => 0o100644,
3232- ObjectType::Tree => 0o040000,
3333- ty @ (ObjectType::Any | ObjectType::Commit | ObjectType::Tag) => {
3434- anyhow::bail!("Unsupported object type {ty}");
3232+ ObjectType::Blob => Left(0o100644),
3333+ ObjectType::Tree => Left(0o040000),
3434+ ObjectType::Commit => Right(object.as_commit().expect("The object is a commit")),
3535+ ty @ (ObjectType::Any | ObjectType::Tag) => {
3636+ anyhow::bail!("Unsupported or recursive object type {ty}");
3537 }
3638 };
37394040+ let commit_author = repo
4141+ .signature()
4242+ .context("Failed to retrieve commit author")?;
4343+3844 let signature = secret_key.sign(object_ptr.as_bytes())?;
3945 let signature_blob = repo
4046 .blob(&signature)
···5965 .insert("algorithm", algo_blob, 0o100644)
6066 .context("Failed to write algorithm to the tree")?;
6167 tree_builder
6262- .insert("object", object_ptr, object_mode)
6363- .context("Failed to write object to the tree")?;
6464- tree_builder
6568 .insert("signature", signature_blob, 0o100644)
6669 .context("Failed to write signature to the tree")?;
67707171+ let parents = object_mode_or_commit.either(
7272+ |object_mode| {
7373+ tree_builder
7474+ .insert("object", object_ptr, object_mode)
7575+ .context("Failed to write object to the tree")?;
7676+ anyhow::Ok(vec![])
7777+ },
7878+ |commit| anyhow::Ok(vec![commit]),
7979+ )?;
8080+6881 let tree_oid = tree_builder
6982 .write()
7083 .context("Failed to write tree to the object store")?;
8484+ let tree = repo
8585+ .find_tree(tree_oid)
8686+ .context("Failed to look-up newly created git tree signature")?;
71877272- Ok(tree_oid)
8888+ let commit_oid = repo
8989+ .commit(
9090+ None,
9191+ &commit_author,
9292+ &commit_author,
9393+ "git-signify signature",
9494+ &tree,
9595+ &parents,
9696+ )
9797+ .context("Failed to create git signature commit")?;
9898+9999+ Ok(commit_oid)
73100}
+94-33
src/utils.rs
···166166 /// Like [`TreeSignature::load`], but uses a concrete revision pointing
167167 /// to the tree signature.
168168 pub fn load_oid(repo: &'repo Repository, oid: Oid) -> Result<Self> {
169169- let tree = repo
170170- .find_tree(oid)
171171- .context("No tree object found for the given revision")?;
172172-173173- let (version, algorithm) = tree.get_name("version").map_or(
174174- Ok((TreeSignatureVersion::V0, TreeSignatureAlgo::Signify)),
175175- |version_tree_entry| {
176176- let version_obj = version_tree_entry
177177- .to_object(repo)
178178- .context("The tree signature version could not be retrieved")?;
179179- let version_blob = match version_obj.into_blob() {
180180- Ok(blob) => blob,
181181- Err(_) => {
182182- return Err(anyhow!("The tree signature version object is not a blob"))
183183- }
184184- };
185185- let version = TreeSignatureVersion::from_blob(version_blob)?;
169169+ let object = repo
170170+ .find_object(oid, None)
171171+ .context("No git object found for the given revision")?;
186172187187- let algorithm_obj = tree
188188- .get_name("algorithm")
189189- .context("Failed to look-up tree signature algorithm")?
190190- .to_object(repo)
191191- .context("The tree signature algorithm could not be retrieved")?;
192192- let algorithm_blob = match algorithm_obj.into_blob() {
193193- Ok(blob) => blob,
194194- Err(_) => {
195195- return Err(anyhow!("The tree signature algorithm object is not a blob"))
196196- }
197197- };
198198- let algorithm = TreeSignatureAlgo::from_blob(algorithm_blob)?;
173173+ match object.kind().context(
174174+ "Failed to determine kind of git object, while determining version of the signature",
175175+ )? {
176176+ ObjectType::Tree => Self::load_oid_v0(repo, object),
177177+ ObjectType::Commit => Self::load_oid_v1(repo, object),
178178+ _ => anyhow::bail!(
179179+ "Invalid object kind provided, while loading tree signature with oid={oid}"
180180+ ),
181181+ }
182182+ }
199183200200- anyhow::Ok((version, algorithm))
201201- },
202202- )?;
184184+ /// Load a v0 [`TreeSignature`].
185185+ pub fn load_oid_v0(repo: &'repo Repository, object: Object<'repo>) -> Result<Self> {
186186+ let tree = object.as_tree().with_context(|| {
187187+ format!(
188188+ "No tree signature found for object with oid={}",
189189+ object.id()
190190+ )
191191+ })?;
203192204193 let object_pointer = tree
205194 .get_name("object")
206195 .context("Failed to look-up signed object in the tree")?
207196 .to_object(repo)
208197 .context("The signed object could not be retrieved")?;
198198+ let signature = {
199199+ let signature = tree
200200+ .get_name("signature")
201201+ .context("Failed to look-up signature in the tree")?
202202+ .to_object(repo)
203203+ .context("The signature object could not be retrieved")?;
204204+ signature
205205+ .into_blob()
206206+ .map_err(|_| anyhow!("The signature object in oid={} is not a blob", object.id()))?
207207+ };
208208+209209+ Ok(Self {
210210+ signature,
211211+ object_pointer,
212212+ version: TreeSignatureVersion::V0,
213213+ algorithm: TreeSignatureAlgo::Signify,
214214+ })
215215+ }
216216+217217+ /// Load a v1 [`TreeSignature`].
218218+ pub fn load_oid_v1(repo: &'repo Repository, object: Object<'repo>) -> Result<Self> {
219219+ let commit = object
220220+ .as_commit()
221221+ .context("Failed to retrieve v1 git commit with signature")?;
222222+ let tree = commit
223223+ .tree()
224224+ .context("Failed to retrieve v1 git tree with signature")?;
225225+226226+ let version = {
227227+ let version_obj = tree
228228+ .get_name("version")
229229+ .context("Failed to look-up tree signature version")?
230230+ .to_object(repo)
231231+ .context("The tree signature version could not be retrieved")?;
232232+ let version_blob = match version_obj.into_blob() {
233233+ Ok(blob) => blob,
234234+ Err(_) => return Err(anyhow!("The tree signature version object is not a blob")),
235235+ };
236236+ TreeSignatureVersion::from_blob(version_blob)?
237237+ };
238238+ let algorithm = {
239239+ let algorithm_obj = tree
240240+ .get_name("algorithm")
241241+ .context("Failed to look-up tree signature algorithm")?
242242+ .to_object(repo)
243243+ .context("The tree signature algorithm could not be retrieved")?;
244244+ let algorithm_blob = match algorithm_obj.into_blob() {
245245+ Ok(blob) => blob,
246246+ Err(_) => return Err(anyhow!("The tree signature algorithm object is not a blob")),
247247+ };
248248+ TreeSignatureAlgo::from_blob(algorithm_blob)?
249249+ };
250250+251251+ let object_pointer = tree.get_name("object").map_or_else(
252252+ || {
253253+ Ok(commit
254254+ .parent(0)
255255+ .context(
256256+ "No signed `object` in the tree signature nor a parent commit \
257257+ to be signed could be found",
258258+ )?
259259+ .into_object())
260260+ },
261261+ |entry| {
262262+ entry.to_object(repo).with_context(|| {
263263+ format!(
264264+ "The signed object with oid={} could not be cast to a git object",
265265+ entry.id()
266266+ )
267267+ })
268268+ },
269269+ )?;
209270210271 let signature = {
211272 let signature = tree
···215276 .context("The signature object could not be retrieved")?;
216277 signature
217278 .into_blob()
218218- .map_err(|_| anyhow!("The signature object in {oid} is not a blob"))?
279279+ .map_err(|_| anyhow!("The signature object in oid={} is not a blob", object.id()))?
219280 };
220281221282 Ok(Self {