Skip to content

Commit 4fd93df

Browse files
committed
WIP: Add support for GPG signing commits
1 parent 1ac8ee6 commit 4fd93df

File tree

4 files changed

+142
-3
lines changed

4 files changed

+142
-3
lines changed

Diff for: Cargo.lock

+84
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: asyncgit/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ rayon-core = "1.7"
1818
crossbeam-channel = "0.4"
1919
log = "0.4"
2020
thiserror = "1.0"
21+
gpg-error = "0.5.1"
22+
gpgme = "0.9.2"
2123

2224
[dev-dependencies]
2325
tempfile = "3.1"

Diff for: asyncgit/src/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pub enum Error {
1313

1414
#[error("git error:{0}")]
1515
Git(#[from] git2::Error),
16+
17+
#[error("gpg error:#{0}")]
18+
Gpg(#[from] gpg_error::Error),
1619
}
1720

1821
pub type Result<T> = std::result::Result<T, Error>;

Diff for: asyncgit/src/sync/commit.rs

+53-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::{get_head, utils::repo, CommitId};
22
use crate::error::Result;
33
use git2::{ErrorCode, ObjectType, Repository, Signature};
4+
use gpgme::{Context, Protocol};
45
use scopetime::scope_time;
56

67
///
@@ -54,6 +55,7 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
5455
scope_time!("commit");
5556

5657
let repo = repo(repo_path)?;
58+
let config = repo.config()?;
5759

5860
let signature = signature_allow_undefined_name(&repo)?;
5961
let mut index = repo.index()?;
@@ -68,16 +70,64 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
6870

6971
let parents = parents.iter().collect::<Vec<_>>();
7072

71-
Ok(repo
72-
.commit(
73+
let commit_oid = if config.get_bool("commit.gpgsign")? {
74+
// Generate commit content
75+
let commit_bufffer = repo.commit_create_buffer(
76+
&signature,
77+
&signature,
78+
msg,
79+
&tree,
80+
parents.as_slice(),
81+
)?;
82+
let commit_content =
83+
commit_bufffer.as_str().expect("Buffer was not valid UTF-8");
84+
85+
// Prepare to sign using the designated key in the user's git config
86+
let mut gpg_ctx = Context::from_protocol(Protocol::OpenPgp)?;
87+
let key = gpg_ctx.get_key(config.get_string("user.signingkey")?)?;
88+
gpg_ctx.add_signer(&key)?;
89+
gpg_ctx.set_armor(true);
90+
91+
// Create GPG signature for commit content
92+
let mut signature_buffer = Vec::new();
93+
gpg_ctx.sign_detached(&*commit_bufffer, &mut signature_buffer)?;
94+
let gpg_signature = std::str::from_utf8(&signature_buffer).unwrap();
95+
96+
let commit_oid =
97+
repo.commit_signed(&commit_content, &gpg_signature, None)?;
98+
99+
match repo.head() {
100+
// If HEAD reference is returned, simply update the target.
101+
Ok(mut head) => { head.set_target(commit_oid, msg)?; }
102+
// If there is an error getting HEAD, likely it is a new repo
103+
// and a reference to a default branch needs to be created.
104+
Err(_) => {
105+
// Default branch name behavior as of git 2.28.
106+
let default_branch_name =
107+
config.get_str("init.defaultBranch").unwrap_or("master");
108+
109+
repo.reference(
110+
&format!("refs/heads/{}", default_branch_name),
111+
commit_oid,
112+
true,
113+
msg
114+
)?;
115+
}
116+
}
117+
118+
commit_oid
119+
} else {
120+
repo.commit(
73121
Some("HEAD"),
74122
&signature,
75123
&signature,
76124
msg,
77125
&tree,
78126
parents.as_slice(),
79127
)?
80-
.into())
128+
};
129+
130+
Ok(commit_oid.into())
81131
}
82132

83133
/// Tag a commit.

0 commit comments

Comments
 (0)