This is part 3 in my Favourite Git Commands series.
You should never change a commit that has been shared with someone else - it makes everything very confusing for your collaborators. This is one of the many reasons you should always be aware exactly what you’re committing. However…
It’s often useful to edit, reorder, split or merge your local (unpushed) commits - for example you might want to fix a bug you introduced 2 commits ago that you don’t want anyone to ever know about. EVER. This ability to easily edit history, fix mistakes in commits (for example if you accidentally added a password) and generally control your revision history is one of the things that makes git so powerful in my opinion.
git commit --amend
Sometimes working on the next commit reveals a bug or typo in a previous commit, in that instance it often makes sense to apply a fix to the previous commit directly. This will save time for your collaborators who may immediately spot the mistake, make a note of it or tell you about it, only to find out that you fixed it in the next commit. Better to fix it so no-one else has to process it, I think.
Other times you may notice you forgot to add a file or said
y to one of the
git add --patch hunks. To fix these situations
simply add any missing files, changes or fixes; review these changes;
git commit --amend.
You can also use this if you just want to change the commit message and nothing else, but be aware that this still changes the commit’s hash, so you still shouldn’t do this on commits that have been pushed.
git reset --soft HEAD^
This is useful if you messed up so bad you want to
remove the last commit and try again
- it resets the current branch to the first parent of HEAD and moves the
committed hunks back to the staging area. One reason you might want to
do this is if you accidentally did a
git commit -a and committed more
code than you intended to. (Tip: you can move changes from the
staging area to the working copy with
git reset HEAD [filename].)
If you want to throw away the code altogether then you can swap
--hard (warning: this discards your working copy changes too).
Before doing this it might make sense to branch - e.g.
messed_up_code - and stash your changes with
git stash. When you’re
happy you did want to discard that code, you can delete this branch with
git branch -d messed_up_code and drop the stash with
git stash drop.
git rebase --interactive HEAD~3
Almost everything in git can be achieved with the use of
Interactive mode is a great introduction to this very powerful mode, and
is useful for squashing (merging), reordering, dropping or editing
commits. When you run this command, it will allow you to chose various
options for each of the last 3 commits (see the help text at the bottom)
and then it will guide you through making the changes. Note that you can
reorder or drop commits by rearranging or deleting their lines.
For example, to edit the 3rd commit ago, I’d run the above command and
then change the relevant
edit in the editor that appears,
then save and exit. Git will then revert me back to that commit, where I
can make changes to the files in the local copy, and then
git commit --amend. When I’m happy with the result I
git rebase --continue and git will apply the future commits on
top of my newly edited commit. Recent versions of git very helpfully
tell us what we need to do at each stage.
Rebase commands in my version of git are:
pick= use commit
reword= use commit, but edit the commit message
edit= use commit, but stop for amending
squash= use commit, but meld into previous commit
squash, but discard this commit’s log message
exec= run command (the rest of the line) using shell
(and don’t forget you can also drop and reorder commits by deleting or rearranging their lines.)