315 lines
9.9 KiB
Rust
315 lines
9.9 KiB
Rust
//! Commit creation and modification operations.
|
|
|
|
use git2::Signature;
|
|
|
|
use crate::commit::types::*;
|
|
use crate::{GitDomain, GitError, GitResult};
|
|
|
|
fn parents_to_git2<'a>(
|
|
repo: &'a git2::Repository,
|
|
parent_ids: &[CommitOid],
|
|
) -> GitResult<Vec<git2::Commit<'a>>> {
|
|
parent_ids
|
|
.iter()
|
|
.map(|oid| {
|
|
repo.find_commit(oid.to_oid()?)
|
|
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
impl GitDomain {
|
|
pub fn commit_default_signature(&self) -> GitResult<CommitSignature> {
|
|
let sig = self
|
|
.repo()
|
|
.signature()
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitSignature::from_git2(sig))
|
|
}
|
|
|
|
pub fn commit_signature_now(&self, name: &str, email: &str) -> GitResult<CommitSignature> {
|
|
let sig = Signature::now(name, email).map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitSignature::from_git2(sig))
|
|
}
|
|
|
|
pub fn commit_signature_at(
|
|
&self,
|
|
name: &str,
|
|
email: &str,
|
|
time_secs: i64,
|
|
offset_minutes: i32,
|
|
) -> GitResult<CommitSignature> {
|
|
let time = git2::Time::new(time_secs, offset_minutes);
|
|
let sig =
|
|
Signature::new(name, email, &time).map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitSignature::from_git2(sig))
|
|
}
|
|
|
|
pub fn commit_signature_to_git2(&self, sig: &CommitSignature) -> GitResult<Signature<'static>> {
|
|
let time = git2::Time::new(sig.time_secs, sig.offset_minutes);
|
|
Signature::new(&sig.name, &sig.email, &time).map_err(|e| GitError::Internal(e.to_string()))
|
|
}
|
|
|
|
pub fn commit_create(
|
|
&self,
|
|
update_ref: Option<&str>,
|
|
author: &CommitSignature,
|
|
committer: &CommitSignature,
|
|
message: &str,
|
|
tree_id: &CommitOid,
|
|
parent_ids: &[CommitOid],
|
|
) -> GitResult<CommitOid> {
|
|
let author = self.commit_signature_to_git2(author)?;
|
|
let committer = self.commit_signature_to_git2(committer)?;
|
|
let tree = self
|
|
.repo()
|
|
.find_tree(tree_id.to_oid()?)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let parents = parents_to_git2(&*self.repo(), parent_ids)?;
|
|
let parent_refs: Vec<&git2::Commit<'_>> =
|
|
parents.iter().map(|p| p as &git2::Commit).collect();
|
|
let oid = self
|
|
.repo()
|
|
.commit(
|
|
update_ref,
|
|
&author,
|
|
&committer,
|
|
message,
|
|
&tree,
|
|
&parent_refs,
|
|
)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitOid::from_git2(oid))
|
|
}
|
|
|
|
pub fn commit_create_from_index(
|
|
&self,
|
|
update_ref: Option<&str>,
|
|
author: &CommitSignature,
|
|
committer: &CommitSignature,
|
|
message: &str,
|
|
parent_ids: &[CommitOid],
|
|
) -> GitResult<CommitOid> {
|
|
let author = self.commit_signature_to_git2(author)?;
|
|
let committer = self.commit_signature_to_git2(committer)?;
|
|
let mut index = self
|
|
.repo()
|
|
.index()
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let tree_oid = index
|
|
.write_tree()
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let tree = self
|
|
.repo()
|
|
.find_tree(tree_oid)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let parents = parents_to_git2(&*self.repo(), parent_ids)?;
|
|
let parent_refs: Vec<&git2::Commit<'_>> =
|
|
parents.iter().map(|p| p as &git2::Commit).collect();
|
|
let oid = self
|
|
.repo()
|
|
.commit(
|
|
update_ref,
|
|
&author,
|
|
&committer,
|
|
message,
|
|
&tree,
|
|
&parent_refs,
|
|
)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitOid::from_git2(oid))
|
|
}
|
|
|
|
pub fn commit_sign(
|
|
&self,
|
|
author: &CommitSignature,
|
|
committer: &CommitSignature,
|
|
message: &str,
|
|
tree_id: &CommitOid,
|
|
parent_ids: &[CommitOid],
|
|
gpg_signature: &str,
|
|
signature_field: Option<&str>,
|
|
) -> GitResult<CommitOid> {
|
|
let author = self.commit_signature_to_git2(author)?;
|
|
let committer = self.commit_signature_to_git2(committer)?;
|
|
let tree = self
|
|
.repo()
|
|
.find_tree(tree_id.to_oid()?)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let parents = parents_to_git2(&*self.repo(), parent_ids)?;
|
|
let parent_refs: Vec<&git2::Commit<'_>> =
|
|
parents.iter().map(|p| p as &git2::Commit).collect();
|
|
let buf = self
|
|
.repo()
|
|
.commit_create_buffer(&author, &committer, message, &tree, &parent_refs)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let buf_str = std::str::from_utf8(&*buf).map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let oid = self
|
|
.repo()
|
|
.commit_signed(buf_str, gpg_signature, signature_field)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitOid::from_git2(oid))
|
|
}
|
|
|
|
pub fn commit_extract_signature(
|
|
&self,
|
|
commit_oid: &CommitOid,
|
|
signature_field: Option<&str>,
|
|
) -> GitResult<Option<(String, String)>> {
|
|
match self
|
|
.repo()
|
|
.extract_signature(&commit_oid.to_oid()?, signature_field)
|
|
{
|
|
Ok((sig_buf, content_buf)) => Ok(Some((
|
|
String::from_utf8_lossy(&*sig_buf).to_string(),
|
|
String::from_utf8_lossy(&*content_buf).to_string(),
|
|
))),
|
|
Err(e) => {
|
|
if e.code() == git2::ErrorCode::NotFound {
|
|
Ok(None)
|
|
} else {
|
|
Err(GitError::Internal(e.to_string()))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn commit_empty(
|
|
&self,
|
|
update_ref: Option<&str>,
|
|
author: &CommitSignature,
|
|
committer: &CommitSignature,
|
|
message: &str,
|
|
parent_ids: &[CommitOid],
|
|
) -> GitResult<CommitOid> {
|
|
let author = self.commit_signature_to_git2(author)?;
|
|
let committer = self.commit_signature_to_git2(committer)?;
|
|
let tree = if let Some(first) = parent_ids.first() {
|
|
let commit = self
|
|
.repo()
|
|
.find_commit(first.to_oid()?)
|
|
.map_err(|_e| GitError::ObjectNotFound(first.to_string()))?;
|
|
commit
|
|
.tree()
|
|
.map_err(|e| GitError::Internal(e.to_string()))?
|
|
} else {
|
|
let mut index = self
|
|
.repo()
|
|
.index()
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let tree_oid = index
|
|
.write_tree()
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
self.repo()
|
|
.find_tree(tree_oid)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?
|
|
};
|
|
let parents = parents_to_git2(&*self.repo(), parent_ids)?;
|
|
let parent_refs: Vec<&git2::Commit<'_>> =
|
|
parents.iter().map(|p| p as &git2::Commit).collect();
|
|
let oid = self
|
|
.repo()
|
|
.commit(
|
|
update_ref,
|
|
&author,
|
|
&committer,
|
|
message,
|
|
&tree,
|
|
&parent_refs,
|
|
)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitOid::from_git2(oid))
|
|
}
|
|
|
|
pub fn commit_amend(
|
|
&self,
|
|
commit_oid: &CommitOid,
|
|
update_ref: Option<&str>,
|
|
author: Option<&CommitSignature>,
|
|
committer: Option<&CommitSignature>,
|
|
message_encoding: Option<&str>,
|
|
message: Option<&str>,
|
|
tree_id: Option<&CommitOid>,
|
|
) -> GitResult<CommitOid> {
|
|
let commit = self
|
|
.repo()
|
|
.find_commit(commit_oid.to_oid()?)
|
|
.map_err(|_e| GitError::ObjectNotFound(commit_oid.to_string()))?;
|
|
let author = author
|
|
.map(|a| self.commit_signature_to_git2(a))
|
|
.transpose()?;
|
|
let committer = committer
|
|
.map(|c| self.commit_signature_to_git2(c))
|
|
.transpose()?;
|
|
let tree = tree_id
|
|
.map(|t| {
|
|
self.repo
|
|
.find_tree(t.to_oid()?)
|
|
.map_err(|e| GitError::Internal(e.to_string()))
|
|
})
|
|
.transpose()?;
|
|
let oid = commit
|
|
.amend(
|
|
update_ref,
|
|
author.as_ref(),
|
|
committer.as_ref(),
|
|
message_encoding,
|
|
message,
|
|
tree.as_ref(),
|
|
)
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
Ok(CommitOid::from_git2(oid))
|
|
}
|
|
|
|
pub fn commit_amend_author(
|
|
&self,
|
|
commit_oid: &CommitOid,
|
|
new_author: &CommitSignature,
|
|
update_ref: Option<&str>,
|
|
) -> GitResult<CommitOid> {
|
|
self.commit_amend(
|
|
commit_oid,
|
|
update_ref,
|
|
Some(new_author),
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
)
|
|
}
|
|
|
|
pub fn commit_amend_message(
|
|
&self,
|
|
commit_oid: &CommitOid,
|
|
new_message: &str,
|
|
update_ref: Option<&str>,
|
|
) -> GitResult<CommitOid> {
|
|
self.commit_amend(
|
|
commit_oid,
|
|
update_ref,
|
|
None,
|
|
None,
|
|
None,
|
|
Some(new_message),
|
|
None,
|
|
)
|
|
}
|
|
|
|
pub fn commit_amend_tree(
|
|
&self,
|
|
commit_oid: &CommitOid,
|
|
new_tree_id: &CommitOid,
|
|
update_ref: Option<&str>,
|
|
) -> GitResult<CommitOid> {
|
|
self.commit_amend(
|
|
commit_oid,
|
|
update_ref,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
Some(new_tree_id),
|
|
)
|
|
}
|
|
}
|