Reputation: 1280
You can programmatically edit only the last commit message:
git commit --amend -m 'xxxxxxx'
Or a random commit interactively:
git rebase -i HEAD~n
# Vim opens up, select the commit you want to modify, and change the word "pick" for "edit"
git commit --amend -m "Changing an old commit message!"
git rebase --continue
How do I combine both? I want to change a message programmatically, but to a prior commit, not just the last one.
The commit I want to modify has already been pushed to a git server, but having other people re-sync the git project is not a concern.
Upvotes: 17
Views: 25346
Reputation: 10958
Instead of rebasing and force pushing the modified branch, it's possible to replace a commit with a different message without affecting the existing commit hashes:
git checkout <commit>
git commit --amend -m "New message"
git replace <commit> $(git rev-parse HEAD)
This can also be done interactively with:
git replace --edit <commit>
Note that the modified references will then have to be pushed and fetched explicitly with:
git push origin 'refs/replace/*'
git fetch origin 'refs/replace/*:refs/replace/*'
Upvotes: 0
Reputation:
Since you want to make the change programmatically, interactive rebase (git rebase -i) is not an option.
Editing an old commit, for whatever reason, will effectively rebase all of the commits on top of that. If you're changing only the commit message, then you don't need to worry about merge conflicts.
You create a new temporary branch with the target commit as its HEAD, edit the commit message, merge the old branch onto the new one, and then delete the old temporary branch.
In a shell script:
CURBRANCH=`git rev-parse --abbrev-ref HEAD`
TMPBRANCH=tmp$$
git checkout $SHA -b $TMPBRANCH
MSG=`tempfile`
git log --format=%B -n 1 HEAD > $MSG
... edit the $MSG file
git commit --amend -F $MSG
SHA=`git rev-list -n 1 HEAD` # Commit has change, so SHA has also changed
rm $MSG
git rebase --onto $TMPBRANCH HEAD $CURBRANCH
git branch -d $TMPBRANCH
Upvotes: 7
Reputation: 164629
If you're just changing a few commits, use git rebase -i
and the "reword" option. For example...
pick 6256642 mv file1 file2
pick 20c2e82 Add another line to file2
# Rebase 8236784..20c2e82 onto 8236784 (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
Switch pick
to reword
and you'll be offered an editor to rewrite the commit message.
If you need to do the same thing to a lot of commits, use git filter-branch
with a --msg-filter
. The original commit message is on stdin, the new commit message is on stdout. Here's one to change "color" to "colour" in all commits in the current branch.
git filter-branch --msg-filter "perl -ple 's{color}{colour}g'"
Upvotes: 14
Reputation: 114219
The reason you can not simply "amend" an arbitrary commit is that commits are immutable. When you amend a commit, it actually replaces the current commit with another and moves your branch to the new commit. The commit with the old message, author name, etc. is still there in the history until you clean it up:
Before:
master
|
v
A -- B -- C
After:
master
|
v
A -- B -- C'
\
\- C
To simulate "amending" an arbitrary commit, you would have to rewrite not only that commit, but the entire history after it, since a commit includes its parents as part of its immutable data:
Before:
master
|
v
A -- B -- C
After:
master
|
v
A -- B' -- C'
\
\- B --- C
You can do this by creating a branch on the commit you are interested in, amending it, and rebasing the range of commits following the original to the tip of your original branch onto the new branch. Here is an example showing what you are after:
Start:
master
|
v
A -- B -- C -- D
New Branch:
master
|
v
A -- B -- C -- D
^
|
temp
Amend:
master
|
v
A -- B -- C -- D
\
\- B'
^
|
temp
Rebase:
A -- B -- C -- D
\
\- B' -- C' -- D'
^ ^
| |
temp master
Cleanup:
A -- B -- C -- D
\
\- B' -- C' -- D'
^
|
master
This is pretty much exactly what interactive rebase does when you only modify a single commit, by the way, except without the explicit temporary branch.
Upvotes: 15