Search code examples
git

git rebase --continue and --stepback?


Is there a way to step back a commit during an interactive rebase?


Solution

  • Yes there is. How to step back during an interactive rebase:

    1. Get the commit hash of your current HEAD, for example with git rev-parse HEAD
    2. run git rebase --edit-todo
    3. insert a pick with that hash to the top of that file pick <hash from step 1>
    4. run git reset --hard HEAD^ (this is a hard reset so if you have done anything you want to keep make sure to store it somewhere e.g. git stash before you run the command)

    Now you are still in the rebase but one commit back and you are free to continue rebasing with

    1. git rebase --continue.

    If you don't want the undone commit to be picked straight up without edits you can add edit <HASH> instead of pick <HASH> to the todo list (step 3).

    Idea from: http://arigrant.com/blog/2014/5/4/git-rebase-stepping-forward-and-back

    Addendum 3: If you are worried of missing a hash in the rebase, you can compare your todo file (.git/rebase-merge/git-rebase-todo) with the original one (.git/rebase-merge/git-rebase-todo.backup) as per comment from @Andrew Keeton .

    Addendum 1 and 2: You can remember more hashes, reset to an even earlier point and add multiple picks to redo more than one commit. When you edit the todo list it also contains a large comment about the different commands available:

    pick 8f8b9645c1c python: remove declare_namespace
    
    # Rebase 803c25a8314..8f8b9645c1c onto 803c25a8314 (1 command)
    #
    # 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 [-C | -c] <commit> = like "squash" but keep only the previous
    #                    commit's log message, unless -C is used, in which case
    #                    keep only this commit's message; -c is same as -C but
    #                    opens the editor
    # 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.