Ken Williams
Ken Williams

Reputation: 24015

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.

Upvotes: 1

Views: 433

Answers (2)

K. A. Buhr
K. A. Buhr

Reputation: 51169

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!

Upvotes: 1

torek
torek

Reputation: 490068

Yes: when an interactive rebase opens the editor, it opens one based on different environment settings:

  • GIT_SEQUENCE_EDITOR is used for the commands to run, but
  • GIT_EDITOR is used for reword operations.

(If one of these is not set, Git falls back to sequence.editor or core.editor or the built in editor as usual. You could change those settings, perhaps temporarily, but the environment variable is easier.)

Hence you can use:

GIT_SEQUENCE_EDITOR=script1 GIT_EDITOR=script2 git rebase -i <arguments>

where script1 replaces edit with reword, and script2 does the s/BB-123/BB-456/ you want, for instance.

Upvotes: 2

Related Questions