Skip to content

Commit 55e422b

Browse files
committed
Take rebase options from post- -- arguments
Options will be read from all arguments after `--`, and will be passed to git rebase as-is. Throws an error if --and-rebase is not also specified.
1 parent 2d78529 commit 55e422b

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

Documentation/git-absorb.adoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ FLAGS
4444

4545
-r::
4646
--and-rebase::
47-
Run rebase if successful
47+
Run rebase if successful.
48+
See also the REBASE_OPTIONS below.
4849

4950
-n::
5051
--dry-run::
@@ -93,6 +94,11 @@ OPTIONS
9394
Generate completions
9495
[possible values: bash, fish, nushell, zsh, powershell, elvish]
9596

97+
-- <REBASE_OPTIONS>::
98+
Options to pass to git rebase after generating commits.
99+
Must be the last arguments and the `--` must be present.
100+
Only valid when `--and-rebase` is used.
101+
96102
USAGE
97103
-----
98104

src/lib.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct Config<'a> {
1515
pub force_detach: bool,
1616
pub base: Option<&'a str>,
1717
pub and_rebase: bool,
18+
pub rebase_options: &'a Vec<&'a str>,
1819
pub whole_file: bool,
1920
pub one_fixup_per_commit: bool,
2021
}
@@ -27,6 +28,12 @@ pub fn run(logger: &slog::Logger, config: &Config) -> Result<()> {
2728
}
2829

2930
fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository) -> Result<()> {
31+
if !config.rebase_options.is_empty() && !config.and_rebase {
32+
return Err(anyhow!(
33+
"REBASE_OPTIONS were specified without --and-rebase flag"
34+
));
35+
}
36+
3037
let config = config::unify(&config, repo);
3138
let stack = stack::working_stack(
3239
repo,
@@ -367,6 +374,10 @@ fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository
367374
let mut command = Command::new("git");
368375
command.args(["rebase", "--interactive", "--autosquash", "--autostash"]);
369376

377+
for arg in config.rebase_options {
378+
command.arg(arg);
379+
}
380+
370381
if number_of_parents == 0 {
371382
command.arg("--root");
372383
} else {
@@ -480,6 +491,7 @@ fn index_stats(repo: &git2::Repository) -> Result<git2::DiffStats> {
480491

481492
#[cfg(test)]
482493
mod tests {
494+
use git2::message_trailers_strs;
483495
use std::path::PathBuf;
484496

485497
use super::*;
@@ -684,6 +696,71 @@ mod tests {
684696
assert!(nothing_left_in_index(&ctx.repo).unwrap());
685697
}
686698

699+
#[test]
700+
fn and_rebase_flag_with_rebase_options() {
701+
let ctx = repo_utils::prepare_and_stage();
702+
repo_utils::set_config_option(&ctx.repo, "core.editor", "true");
703+
704+
// run 'git-absorb'
705+
let drain = slog::Discard;
706+
let logger = slog::Logger::root(drain, o!());
707+
let config = Config {
708+
and_rebase: true,
709+
rebase_options: &vec!["--signoff"],
710+
..DEFAULT_CONFIG
711+
};
712+
repo_utils::run_in_repo(&ctx, || run_with_repo(&logger, &config, &ctx.repo)).unwrap();
713+
714+
let mut revwalk = ctx.repo.revwalk().unwrap();
715+
revwalk.push_head().unwrap();
716+
assert_eq!(revwalk.count(), 1);
717+
718+
let trailers = message_trailers_strs(
719+
ctx.repo
720+
.head()
721+
.unwrap()
722+
.peel_to_commit()
723+
.unwrap()
724+
.message()
725+
.unwrap(),
726+
)
727+
.unwrap();
728+
assert_eq!(
729+
trailers
730+
.iter()
731+
.filter(|trailer| trailer.0 == "Signed-off-by")
732+
.count(),
733+
1
734+
);
735+
736+
assert!(nothing_left_in_index(&ctx.repo).unwrap());
737+
}
738+
739+
#[test]
740+
fn rebase_options_without_and_rebase_flag() {
741+
let ctx = repo_utils::prepare_and_stage();
742+
743+
// run 'git-absorb'
744+
let drain = slog::Discard;
745+
let logger = slog::Logger::root(drain, o!());
746+
let config = Config {
747+
rebase_options: &vec!["--some-option"],
748+
..DEFAULT_CONFIG
749+
};
750+
let result = run_with_repo(&logger, &config, &ctx.repo);
751+
752+
assert_eq!(
753+
result.err().unwrap().to_string(),
754+
"REBASE_OPTIONS were specified without --and-rebase flag"
755+
);
756+
757+
let mut revwalk = ctx.repo.revwalk().unwrap();
758+
revwalk.push_head().unwrap();
759+
assert_eq!(revwalk.count(), 1);
760+
let is_something_in_index = !nothing_left_in_index(&ctx.repo).unwrap();
761+
assert!(is_something_in_index);
762+
}
763+
687764
fn autostage_common(ctx: &repo_utils::Context, file_path: &PathBuf) -> (PathBuf, PathBuf) {
688765
// 1 modification w/o staging
689766
let path = ctx.join(&file_path);
@@ -809,6 +886,7 @@ mod tests {
809886
force_detach: false,
810887
base: None,
811888
and_rebase: false,
889+
rebase_options: &Vec::new(),
812890
whole_file: false,
813891
one_fixup_per_commit: false,
814892
};

src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ struct Cli {
3232
/// Run rebase if successful
3333
#[clap(long, short = 'r')]
3434
and_rebase: bool,
35+
/// Extra arguments to pass to git rebase. Only valid if --and-rebase is set
36+
#[clap(last = true)]
37+
rebase_options: Vec<String>,
3538
/// Generate completions
3639
#[clap(long, value_name = "SHELL", value_parser = ["bash", "fish", "nushell", "zsh", "powershell", "elvish"])]
3740
gen_completions: Option<String>,
@@ -52,6 +55,7 @@ fn main() {
5255
force,
5356
verbose,
5457
and_rebase,
58+
rebase_options,
5559
gen_completions,
5660
whole_file,
5761
one_fixup_per_commit,
@@ -93,6 +97,7 @@ fn main() {
9397
));
9498
}
9599

100+
let rebase_options: Vec<&str> = rebase_options.iter().map(AsRef::as_ref).collect();
96101
if let Err(e) = git_absorb::run(
97102
&logger,
98103
&git_absorb::Config {
@@ -101,6 +106,7 @@ fn main() {
101106
force_detach: force_detach || force,
102107
base: base.as_deref(),
103108
and_rebase,
109+
rebase_options: &rebase_options,
104110
whole_file,
105111
one_fixup_per_commit,
106112
},

0 commit comments

Comments
 (0)