Change local history making it appear linear thus clearer.
As any history change, should only be done before pushing to a remote.
(A)----(B)----(C) | | | master * | +-----(D) | feature
If you do:
git checkout feature git rebase master
(A)----(B)----(C)-------(D2) | | master feature *
Therefore the rebase changes the history, making it look linear and therefore easier to understand.
This is how you should incorporate upstreams changes on your feature branch before you make a pull request, followed often by a squash interactive rebase.
git rebase -i HEAD~3
Opens up a Vim buffer where you can modify all commits between
HEAD~2 (total 3 commits).
The buffer should contain something like this:
pick fc95d59 last - 2 commit message pick 81961e9 last - 1 commit message pick d13a071 last commit message # Rebase d57a363..d13a071 onto d57a363 # # 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 # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
edit can be used for example if we want to change the commit message for
HEAD~ we edit that to:
pick fc95d59 last - 2 commit message edit 81961e9 last - 1 commit message pick d13a071 last commit message
Save and quit.
Now git puts us back as
We can then:
git commit --amend -m 'new last - 1 commit message'
When you are satisfied:
git rebase --continue
If you change your mind and think that it is better not to rebase do:
git rebase --abort
If you change your mind only about a single
commit, but still want to change the others to:
git rebase --skip
And we are back to
git log --pretty=oneline -n3 gives:
fc95d59[...] last - 2 commit message 81961e9[...] new last - 1 commit message d13a071[...] last commit message
squash can be used if you want to remove all trace of a commit.
squash is useful when you are developing a feature locally and you want to save progress at several points in case you want to go back.
When you are done, you can expose a single commit for the feature, which will be much more concise and useful to others, or at least people will know that you can use
You will also look much smarter, since it will seem that you did not make lots of trials before getting things right.
If we want to remove only the
HEAD~ from history we edit as:
pick fc95d59 last - 2 commit message s 81961e9 last - 1 commit message pick d13a071 last commit message
This will open up another Vim buffer like:
# This is a combination of 2 commits. # The first commit's message is: last -2 commit message # This is the 2nd commit message: last -1 commit message #[more comments]
HEAD~2 will be turned into one, it is likely that the new message will be neither of the two.
So, erase all non comment lines and do something like:
last -1 and last -2 together #[more comments]
git log --pretty=oneline -n2 gives something like:
fc95d59[...] last -1 and last -2 together d13a071[...] last commit message
It is not possible to squash the last commit of a rebase:
squash fc95d59 last - 2 commit message pick 81961e9 last - 1 commit message pick d13a071 last commit message
To do that, it would be necessary to do a
git rabase -i HEAD~4, and
pick fc95d59 last - 2 commit message squash fc95d59 last - 2 commit message pick 81961e9 last - 1 commit message pick d13a071 last commit message
reorder and delete
It is also possible to reorder and erase any commit on the commit list.
All you need to do is to change the line order or remove lines.
TODO What does rebase do exactly? I think it was a series of merges like this:
A---B---C---D---E (master) | F---G (HEAD)
new_comit = merge(base, commit1, commit2)
be the basic 3-way merge operation, where:
basemust be is an ancestor of both
new_commitis always a child of
A---B---C---D---E (master) | | | +---F2 | | F-------+ | +---G (HEAD)
F2 = merge(B, F, C)
F are parents of
G is advanced over
A---B---C---D----E (master) | | | +---F2---G2 (HEAD) | | | F-------+ | | | +---G--------+
G2 = merge(F, F2, G)
Now we can just throw away the old
A---B---C---D----E (master) | +---F2---G2 (HEAD)
and it is clear that we have advanced one step!
Now we just repeat until advancing over
E to reach: