Skip to content

Commit 179d6d4

Browse files
committed
WIP: Add support for GPG signing commits
1 parent 1ac8ee6 commit 179d6d4

File tree

4 files changed

+152
-3
lines changed

4 files changed

+152
-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

+63-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,74 @@ 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 = commit_bufffer
83+
.as_str()
84+
.expect("Buffer was not valid UTF-8");
85+
86+
// Prepare to sign using the designated key in the user's git config
87+
let mut gpg_ctx = Context::from_protocol(Protocol::OpenPgp)?;
88+
let key =
89+
gpg_ctx.get_key(config.get_string("user.signingkey")?)?;
90+
gpg_ctx.add_signer(&key)?;
91+
gpg_ctx.set_armor(true);
92+
93+
// Create GPG signature for commit content
94+
let mut signature_buffer = Vec::new();
95+
gpg_ctx
96+
.sign_detached(&*commit_bufffer, &mut signature_buffer)?;
97+
let gpg_signature =
98+
std::str::from_utf8(&signature_buffer).unwrap();
99+
100+
let commit_oid = repo.commit_signed(
101+
&commit_content,
102+
&gpg_signature,
103+
None,
104+
)?;
105+
106+
match repo.head() {
107+
// If HEAD reference is returned, simply update the target.
108+
Ok(mut head) => {
109+
head.set_target(commit_oid, msg)?;
110+
}
111+
// If there is an error getting HEAD, likely it is a new repo
112+
// and a reference to a default branch needs to be created.
113+
Err(_) => {
114+
// Default branch name behavior as of git 2.28.
115+
let default_branch_name = config
116+
.get_str("init.defaultBranch")
117+
.unwrap_or("master");
118+
119+
repo.reference(
120+
&format!("refs/heads/{}", default_branch_name),
121+
commit_oid,
122+
true,
123+
msg,
124+
)?;
125+
}
126+
}
127+
128+
commit_oid
129+
} else {
130+
repo.commit(
73131
Some("HEAD"),
74132
&signature,
75133
&signature,
76134
msg,
77135
&tree,
78136
parents.as_slice(),
79137
)?
80-
.into())
138+
};
139+
140+
Ok(commit_oid.into())
81141
}
82142

83143
/// Tag a commit.

0 commit comments

Comments
 (0)