Search code examples
gitrebasesquashgit-squashgit-interactive-rebase

Which one is my previous commit when running interactive rebase?


When I run something like git rebase -i --rebase-merges eeb1425e0 my commits list is showed upside down, i.e., the first commit is the last and the last commit is the first:

pick A Penultimate commit
pick B Penult commmit
pick C Latest commit

# Rebase eeb1425e..5a3f045b onto eeb1425e (288 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Then, when the squash option says squash = use commit, but meld into previous commit, and I push squash as following:

pick A Penultimate commit
squash B Penult commmit
pick C Latest commit

The previous commit of B is A or C? Is B going to be merged/squashed with A or C?

Related to:

  1. How can I merge two commits into one if I already started rebase?
  2. What exactly does git's "rebase --preserve-merges" do (and why?)

Solution

  • Commit ordering is is indeed reversed from what you normally see with git log, so for:

    pick A Penultimate commit
    pick B Penult commmit
    pick C Latest commit
    

    if you change the second line to read squash, Git will:

    • cherry-pick commit A to begin making a new commit, but not make it yet;
    • squash commit B into that before actually making the new commit; and
    • cherry-pick commit C to make a second new commit;

    giving you what I like to draw, pictorially, as:

              A--B--C   [abandoned]
             /
    ...--o--o--AB--C'   <-- branch-name
    

    Here the A-B-C chain are the original three commits, with the last one drawn on the right (rather than top or bottom), and the new AB-C' chain are the two new commits, again with the last one drawn on the right. Your branch name used to identify commit C, and now identifies new copy C'.


    Side note: the word for third-from-last is antepenultimate. "Penultimate" literally means "second from last". If there is a fourth from last you need to identify, this is the preantepenultimate. There appears to be no English-language word for "fifth from last". Following links here arrives at propreantepenultimate, but see the status at the Collins dictionary for instance.

    Another side note: interactive rebase is willing to make and then unmake commits, so the description above ("squash before actually making commit") is just shorthand for what you'll see when you look at results, rather than a full description of the underlying machinery. The actual set of commits may include extra abandoned temporary commits. Git will clean all of these up on its own eventually.