Skip to content

Commit be8d3bd

Browse files
committed
add ssh sign feature
1 parent b092208 commit be8d3bd

23 files changed

+903
-62
lines changed

Cargo.lock

Lines changed: 741 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ tempfile = "3.4"
5959
maintenance = { status = "actively-developed" }
6060

6161
[features]
62-
default = ["ghemoji", "regex-fancy", "trace-libgit", "vendor-openssl"]
62+
default = ["asyncgit/common-lib", "ghemoji", "regex-fancy", "trace-libgit", "vendor-openssl"]
6363
ghemoji = ["gh-emoji"]
6464
# regex-* features are mutually exclusive.
6565
regex-fancy = ["syntect/regex-fancy"]

asyncgit/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ rayon = "1.8"
2727
rayon-core = "1.12"
2828
scopetime = { path = "../scopetime", version = "0.1" }
2929
serde = { version = "1.0", features = ["derive"] }
30+
ssh-key = { version = "0.6.4", features = ["crypto", "encryption"] }
3031
thiserror = "1.0"
3132
unicode-truncate = "0.2.0"
3233
url = "2.5"
@@ -39,6 +40,7 @@ serial_test = "1.0"
3940
tempfile = "3.4"
4041

4142
[features]
42-
default = ["trace-libgit"]
43+
common-lib = []
44+
default = ["common-lib", "trace-libgit"]
4345
trace-libgit = []
4446
vendor-openssl = ["openssl-sys"]

asyncgit/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ pub enum Error {
8484
///
8585
#[error("git hook error: {0}")]
8686
Hooks(#[from] git2_hooks::HooksError),
87+
88+
///
89+
#[error("ssh key error: {0}")]
90+
SshKeyError(#[from] ssh_key::Error),
8791
}
8892

8993
///

asyncgit/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub use crate::{
7575
treefiles::AsyncTreeFilesJob,
7676
};
7777
pub use git2::message_prettify;
78+
#[cfg(feature = "common-lib")]
79+
pub use ssh_key;
7880
use std::{
7981
collections::hash_map::DefaultHasher,
8082
hash::{Hash, Hasher},

asyncgit/src/sync/blame.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ mod tests {
175175
File::create(root.join(file_path))?.write_all(b"line 1\n")?;
176176

177177
stage_add_file(repo_path, file_path)?;
178-
commit(repo_path, "first commit")?;
178+
commit(repo_path, "first commit", None)?;
179179

180180
let blame = blame_file(repo_path, "foo", None)?;
181181

@@ -199,7 +199,7 @@ mod tests {
199199
file.write(b"line 2\n")?;
200200

201201
stage_add_file(repo_path, file_path)?;
202-
commit(repo_path, "second commit")?;
202+
commit(repo_path, "second commit", None)?;
203203

204204
let blame = blame_file(repo_path, "foo", None)?;
205205

@@ -233,7 +233,7 @@ mod tests {
233233
assert_eq!(blame.lines.len(), 2);
234234

235235
stage_add_file(repo_path, file_path)?;
236-
commit(repo_path, "third commit")?;
236+
commit(repo_path, "third commit", None)?;
237237

238238
let blame = blame_file(repo_path, "foo", None)?;
239239

@@ -258,7 +258,7 @@ mod tests {
258258
.unwrap();
259259

260260
stage_add_file(repo_path, file_path).unwrap();
261-
commit(repo_path, "first commit").unwrap();
261+
commit(repo_path, "first commit", None).unwrap();
262262

263263
assert!(blame_file(repo_path, "bar\\foo", None).is_ok());
264264
}

asyncgit/src/sync/commit.rs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use super::{CommitId, RepoPath};
2+
use crate::sync::reword::get_current_branch;
3+
use crate::sync::utils::bytes2string;
24
use crate::{
35
error::Result,
46
sync::{repository::repo, utils::get_head_repo},
57
};
68
use git2::{ErrorCode, ObjectType, Repository, Signature};
79
use scopetime::scope_time;
10+
use ssh_key::{HashAlg, LineEnding, PrivateKey};
811

912
///
1013
pub fn amend(
@@ -61,7 +64,11 @@ pub(crate) fn signature_allow_undefined_name(
6164
}
6265

6366
/// this does not run any git hooks, git-hooks have to be executed manually, checkout `hooks_commit_msg` for example
64-
pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
67+
pub fn commit(
68+
repo_path: &RepoPath,
69+
msg: &str,
70+
sk: Option<&PrivateKey>,
71+
) -> Result<CommitId> {
6572
scope_time!("commit");
6673

6774
let repo = repo(repo_path)?;
@@ -78,17 +85,39 @@ pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
7885
};
7986

8087
let parents = parents.iter().collect::<Vec<_>>();
81-
82-
Ok(repo
83-
.commit(
84-
Some("HEAD"),
88+
if let Some(sk) = sk {
89+
let buffer = repo.commit_create_buffer(
8590
&signature,
8691
&signature,
8792
msg,
8893
&tree,
8994
parents.as_slice(),
90-
)?
91-
.into())
95+
)?;
96+
let content = String::from_utf8(buffer.to_vec())?;
97+
let sig = sk
98+
.sign("git", HashAlg::Sha256, &buffer)?
99+
.to_pem(LineEnding::LF)?;
100+
let commit_id = repo.commit_signed(&content, &sig, None)?;
101+
if let Ok(Some(branch)) = get_current_branch(&repo) {
102+
let commit = repo.find_commit(commit_id)?;
103+
let cur_branch_name = bytes2string(branch.name_bytes()?)?;
104+
repo.set_head_detached(commit_id)?;
105+
repo.checkout_head(None)?;
106+
repo.branch(&cur_branch_name, &commit, true)?;
107+
}
108+
Ok(commit_id.into())
109+
} else {
110+
Ok(repo
111+
.commit(
112+
Some("HEAD"),
113+
&signature,
114+
&signature,
115+
msg,
116+
&tree,
117+
parents.as_slice(),
118+
)?
119+
.into())
120+
}
92121
}
93122

94123
/// Tag a commit.
@@ -162,7 +191,7 @@ mod tests {
162191

163192
assert_eq!(get_statuses(repo_path), (0, 1));
164193

165-
commit(repo_path, "commit msg").unwrap();
194+
commit(repo_path, "commit msg", None).unwrap();
166195

167196
assert_eq!(get_statuses(repo_path), (0, 0));
168197
}
@@ -188,7 +217,7 @@ mod tests {
188217

189218
assert_eq!(get_statuses(repo_path), (0, 1));
190219

191-
commit(repo_path, "commit msg").unwrap();
220+
commit(repo_path, "commit msg", None).unwrap();
192221

193222
assert_eq!(get_statuses(repo_path), (0, 0));
194223
}
@@ -205,7 +234,7 @@ mod tests {
205234
File::create(root.join(file_path1))?.write_all(b"test1")?;
206235

207236
stage_add_file(repo_path, file_path1)?;
208-
let id = commit(repo_path, "commit msg")?;
237+
let id = commit(repo_path, "commit msg", None)?;
209238

210239
assert_eq!(count_commits(&repo, 10), 1);
211240

@@ -244,7 +273,7 @@ mod tests {
244273

245274
stage_add_file(repo_path, file_path)?;
246275

247-
let new_id = commit(repo_path, "commit msg")?;
276+
let new_id = commit(repo_path, "commit msg", None)?;
248277

249278
tag_commit(repo_path, &new_id, "tag", None)?;
250279

@@ -286,7 +315,7 @@ mod tests {
286315

287316
stage_add_file(repo_path, file_path)?;
288317

289-
let new_id = commit(repo_path, "commit msg")?;
318+
let new_id = commit(repo_path, "commit msg", None)?;
290319

291320
tag_commit(repo_path, &new_id, "tag", Some("tag-message"))?;
292321

@@ -322,13 +351,13 @@ mod tests {
322351

323352
repo.config()?.remove("user.email")?;
324353

325-
let error = commit(repo_path, "commit msg");
354+
let error = commit(repo_path, "commit msg", None);
326355

327356
assert!(matches!(error, Err(_)));
328357

329358
repo.config()?.set_str("user.email", "email")?;
330359

331-
let success = commit(repo_path, "commit msg");
360+
let success = commit(repo_path, "commit msg", None);
332361

333362
assert!(matches!(success, Ok(_)));
334363
assert_eq!(count_commits(&repo, 10), 1);
@@ -358,7 +387,7 @@ mod tests {
358387

359388
repo.config()?.remove("user.name")?;
360389

361-
let mut success = commit(repo_path, "commit msg");
390+
let mut success = commit(repo_path, "commit msg", None);
362391

363392
assert!(matches!(success, Ok(_)));
364393
assert_eq!(count_commits(&repo, 10), 1);
@@ -371,7 +400,7 @@ mod tests {
371400

372401
repo.config()?.set_str("user.name", "name")?;
373402

374-
success = commit(repo_path, "commit msg");
403+
success = commit(repo_path, "commit msg", None);
375404

376405
assert!(matches!(success, Ok(_)));
377406
assert_eq!(count_commits(&repo, 10), 2);

asyncgit/src/sync/commit_details.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ mod tests {
144144
stage_add_file(repo_path, file_path).unwrap();
145145

146146
let msg = invalidstring::invalid_utf8("test msg");
147-
let id = commit(repo_path, msg.as_str()).unwrap();
147+
let id = commit(repo_path, msg.as_str(), None).unwrap();
148148

149149
let res = get_commit_details(repo_path, id).unwrap();
150150

asyncgit/src/sync/commit_files.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ mod tests {
180180

181181
stage_add_file(repo_path, file_path)?;
182182

183-
let id = commit(repo_path, "commit msg")?;
183+
let id = commit(repo_path, "commit msg", None)?;
184184

185185
let diff = get_commit_files(repo_path, id, None)?;
186186

@@ -222,7 +222,7 @@ mod tests {
222222

223223
File::create(root.join(file_path1))?.write_all(b"test")?;
224224
stage_add_file(repo_path, file_path1)?;
225-
commit(repo_path, "c1")?;
225+
commit(repo_path, "c1", None)?;
226226

227227
File::create(root.join(file_path1))?
228228
.write_all(b"modified")?;

asyncgit/src/sync/commit_revert.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
sync::{repository::repo, utils::read_file},
55
};
66
use scopetime::scope_time;
7+
use ssh_key::PrivateKey;
78

89
const GIT_REVERT_HEAD_FILE: &str = "REVERT_HEAD";
910

@@ -40,10 +41,11 @@ pub fn revert_head(repo_path: &RepoPath) -> Result<CommitId> {
4041
pub fn commit_revert(
4142
repo_path: &RepoPath,
4243
msg: &str,
44+
sk: Option<&PrivateKey>,
4345
) -> Result<CommitId> {
4446
scope_time!("commit_revert");
4547

46-
let id = crate::sync::commit(repo_path, msg)?;
48+
let id = crate::sync::commit(repo_path, msg, sk)?;
4749

4850
repo(repo_path)?.cleanup_state()?;
4951

0 commit comments

Comments
 (0)