Search code examples
gitrebasegit-amend

Git rebase: fixup by running command


I'm on a branch that's many commits (in a straight line of history) ahead of my develop branch, and in each commit message is the name of the issue-tracker ticket I'm working on. Now I want to rebase through each commit and change the name of that ticket to a different ticket, conceptually something like:

perl -pi -e 's/BB-123/BB-456/' .git/COMMIT_EDITMSG

Is there a way to tell rebase's reword action that instead of opening an interactive editor, I want to run a command on the message text instead?

The best I've come up with would be to put an exec action after each commit that runs git commit --amend and changes the $EDITOR variable to the above command or something, but that's pretty horrible.

Without a good automatic solution, I usually rebase with a reword action on every commit and edit all the messages manually.


Solution

  • You're probably looking for git filter-branch. Suppose your log looks like this:

    * commit 7534b4609b84b87232ad933c83d5b802adfe2814 (HEAD -> wip/T1234)
    |
    |     More work on ticket #1234
    |
    * commit e23ef0941b7a52dbb46876db4c985a1c30ec5e16
    |
    |     Some work on ticket #1234
    |
    * commit b660a05e4d90fc8ae61b7d39e716ee3cdcc382ec (develop)
    |
    |     Current develop branch head
    |
    * commit 1b67c0b8b1b4f4c22ddc38ba78c499dde749006f
    
          Initial commit
    

    So, you have a straight-line history of commits from develop to wip/T1234. You realize the ticket should have read #1236, so you run:

    $ git filter-branch --msg-filter 'sed -e s/1234/1236/g' \
    >    develop..wip/T1234
    Rewrite 7534b4609b84b87232ad933c83d5b802adfe2814 (2/2) (0 seconds passed,
    remaining 0 predicted)
    Ref 'refs/heads/wip/T1234' was rewritten
    $
    

    and now the commits are rewritten:

    * commit c30e8becea1221d563d2be0e5a8c048c2c5bf608 (HEAD -> wip/T1234)
    |
    |     More work on ticket #1236
    |
    * commit 0417b49944b3c9311031a657bc8be3c8bf05b54d
    |
    |     Some work on ticket #1236
    |
    * commit b660a05e4d90fc8ae61b7d39e716ee3cdcc382ec (develop)
    |
    |     Current develop branch head
    |
    * commit 1b67c0b8b1b4f4c22ddc38ba78c499dde749006f
    
          Initial commit
    

    A copy of the original refs will be written to refs/original, so you can still access the pre-filtered version:

    $ git log refs/original/logs/refs/heads/wip/T1234
    ... version with #1234 still in place
    

    Obviously, you want to check the rewrites carefully before removing the originals!