Skip to content

Instantly share code, notes, and snippets.

@selfawaresoup
Last active July 10, 2017 14:47
Show Gist options
  • Save selfawaresoup/e95e13fa063edb00e7939b1c9a5e207e to your computer and use it in GitHub Desktop.
Save selfawaresoup/e95e13fa063edb00e7939b1c9a5e207e to your computer and use it in GitHub Desktop.
Squashing a feature branch in Git before merging

Squashing a feature branch in Git before merging

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

Shortcuts

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment