How to Amend Git Commits

I've talked about Git a couple of times before. It's fair to say that version control is a fundamental tool in any developer's arsenal, and knowing a little more about how it works, and how to use it really should be very high on any young developer's list of things to learn.
One situation that every developer will find themselves in time and again is needing to amend a commit that has already gone in. We've all been there. It's hardly the end of the world to simply push a second commit in, but I personally like a tidy Git history and it seems a bit much to create one for a tiny missed change all on its own.
Luckily, you can amend local git commits using git commit --amend.
Forgotten a File or Need to Edit One?
If you need to amend the contents of your previous commit, for example, you need to further edit a file, or you've missed one out altogether, then this is a quick and easy one.
In your branch, carry out the additional work that you need to do (or add the additional files you missed out), and then use git add to add the additional work to the Git index. If you're adding everything that you've changed, you can append the command with a ., this will add everything: git add .
With the updates added to the index, you can simply run:
git commit --amend --no-editThis takes the previous commit in the branch and amends it with the latest files you've added to the index. The additional --no-edit flag just means that you don't need to edit your commit message at all, and Git just needs to include the new files, within the previous commit.
Now you can push this commit, and it will have all the files you needed!
Need to Update Your Commit Message?
The other common scenario for needing to amend a Git commit is when you have made a mistake in the message itself. It happens to the best of us. A typo slips through, or you leave out a useful detail. Again, git commit --amend is the answer, this time allowing Git to open the message in your editor.
After you've made your commit with the message that you need to update, just run:
git commit --amendGit opens the existing message in your configured editor. Correct it, save and close the editor to complete the amendment. Alternatively, supply the whole message directly with git commit --amend -m "Corrected message"; -m needs that argument. Check the staging index first, because an amendment also includes staged changes. You can push normally if the original commit has not already been pushed.
Using Reset to Step Back through Git History
Another alternative for rewriting your local Git history is using reset instead of amend. Whereas amend can be used to modify the most recent commit in your local branch, reset can be used to simply undo the commit altogether.
reset is a complex and versatile tool, which I will only scratch the surface of today. Here, we are interested in the --soft flag, which will undo the previous (or several previous ‑ as you will see) commits, and place them back into the staging index:
git reset --soft HEAD~1This moves the tip of your local branch back by one commit without changing the index or working files. The changes from that commit therefore remain staged relative to the earlier commit, ready for you to review and commit again.
From here, you can make the change you need, and simply commit again. This is the technique I use most often, often keeping the original commit message in my clipboard or on a note on my desktop until I'm ready to commit it again.
The ~1 at the end of that command is important: if you found you wanted to undo further back down the repository history, you could change this number. git reset --soft HEAD~5 for example would undo the most recent five commits, placing all those changes back in your index. This is especially useful if you want to combine commits together before pushing up to the remote.
One word of warning: this is the command I most often use, and I tend to retrieve it from the shell history. In Bash's default key bindings, Control + R searches backwards through that history. If you have recently been undoing several commits, check which command appears before pressing Enter, or you may step back much further than you intended!
A Note About Amending Remote Commits
You may have noticed that my examples here today all focus on editing commits locally, which have not yet made their way into the remote repository. A word of advice though: if you have already pushed your commit up, I would generally, and absolutely not recommend attempting to amend a remote commit.
On a branch where you have agreed that rewriting history is acceptable, git push --force can replace the remote branch with your amended local history. It can also discard commits that somebody else has pushed in the meantime. Recovery is sometimes possible if the old commits remain in a local reflog or another clone, but it is not guaranteed: reflogs expire and unreachable objects can be removed. I would not rely on being able to put everything back afterwards.
For me, and in most of the teams I work within, it is simply not a risk worth taking. In a previous project I was involved in, one of my developers used --force, thinking they were in their branch, inadvertently overwriting the entire remote master branch. Fortunately, I had a recent local copy, but even then it was still several merge requests behind so we spent an afternoon reinstating master. Not an experience I would wish on another developer, and not a task I enjoyed undertaking!
So, my advice: if you find you need to amend a remote commit, then just create a new one and push it up. I would far rather have multiple commits in the Git log than lose my team's work ‑ or lose an afternoon of my team's productivity whilst we piece the remote back together again.