Skip to content

Commit feba5bd

Browse files
committed
feat(rebase): support GPG-signing for all rebase-based commands
1 parent a7a5e9b commit feba5bd

File tree

13 files changed

+82
-19
lines changed

13 files changed

+82
-19
lines changed

git-branchless-lib/src/core/rewrite/execute.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::core::formatting::Pluralize;
1515
use crate::core::repo_ext::RepoExt;
1616
use crate::git::{
1717
BranchType, CategorizedReferenceName, GitRunInfo, MaybeZeroOid, NonZeroOid, ReferenceName,
18-
Repo, ResolvedReferenceInfo,
18+
Repo, ResolvedReferenceInfo, SignOption,
1919
};
2020
use crate::util::{ExitCode, EyreExitOr};
2121

@@ -435,7 +435,8 @@ mod in_memory {
435435
use crate::core::rewrite::move_branches;
436436
use crate::core::rewrite::plan::{OidOrLabel, RebaseCommand, RebasePlan};
437437
use crate::git::{
438-
CherryPickFastError, CherryPickFastOptions, GitRunInfo, MaybeZeroOid, NonZeroOid, Repo,
438+
self, CherryPickFastError, CherryPickFastOptions, GitRunInfo, MaybeZeroOid, NonZeroOid,
439+
Repo,
439440
};
440441
use crate::util::EyreExitOr;
441442

@@ -498,6 +499,7 @@ mod in_memory {
498499
force_on_disk: _,
499500
resolve_merge_conflicts: _, // May be needed once we can resolve merge conflicts in memory.
500501
check_out_commit_options: _, // Caller is responsible for checking out to new HEAD.
502+
sign_option,
501503
} = options;
502504

503505
let mut current_oid = rebase_plan.first_dest_oid;
@@ -535,6 +537,8 @@ mod in_memory {
535537
.count();
536538
let (effects, progress) = effects.start_operation(OperationType::RebaseCommits);
537539

540+
let signer = git::get_signer(&repo, sign_option)?;
541+
538542
for command in rebase_plan.commands.iter() {
539543
match command {
540544
RebaseCommand::CreateLabel { label_name } => {
@@ -633,7 +637,7 @@ mod in_memory {
633637
commit_message,
634638
&commit_tree,
635639
vec![&current_commit],
636-
None,
640+
signer.as_deref(),
637641
)
638642
.wrap_err("Applying rebased commit")?;
639643

@@ -753,7 +757,7 @@ mod in_memory {
753757
replacement_commit_message,
754758
&replacement_tree,
755759
parents.iter().collect(),
756-
None,
760+
signer.as_deref(),
757761
)
758762
.wrap_err("Applying rebased commit")?;
759763

@@ -864,6 +868,7 @@ mod in_memory {
864868
force_on_disk: _,
865869
resolve_merge_conflicts: _,
866870
check_out_commit_options,
871+
sign_option: _,
867872
} = options;
868873

869874
// Note that if an OID has been mapped to multiple other OIDs, then the last
@@ -959,6 +964,7 @@ mod on_disk {
959964
force_on_disk: _,
960965
resolve_merge_conflicts: _,
961966
check_out_commit_options: _, // Checkout happens after rebase has concluded.
967+
sign_option,
962968
} = options;
963969

964970
let (effects, _progress) = effects.start_operation(OperationType::InitializeRebase);
@@ -1073,6 +1079,16 @@ mod on_disk {
10731079
)
10741080
})?;
10751081

1082+
let gpg_sign_opt_file = rebase_state_dir.join("gpg_sign_opt");
1083+
if let Some(sign_flag) = sign_option.as_rebase_flag(repo)? {
1084+
std::fs::write(&gpg_sign_opt_file, sign_flag).wrap_err_with(|| {
1085+
format!(
1086+
"Writing `gpg_sign_opt` to: {:?}",
1087+
gpg_sign_opt_file.as_path()
1088+
)
1089+
})?;
1090+
}
1091+
10761092
let end_file_path = rebase_state_dir.join("end");
10771093
std::fs::write(
10781094
end_file_path.as_path(),
@@ -1132,6 +1148,7 @@ mod on_disk {
11321148
force_on_disk: _,
11331149
resolve_merge_conflicts: _,
11341150
check_out_commit_options: _, // Checkout happens after rebase has concluded.
1151+
sign_option: _,
11351152
} = options;
11361153

11371154
match write_rebase_state_to_disk(effects, git_run_info, repo, rebase_plan, options)? {
@@ -1176,6 +1193,9 @@ pub struct ExecuteRebasePlanOptions {
11761193

11771194
/// If `HEAD` was moved, the options for checking out the new `HEAD` commit.
11781195
pub check_out_commit_options: CheckOutCommitOptions,
1196+
1197+
/// GPG-sign commits.
1198+
pub sign_option: SignOption,
11791199
}
11801200

11811201
/// The result of executing a rebase plan.
@@ -1221,6 +1241,7 @@ pub fn execute_rebase_plan(
12211241
force_on_disk,
12221242
resolve_merge_conflicts,
12231243
check_out_commit_options: _,
1244+
sign_option: _,
12241245
} = options;
12251246

12261247
if !force_on_disk {

git-branchless-lib/src/git/sign.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use tracing::instrument;
33
use super::{repo::Result, Repo, RepoError};
44

55
/// GPG-signing option.
6-
#[derive(Debug, PartialEq, Eq)]
6+
#[derive(Debug, Clone, PartialEq, Eq)]
77
pub enum SignOption {
88
/// Sign commits conditionally based on the `commit.gpgsign` configuration and
99
/// and the key `user.signingkey`.
@@ -26,6 +26,22 @@ impl SignOption {
2626
Self::Disable => Some("--no-gpg-sign".to_string()),
2727
}
2828
}
29+
30+
/// GPG-signing flag to use for interactive rebase
31+
pub fn as_rebase_flag(&self, repo: &Repo) -> Result<Option<String>> {
32+
Ok(match self {
33+
Self::UseConfig => {
34+
let config = repo.inner.config().map_err(RepoError::ReadConfig)?;
35+
match config.get_bool("commit.gpgsign").ok() {
36+
Some(true) => Some("-S".to_string()),
37+
Some(false) | None => None,
38+
}
39+
}
40+
Self::UseConfigKey => Some("-S".to_string()),
41+
Self::KeyOverride(keyid) => Some(format!("-S{}", keyid)),
42+
Self::Disable => None,
43+
})
44+
}
2945
}
3046

3147
/// Get commit signer configured from CLI arguments and repository configurations.

git-branchless-lib/tests/test_rewrite_plan.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use branchless::core::rewrite::{
1515
execute_rebase_plan, BuildRebasePlanOptions, ExecuteRebasePlanOptions, ExecuteRebasePlanResult,
1616
RebasePlan, RebasePlanBuilder, RepoResource,
1717
};
18+
use branchless::git::SignOption;
1819
use git_branchless_testing::{make_git, Git};
1920

2021
#[test]
@@ -713,6 +714,7 @@ fn create_and_execute_plan(
713714
reset: false,
714715
render_smartlog: false,
715716
},
717+
sign_option: SignOption::Disable,
716718
};
717719
let git_run_info = git.get_git_run_info();
718720
let result = execute_rebase_plan(

git-branchless-move/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ pub fn r#move(
261261
resolve_merge_conflicts,
262262
dump_rebase_constraints,
263263
dump_rebase_plan,
264+
ref sign_options,
264265
} = *move_options;
265266
let now = SystemTime::now();
266267
let event_tx_id = event_log_db.make_transaction_id(now, "move")?;
@@ -471,6 +472,7 @@ pub fn r#move(
471472
force_on_disk,
472473
resolve_merge_conflicts,
473474
check_out_commit_options: Default::default(),
475+
sign_option: sign_options.to_owned().into(),
474476
};
475477
execute_rebase_plan(
476478
effects,

git-branchless-opts/src/lib.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ pub struct MoveOptions {
9191
/// executing it.
9292
#[clap(action, long = "debug-dump-rebase-plan")]
9393
pub dump_rebase_plan: bool,
94+
95+
/// GPG-sign commits.
96+
#[clap(flatten)]
97+
pub sign_options: SignOptions,
9498
}
9599

96100
/// Options for traversing commits.
@@ -179,7 +183,7 @@ pub struct SwitchOptions {
179183
}
180184

181185
/// Options for signing commits
182-
#[derive(Args, Debug)]
186+
#[derive(Args, Debug, Clone)]
183187
pub struct SignOptions {
184188
/// GPG-sign commits. The `keyid` argument is optional and defaults to the committer
185189
/// identity.
@@ -464,10 +468,6 @@ pub enum Command {
464468
/// formatting or refactoring changes.
465469
#[clap(long)]
466470
reparent: bool,
467-
468-
/// Options for signing commits.
469-
#[clap(flatten)]
470-
sign_options: SignOptions,
471471
},
472472

473473
/// Gather information about recent operations to upload as part of a bug
@@ -650,6 +650,10 @@ pub enum Command {
650650
/// use with `git rebase --autosquash`) targeting the supplied commit.
651651
#[clap(value_parser, long = "fixup", conflicts_with_all(&["messages", "discard"]))]
652652
commit_to_fixup: Option<Revset>,
653+
654+
/// GPG-sign commits.
655+
#[clap(flatten)]
656+
sign_options: SignOptions,
653657
},
654658

655659
/// `smartlog` command.

git-branchless-record/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ fn record(
215215
effects,
216216
git_run_info,
217217
now,
218-
event_tx_id
218+
event_tx_id,
219+
sign_option,
219220
)?);
220221
}
221222

@@ -357,6 +358,7 @@ fn insert_before_siblings(
357358
git_run_info: &GitRunInfo,
358359
now: SystemTime,
359360
event_tx_id: EventTransactionId,
361+
sign_option: &SignOption,
360362
) -> EyreExitOr<()> {
361363
// Reopen the repository since references may have changed.
362364
let repo = Repo::from_dir(&git_run_info.working_directory)?;
@@ -484,6 +486,7 @@ To proceed anyways, run: git move -f -s 'siblings(.)",
484486
force_on_disk: false,
485487
resolve_merge_conflicts: false,
486488
check_out_commit_options: Default::default(),
489+
sign_option: sign_option.to_owned(),
487490
};
488491
let result = execute_rebase_plan(
489492
effects,

git-branchless-reword/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use lib::core::rewrite::{
4242
};
4343
use lib::git::{message_prettify, Commit, GitRunInfo, MaybeZeroOid, NonZeroOid, Repo};
4444

45-
use git_branchless_opts::{ResolveRevsetOptions, Revset};
45+
use git_branchless_opts::{ResolveRevsetOptions, Revset, SignOptions};
4646
use git_branchless_revset::resolve_commits;
4747

4848
/// The commit message(s) provided by the user.
@@ -67,6 +67,7 @@ pub fn reword(
6767
messages: InitialCommitMessages,
6868
git_run_info: &GitRunInfo,
6969
force_rewrite_public_commits: bool,
70+
sign_options: SignOptions,
7071
) -> EyreExitOr<()> {
7172
let repo = Repo::from_current_dir()?;
7273
let references_snapshot = repo.get_references_snapshot()?;
@@ -290,6 +291,7 @@ pub fn reword(
290291
reset: false,
291292
render_smartlog: false,
292293
},
294+
sign_option: sign_options.into(),
293295
};
294296
let result = execute_rebase_plan(
295297
effects,

git-branchless-submit/src/phabricator.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ use lib::core::rewrite::{
2626
execute_rebase_plan, BuildRebasePlanError, BuildRebasePlanOptions, ExecuteRebasePlanOptions,
2727
ExecuteRebasePlanResult, RebasePlanBuilder, RebasePlanPermissions, RepoResource,
2828
};
29-
use lib::git::{Commit, GitRunInfo, MaybeZeroOid, NonZeroOid, Repo, RepoError, TestCommand};
29+
use lib::git::{
30+
Commit, GitRunInfo, MaybeZeroOid, NonZeroOid, Repo, RepoError, SignOption, TestCommand,
31+
};
3032
use lib::try_exit_code;
3133
use lib::util::{ExitCode, EyreExitOr};
3234
use rayon::ThreadPoolBuilder;
@@ -359,6 +361,7 @@ impl Forge for PhabricatorForge<'_> {
359361
render_smartlog: false,
360362
..Default::default()
361363
},
364+
sign_option: SignOption::Disable,
362365
};
363366
let permissions =
364367
RebasePlanPermissions::verify_rewrite_set(self.dag, build_options, &commit_set)
@@ -608,6 +611,7 @@ Differential Revision: https://phabricator.example.com/D000$(git rev-list --coun
608611
render_smartlog: false,
609612
..Default::default()
610613
},
614+
sign_option: SignOption::Disable,
611615
};
612616
let permissions =
613617
RebasePlanPermissions::verify_rewrite_set(self.dag, build_options, &commit_set)

git-branchless-test/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ use lib::core::rewrite::{
5151
use lib::git::{
5252
get_latest_test_command_path, get_test_locks_dir, get_test_tree_dir, get_test_worktrees_dir,
5353
make_test_command_slug, Commit, ConfigRead, GitRunInfo, GitRunResult, MaybeZeroOid, NonZeroOid,
54-
Repo, SerializedNonZeroOid, SerializedTestResult, TestCommand, WorkingCopyChangesType,
55-
TEST_ABORT_EXIT_CODE, TEST_INDETERMINATE_EXIT_CODE, TEST_SUCCESS_EXIT_CODE,
54+
Repo, SerializedNonZeroOid, SerializedTestResult, SignOption, TestCommand,
55+
WorkingCopyChangesType, TEST_ABORT_EXIT_CODE, TEST_INDETERMINATE_EXIT_CODE,
56+
TEST_SUCCESS_EXIT_CODE,
5657
};
5758
use lib::try_exit_code;
5859
use lib::util::{get_sh, ExitCode, EyreExitOr};
@@ -386,6 +387,7 @@ BUG: Expected resolved_interactive ({resolved_interactive:?}) to match interacti
386387
resolve_merge_conflicts,
387388
dump_rebase_constraints,
388389
dump_rebase_plan,
390+
sign_options,
389391
} = move_options;
390392

391393
let force_in_memory = true;
@@ -414,6 +416,7 @@ BUG: Expected resolved_interactive ({resolved_interactive:?}) to match interacti
414416
render_smartlog: false,
415417
..Default::default()
416418
},
419+
sign_option: sign_options.to_owned().into(),
417420
};
418421
let permissions =
419422
match RebasePlanPermissions::verify_rewrite_set(dag, build_options, commits)? {
@@ -730,6 +733,7 @@ fn set_abort_trap(
730733
render_smartlog: false,
731734
..Default::default()
732735
},
736+
sign_option: SignOption::Disable,
733737
},
734738
)? {
735739
ExecuteRebasePlanResult::Succeeded { rewritten_oids: _ } => {

git-branchless/src/commands/amend.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use lib::core::rewrite::{
2626
execute_rebase_plan, move_branches, BuildRebasePlanOptions, ExecuteRebasePlanOptions,
2727
ExecuteRebasePlanResult, RebasePlanBuilder, RebasePlanPermissions, RepoResource,
2828
};
29-
use lib::git::{get_signer, SignOption};
29+
use lib::git::get_signer;
3030
use lib::git::{AmendFastOptions, GitRunInfo, MaybeZeroOid, Repo, ResolvedReferenceInfo};
3131
use lib::try_exit_code;
3232
use lib::util::{ExitCode, EyreExitOr};
@@ -41,7 +41,6 @@ pub fn amend(
4141
resolve_revset_options: &ResolveRevsetOptions,
4242
move_options: &MoveOptions,
4343
reparent: bool,
44-
sign_option: SignOption,
4544
) -> EyreExitOr<()> {
4645
let now = SystemTime::now();
4746
let timestamp = now.duration_since(SystemTime::UNIX_EPOCH)?.as_secs_f64();
@@ -157,6 +156,7 @@ pub fn amend(
157156
)
158157
};
159158

159+
let sign_option = move_options.sign_options.to_owned().into();
160160
let signer = get_signer(&repo, &sign_option)?;
161161

162162
let amended_commit_oid = repo.amend_commit(
@@ -305,6 +305,7 @@ pub fn amend(
305305
reset: true,
306306
render_smartlog: false,
307307
},
308+
sign_option,
308309
};
309310
match execute_rebase_plan(
310311
effects,

0 commit comments

Comments
 (0)