Note: This tutorial uses default
instead of master
for the main branch of a Git repository. See this Django PR: https://code.djangoproject.com/ticket/22667
The scenario is as follows:
There is a default
branch where features are merged when they're done.
Whenever you work on a feature or bugfix, you checkout the latest default
and create a new feature branch:
git checkout -b 'feature/unicorn-color-scheme'
You then start your work and in the process create a bunch of commmits. Since you're a responsible developer, you commit and push each day before you leave the office. 😎
So when you're done, check your git history since branch creation with git log default..
This will show you all the commits since your branch diverged off of default
including the latest commit that both have in common right at the end of the list (this is important!).
commit 67a7ad1d973b3ef366ce6701afb146b77d8fba97 (HEAD -> feature/unicorn-color-scheme)
Author: Leon Weidauer <leon@lnwdr.de>
Date: Sat Jul 01 10:29:45 2017 +0200
bump version number
commit 13c8fbdfb5e61d416d1e95c8ce4abe665977c746
Author: Leon Weidauer <leon@lnwdr.de>
Date: Fri Jun 30 18:17:30 2017 +0200
work in progress
commit bd05ca3c2ab542b81016a75752e793092b0898c9
Author: Leon Weidauer <leon@lnwdr.de>
Date: Fri Jun 30 13:45:51 2017 +0200
add tests
commit 6e7f6e9ac9710fb487b6bec599499f554327207d
Author: Leon Weidauer <leon@lnwdr.de>
Date: Thu Jun 29 18:43:02 2017 +0200
work in progress
commit 0acf697dd1e599a00a2df2fc18c831903b530b43
Author: Leon Weidauer <leon@lnwdr.de>
Date: Thu Jun 29 12:34:08 2017 +0200
work in progress
commit 6191c8d97f33a725a0d89657e2da7bb29fab8dc3
Author: Leon Weidauer <leon@lnwdr.de>
Date: Wed Jun 28 10:12:54 2017 +0200
latest design changes
commit 09e3c3a2fdff9035225cf7461324a8265664e3b3
Author: Leon Weidauer <leon@lnwdr.de>
Date: Tue Jun 27 16:23:06 2017 +0200
add new color scheme
commit aa028af2a168e259b33940c0c012b490c2981fe2 (default)
Author: Leon Weidauer <leon@lnwdr.de>
Date: Mon Jun 26 14:36:56 2017 +0200
initial
Now, if you'd merge this as-is, your Git history would be cluttered with all these commits and later it can be hard to tell, which commits belong to which feature. Also, cherry-picking or reverting them would be difficult.
To squash your work into one single commit, enter an interactive rebase session from your branch's HEAD down to but not including the latest common commit of both branches:
# use the commit hash of the last commit from the log
git rebase -i aa028af2a168e259b33940c0c012b490c2981fe2
Git will open a text editor with a view like this:
pick 09e3c3a add new color scheme
pick 6191c8d latest design changes
pick 0acf697 work in progress
pick 6e7f6e9 work in progress
pick bd05ca3 add tests
pick 13c8fbd work in progress
pick 67a7ad1 bumb version number
# Rebase aa028af..67a7ad1 onto aa028af (7 commands)
#
# 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
# d, drop = remove commit
#
# 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
Lines starting with #
will be ignored by Git, just like in commit messages.
Now replace pick
with squash
(or just s
) for all but the top line:
pick 09e3c3a add new color scheme
s 6191c8d latest design changes
s 0acf697 work in progress
s 6e7f6e9 work in progress
s bd05ca3 add tests
s 13c8fbd work in progress
s 67a7ad1 bump version number
Save the text file and close the editor just like you would when writing a commit message.
Git will now let you write the commit message for the new, squashed commit:
# This is a combination of 7 commits.
# This is the 1st commit message:
add new color scheme
# This is the commit message #2:
latest design changes
# This is the commit message #3:
work in progress
# This is the commit message #4:
work in progress
# This is the commit message #5:
add tests
# This is the commit message #6:
work in progress
# This is the commit message #7:
bump version number
It will consist of all the messages from the squashed commmits but you should probably remove everything that is not important for the changelog:
add unicorn color scheme
* create new scheme
* incorporate latest design changes
* add tests
Again, save and close the editor. Git will now create a new commit with that message that contains all your work.
If you check git log default..
again, it will look like this:
commit 1b62f3f8140b84cebed79cc77999bddd29493047 (HEAD -> feature/unicorn-color-scheme)
Author: Leon Weidauer <leon@lnwdr.de>
Date: Sat Jul 01 18:42:30 2017 +0200
add unicorn color scheme
* create new scheme
* incorporate latest design changes
* add tests
If you knwo how many commits you have to squash, let's say 5, you can start the interactibe rebase immediately without finding the latest common commit first:
git rebase -i HEAD~5
You can also just start a rebase session with a larger number of commits and just leave the ones that done belong to your branch as pick
.