Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Note: `cargo install` does not currently know how to install manpages ([cargo#27
1. `git add` any changes that you want to absorb. By design, `git absorb` will only consider content in the git index (staging area).
2. `git absorb`. This will create a sequence of commits on `HEAD`. Each commit will have a `fixup!` message indicating the message (if unique) or SHA of the commit it should be squashed into.
3. If you are satisfied with the output, `git rebase -i --autosquash` to squash the `fixup!` commits into their predecessors. You can set the [`GIT_SEQUENCE_EDITOR`](https://stackoverflow.com/a/29094904) environment variable if you don't need to edit the rebase TODO file.
4. If you are not satisfied (or if something bad happened), `git reset --soft` to the pre-absorption commit to recover your old state. (You can find the commit in question with `git reflog`.) And if you think `git absorb` is at fault, please [file an issue](https://github.com/tummychow/git-absorb/issues/new).
4. If you are not satisfied (or if something bad happened), `git reset --soft` to the pre-absorption commit to recover your old state. (You can find the commit in question with `git reflog`. Alternatively, you can run `git absorb` with `--one-reflog-entry`, in which case the pre-absorption commit will be `HEAD@{1}`.) And if you think `git absorb` is at fault, please [file an issue](https://github.com/tummychow/git-absorb/issues/new).

## How it works (roughly)

Expand Down
46 changes: 45 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod config;
mod owned;
mod stack;

use git2::ResetType;
use std::io::Write;
use std::path::Path;

Expand All @@ -19,6 +20,7 @@ pub struct Config<'a> {
pub rebase_options: &'a Vec<&'a str>,
pub whole_file: bool,
pub one_fixup_per_commit: bool,
pub one_reflog_entry: bool,
}

pub fn run(logger: &slog::Logger, config: &Config) -> Result<()> {
Expand Down Expand Up @@ -316,7 +318,7 @@ fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository
if !config.dry_run {
head_tree = new_head_tree;
head_commit = repo.find_commit(repo.commit(
Some("HEAD"),
(!config.one_reflog_entry).then_some("HEAD"),
&signature,
&signature,
&format!("fixup! {}\n", dest_commit_locator),
Expand All @@ -339,6 +341,14 @@ fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository
}
}

if config.one_reflog_entry && !config.dry_run {
let mut head_ref = repo.head()?;
head_ref.set_target(head_commit.id(), "absorb: adding fixup commits")?;
// If you don't like the fancy custom reflog message above and want to stick to Git's own
// messages, this would also work:
// repo.reset(head_commit.as_object(), ResetType::Soft, None)?;
Comment on lines +345 to +349
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If everything else is fine, an opinion would be needed here.

}

Comment on lines +344 to +351
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: This almost definitely doesn't work in combination with --and-rebase, which I forgot to consider => fix & add test.

Or would it be fine to just make --one-reflog-entry mutually exclusive with --and-rebase? 🤔 Because I think making these work together could become quite involved...

Copy link
Contributor Author

@sh-at-cs sh-at-cs Mar 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, another idea: Call the option --one-reflog-entry-for-commits or something like that, which makes it clear that this applies to the commits only and not to the subsequent rebase of --and-rebase. Thoughts?

if autostage_enabled && we_added_everything_to_index {
// now that the fixup commits have been created,
// we should unstage the remaining changes from the index.
Expand Down Expand Up @@ -557,6 +567,39 @@ mod tests {
assert!(nothing_left_in_index(&ctx.repo).unwrap());
}

#[test]
fn one_reflog_entry() {
let ctx = repo_utils::prepare_and_stage();

// get initial reflog length to compare
let initial_reflog_len = ctx.repo.reflog("HEAD").unwrap().len();

// run 'git-absorb'
let drain = slog::Discard;
let logger = slog::Logger::root(drain, o!());
let config = Config {
one_reflog_entry: true,
..DEFAULT_CONFIG
};
run_with_repo(&logger, &config, &ctx.repo).unwrap();

// sanity checks: all is as usual when it comes to commits and index:
let mut revwalk = ctx.repo.revwalk().unwrap();
revwalk.push_head().unwrap();
assert_eq!(revwalk.count(), 3);
assert!(nothing_left_in_index(&ctx.repo).unwrap());
Comment on lines +586 to +590
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copied this from the --one-fixup-per-commit test, but should anything else be checked to ensure this doesn't accidentally alter the behavior in other ways?


// check that only one reflog entry was created:
let reflog = ctx.repo.reflog("HEAD").unwrap();
let final_reflog_len = reflog.len();
assert_eq!(final_reflog_len, initial_reflog_len + 1);

// check reflog entry message:
let reflog_entry = reflog.get(0).unwrap();
let reflog_message = reflog_entry.message().unwrap();
assert_eq!(reflog_message, "absorb: adding fixup commits");
}

#[test]
fn foreign_author() {
let ctx = repo_utils::prepare_and_stage();
Expand Down Expand Up @@ -973,5 +1016,6 @@ mod tests {
rebase_options: &Vec::new(),
whole_file: false,
one_fixup_per_commit: false,
one_reflog_entry: false,
};
}
5 changes: 5 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ struct Cli {
/// Only generate one fixup per commit
#[clap(long, short = 'F')]
one_fixup_per_commit: bool,
/// Only generate one reflog entry, no matter how many commits were made
#[clap(long, short = 'F')]
one_reflog_entry: bool,
}

fn main() {
Expand All @@ -59,6 +62,7 @@ fn main() {
gen_completions,
whole_file,
one_fixup_per_commit,
one_reflog_entry,
} = Cli::parse();

if let Some(shell) = gen_completions {
Expand Down Expand Up @@ -109,6 +113,7 @@ fn main() {
rebase_options: &rebase_options,
whole_file,
one_fixup_per_commit,
one_reflog_entry,
},
) {
crit!(logger, "absorb failed"; "err" => e.to_string());
Expand Down