Extracting changes from a commit
Once you start being mindful of what changes should go into each commit, you’ll eventually find yourself with a commit that contains fewer changes than it was supposed to have. Luckily, there’s an easy fix: stage the remaining changes and amend the commit.
But what if the commit contains more changes than it was supposed to have? How do you amend a commit by removing changes from it?
Three methods
There are three different ways to extract changes from a commit.
Method 1: Reuse changes
A simple approach is to undo the commit while keeping the changes in the working directory, then commit again without the unwanted change:
git reset HEAD~1
This method has the advantage of relying on commands you probably already know and use often.
Method 2: Reuse message
If the commit you’re trying to amend has a long commit message, then it can be annoying to have to retype it (or copy to/paste from a temporary location) when you recreate the commit using Method 1. Luckily, Commit accepts the flag --reuse-message
:
git commit --reuse-message HEAD@{1} --reset-author
The command above assumes you’ve just undone the commit you want to change with Reset. The revision HEAD@{1}
means “the commit previously referenced by HEAD
”1, and the flag --reset-author
is optionally used to reset the timestamp (otherwise the timestamp of the original commit is preserved).
Method 3: Reuse commit
Having to redo the whole commit can feel like a lot of rework if you only need to extract a small change from it. In this case, you can use Reset with the -p
flag:
Similar to staging changes interactively, this option goes through changes from the last commit and prompts for an action. But there’s a catch: the changes appear as the inverse of the changes originally added to the commit.
As suggested by the message in the prompt, choosing the action y
will cause the change to be applied to the index (a.k.a. staging area), but not to the working directory:
The three trees
In computing, trees are data structures used to represent files and directories. You can think of them as a mapping between file paths and their contents. There are three important trees in Git: working directory, staging area and repository. Learning what they are and how different commands interact with them is key to using Git effectively.
At this point, if you amend the commit with the inverse changes in the staging area, they will cancel out the changes you wanted to extract from the previous commit. Running a few variations of Diff might help you understand what’s going on:
git diff --staged
git diff
git diff HEAD
2
Consequences
Regardless of which method you choose, redoing/amending a commit will rewrite history. As such, you’ll need to deal with the implications of doing so.
Using Diff against HEAD
allows you to preview the result of amending the latest commit with the contents of the staging area.