From c3692bccfa219c6959bbe1426728f0f35d0f8bdd Mon Sep 17 00:00:00 2001 From: Blair Conrad Date: Tue, 27 May 2025 06:33:06 -0400 Subject: [PATCH 1/4] Fix badly-rendered double hyphen in documentation --- Documentation/git-absorb.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-absorb.adoc b/Documentation/git-absorb.adoc index d6bce4c..ce33bb6 100644 --- a/Documentation/git-absorb.adoc +++ b/Documentation/git-absorb.adoc @@ -98,7 +98,7 @@ OPTIONS Generate completions [possible values: bash, fish, nushell, zsh, powershell, elvish] --- :: +\-- :: Options to pass to git rebase after generating commits. Must be the last arguments and the `--` must be present. Only valid when `--and-rebase` is used. From 35763de5939b62a65d367edb7ee96724ed9f5c33 Mon Sep 17 00:00:00 2001 From: Blair Conrad Date: Thu, 29 May 2025 05:37:27 -0400 Subject: [PATCH 2/4] Add test to characterize default commit messages --- src/lib.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d821789..c7d17c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -718,6 +718,15 @@ mod tests { let pre_absorb_ref_commit = ctx.repo.refname_to_id("PRE_ABSORB_HEAD").unwrap(); assert_eq!(pre_absorb_ref_commit, actual_pre_absorb_commit); + assert_eq!( + extract_commit_messages(&ctx.repo), + vec![ + "fixup! Initial commit.\n", + "fixup! Initial commit.\n", + "Initial commit.", + ] + ); + log_utils::assert_log_messages_are( capturing_logger.visible_logs(), vec![ @@ -1814,6 +1823,23 @@ mod tests { assert_eq!(actual_msg, expected_msg); } + /// Perform a revwalk from HEAD, extracting the commit messages. + fn extract_commit_messages(repo: &git2::Repository) -> Vec { + let mut revwalk = repo.revwalk().unwrap(); + revwalk.push_head().unwrap(); + + let mut messages = Vec::new(); + + for oid in revwalk { + let commit = repo.find_commit(oid.unwrap()).unwrap(); + if let Some(message) = commit.message() { + messages.push(message.to_string()); + } + } + + messages + } + const DEFAULT_CONFIG: Config = Config { dry_run: false, force_author: false, From 7edcccdcf796f312d00fe6068e76f98b90d9bc1e Mon Sep 17 00:00:00 2001 From: Blair Conrad Date: Thu, 29 May 2025 05:37:45 -0400 Subject: [PATCH 3/4] Add --squash flag to create squash commits --- Documentation/git-absorb.adoc | 8 +++++++ src/lib.rs | 40 ++++++++++++++++++++++++++++++++++- src/main.rs | 5 +++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Documentation/git-absorb.adoc b/Documentation/git-absorb.adoc index ce33bb6..f8bc27a 100644 --- a/Documentation/git-absorb.adoc +++ b/Documentation/git-absorb.adoc @@ -66,6 +66,14 @@ FLAGS Skip all safety checks as if all --force-* flags were given. See those flags to understand the full effect of supplying --force. +-s:: +--squash:: + Create squash commits instead of fixup commits. + + + When this flag is used, "fixup commit" may be read as "squash commit" + throughout the documentation. All configuration relating to fixup + commits will apply to the squash commits instead. + -w:: --whole-file:: Match the first commit touching the same file as the current hunk. diff --git a/src/lib.rs b/src/lib.rs index c7d17c9..8348ccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ pub struct Config<'a> { pub rebase_options: &'a Vec<&'a str>, pub whole_file: bool, pub one_fixup_per_commit: bool, + pub squash: bool, pub message: Option<&'a str>, } @@ -321,7 +322,8 @@ fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository .stats()?; if !config.dry_run { head_tree = new_head_tree; - let mut message = format!("fixup! {}\n", dest_commit_locator); + let verb = if config.squash { "squash" } else { "fixup" }; + let mut message = format!("{}! {}\n", verb, dest_commit_locator); if let Some(m) = config.message.filter(|m| !m.is_empty()) { message.push('\n'); message.push_str(m); @@ -1504,6 +1506,41 @@ mod tests { assert!(is_something_in_index); } + #[test] + fn squash_flag() { + let ctx = repo_utils::prepare_and_stage(); + + // run 'git-absorb' + let mut capturing_logger = log_utils::CapturingLogger::new(); + let config = Config { + squash: true, + ..DEFAULT_CONFIG + }; + run_with_repo(&capturing_logger.logger, &config, &ctx.repo).unwrap(); + + assert_eq!( + extract_commit_messages(&ctx.repo), + vec![ + "squash! Initial commit.\n", + "squash! Initial commit.\n", + "Initial commit.", + ] + ); + + log_utils::assert_log_messages_are( + capturing_logger.visible_logs(), + vec![ + &json!({"level": "INFO", "msg": "committed"}), + &json!({"level": "INFO", "msg": "committed"}), + &json!({ + "level": "INFO", + "msg": "To squash the new commits, rebase:", + "command": "git rebase --interactive --autosquash --autostash --root", + }), + ], + ); + } + #[test] fn dry_run_flag() { let ctx = repo_utils::prepare_and_stage(); @@ -1849,6 +1886,7 @@ mod tests { rebase_options: &Vec::new(), whole_file: false, one_fixup_per_commit: false, + squash: false, message: None, }; } diff --git a/src/main.rs b/src/main.rs index 7a4301c..59e88cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,9 @@ struct Cli { /// Only generate one fixup per commit #[clap(long, short = 'F')] one_fixup_per_commit: bool, + /// Create squash commits instead of fixup + #[clap(long, short = 's')] + squash: bool, /// Commit message body that is given to all fixup commits #[clap(long, short)] message: Option, @@ -62,6 +65,7 @@ fn main() { gen_completions, whole_file, one_fixup_per_commit, + squash, message, } = Cli::parse(); @@ -113,6 +117,7 @@ fn main() { rebase_options: &rebase_options, whole_file, one_fixup_per_commit, + squash, message: message.as_deref(), }, ) { From c397f9200436cd0d9cb257c23c1772819184c722 Mon Sep 17 00:00:00 2001 From: Blair Conrad Date: Thu, 29 May 2025 05:33:55 -0400 Subject: [PATCH 4/4] Add createSquashCommits configuration option to always create squash commits --- Documentation/git-absorb.adoc | 16 ++++++++++++++++ src/config.rs | 9 +++++++++ src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/Documentation/git-absorb.adoc b/Documentation/git-absorb.adoc index f8bc27a..c4e5534 100644 --- a/Documentation/git-absorb.adoc +++ b/Documentation/git-absorb.adoc @@ -220,6 +220,22 @@ edit your local or global `.gitconfig` and add the following section: forceDetach = true ............................................................................. +GENERATE SQUASH COMMITS INSTEAD OF FIXUPS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, git-absorb will generate fixup commits. +To instead generate squash commits, edit your local or global `.gitconfig` +and add the following section: + +............................................................................. +[absorb] + createSquashCommits = true +............................................................................. + +When this option is set, "fixup commit" may be read as "squash commit" +throughout the documentation. All configuration relating to fixup +commits will apply to the squash commits instead. + GITHUB PROJECT -------------- diff --git a/src/config.rs b/src/config.rs index 39e576b..fc20e5f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,6 +19,9 @@ pub const AUTO_STAGE_IF_NOTHING_STAGED_DEFAULT: bool = false; pub const FIXUP_TARGET_ALWAYS_SHA_CONFIG_NAME: &str = "absorb.fixupTargetAlwaysSHA"; pub const FIXUP_TARGET_ALWAYS_SHA_DEFAULT: bool = false; +pub const CREATE_SQUASH_COMMITS_CONFIG_NAME: &str = "absorb.createSquashCommits"; +pub const CREATE_SQUASH_COMMITS_DEFAULT: bool = false; + pub fn unify<'config>(config: &'config Config, repo: &Repository) -> Config<'config> { Config { // here, we default to the git config value, @@ -36,6 +39,12 @@ pub fn unify<'config>(config: &'config Config, repo: &Repository) -> Config<'con ONE_FIXUP_PER_COMMIT_CONFIG_NAME, ONE_FIXUP_PER_COMMIT_DEFAULT, ), + squash: config.squash + || bool_value( + repo, + CREATE_SQUASH_COMMITS_CONFIG_NAME, + CREATE_SQUASH_COMMITS_DEFAULT, + ), force_author: config.force_author || bool_value(repo, FORCE_AUTHOR_CONFIG_NAME, FORCE_AUTHOR_DEFAULT), force_detach: config.force_detach diff --git a/src/lib.rs b/src/lib.rs index 8348ccd..156c1b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1541,6 +1541,39 @@ mod tests { ); } + #[test] + fn run_with_squash_config_option() { + let ctx = repo_utils::prepare_and_stage(); + + repo_utils::set_config_flag(&ctx.repo, "absorb.createSquashCommits"); + + // run 'git-absorb' + let mut capturing_logger = log_utils::CapturingLogger::new(); + run_with_repo(&capturing_logger.logger, &DEFAULT_CONFIG, &ctx.repo).unwrap(); + + assert_eq!( + extract_commit_messages(&ctx.repo), + vec![ + "squash! Initial commit.\n", + "squash! Initial commit.\n", + "Initial commit.", + ] + ); + + log_utils::assert_log_messages_are( + capturing_logger.visible_logs(), + vec![ + &json!({"level": "INFO", "msg": "committed"}), + &json!({"level": "INFO", "msg": "committed"}), + &json!({ + "level": "INFO", + "msg": "To squash the new commits, rebase:", + "command": "git rebase --interactive --autosquash --autostash --root", + }), + ], + ); + } + #[test] fn dry_run_flag() { let ctx = repo_utils::prepare_and_stage();