Move and undo commits and chains of commits
There's a difference between the content of a commit and the changes between a commit and its parent. Usually, the most useful thing is to move changes, but we'll look at both cases.
Moving the content in a commit
If you want to copy some or all of the files in a commit into your working copy, right-click the commit and select Apply content
. You can choose between:
all files into WC
copies every file from that commit into your working copy.some files into WC
copies individual files one at a time into your working copy.
Moving the changes in a commit
When you click a commit, you see the changes that took place between that commit and its parent. To apply those same changes to your WC, all you have to do is right-click the commit, select Apply delta
, and then one of its children:
and commit
all files into WC
some files into WC
This will create a patch for the changes in this commit, and then apply it as either a new commit (and commit
), or as an uncommitted change in the working copy (all files into WC
and some files into WC
). The distinction between all files
and some files
is whether you will apply every change, or just some of the changes.
Note: Vanilla git calls this "git cherry-pick". See departures from vanilla git for why we use a different name.
Undoing a commit
Given that we can create a patch from a commit to its parent, we can use the exact same process to create a patch from the parent to the commit - the inverse patch. By doing this, we will undo all the changes in that commit. This is super useful because it works even if many other commits have happened in the meantime.
Note: Vanilla git calls this "git revert". See departures from vanilla git for why we use a different name.
Move a chain of commits
Let's say you have a series of commits like this:
And you would prefer that they looked like this:
Happens all the time. The letters
branch is based off of the common
commit, and you would rather it be based on the 3
commit.
One way to do this is to right-click each commit, one at a time, and select Apply delta
.
A faster way to accomplish this is the rebase
command. Here's how it works:
- checkout the branch you want to move
- right-click the branch you'd like to change its base to
- select
rebase
Rebase onto origin/master
The scenario above happens especially often in a team setting - somebody else uploaded their changes before you did. Because it's so common, there's a shortcut for exactly this case:
But you need to be careful about rebasing branches which you have shared. When your branch moves to the new tip after a rebase, your old commits are lost.
That's fine if you haven't shared those commits with anyone else, but it's not good if you already have. In the situation below, for example, the commits in master
have already been published to origin/sharedFeature
.
If you rebase master
, there will be some duplicated commits!
Merging is never bad, only use rebase when you haven't shared the commits with anyone else.
rebase(123, abc) == merge(123, abc)
The final result of rebasing one branch onto another is exactly the same as the final result of merging those two branches, except for the history.
The only difference is in the story that your history will tell. So whether we like it or not, every time we choose either to merge or to rebase, we have made a subjective, editorial decision about what kind of history will tell a better story. Which sets us up perfectly for our next topic: rewriting history.