Git 101: Working with branches

This is a basic guide on how to create new local branches, and merging branches (while squashing all commits into a single commit) to another branch. In my team, we like to use the following model when it comes to git:

  • No commits are allowed on the master branch
  • All work is to be done either on a feature or develop(ment) branch
  • Once all work is completed and reviewed, we can push it back to develop(ment) branch
  • When merging back to develop(ment) branch, all commits are squashed into a single commit

The reason for that approach is as follows:
Development branch has a fully working code. Our CI server is building develop branch on each commit and promoted to master branch.
Feature branches are used to isolate feature development. Since all our code has to go through a review process and our CI servers auto-promote to master on each commit to development branch, all work is being done on feature branches.

So, this article is going to walk you through the command line approach to this model. For simplicity, I am assuming that you are working locally and not pushing your changes to a centralized server (i.e. GitHub).

Regardless of whether you agree with our flow or not, chances are, you will still be facing merging and squashing. Anyhow, here's how to do just that starting from scratch (empty folder)

Please note that for this tutorial I am NOT going to follow the git model I described above. My model is going to look like this:

master
   |
   | \
   |  \
   |   \
   |    \
   |  develop
   |    |
   |    | <- commit 1
   |    | <- commit 2
   |    | <- commit 3
   |    |
   |    /
   |   /
   |  /
   | /
   |/ <- A single commit without history that contains all 3 commits from develop branch
   |

First thing's first, we need to create our git repository:

git init ~/my_git_repo
 
# Set up some config
cd ~/my_git_repo
git config --local user.name "Some User"
git config --local user.email "some_user@some_domain.com"

Now that we have an initialized git repo, we can start making commits. First, let's make an initial commit

# create a readme file
echo "My Repo" > README.md
 
# tell git to track this file and commit it
git add README.md
git commit README.md -m "initial commit"

Let's verify that our commit went through

$ git log
commit 20be9f36070498080d43aa07bfea5b4a499f61ea
Author: Some User <<a href="mailto:some_user@some_domain.com">some_user@some_domain.com</a>>
Date:   Mon Dec 22 07:13:06 2014 -0800
 
    initial commit

Now it's time to create our branch!

git checkout -b my_branch

That wasn't too bad!

It's time to pretend to do some work and commit some files to our newly created branch

# create 3 files and commit them to our repo
for i in `seq 3`; do touch f$i; git add f$i; git commit f$i -m "Commit #$i"; done

Great! Let's make sure that your files got commited:

$ git log
commit ed62b41b7bc9a0cb3bec620b89ed2bc8c9dfe684
Author: Some User <some_user@some_domain.com>
Date:   Mon Dec 22 07:21:02 2014 -0800
 
    Commit #3
 
commit 48f5b0e922e968e878e275106dedf65938956899
Author: Some User <some_user@some_domain.com>
Date:   Mon Dec 22 07:21:02 2014 -0800
 
    Commit #2
 
commit 6b9a43e1423c370831e1eb8304a9734117d1fc8a
Author: Some User <some_user@some_domain.com>
Date:   Mon Dec 22 07:21:02 2014 -0800
 
    Commit #1
 
commit c10b95633b99f3520ae24b43c6403cce2646d1ba
Author: Some User <some_user@some_domain.com>
Date:   Mon Dec 22 07:20:45 2014 -0800
 
    initial commit

At this point, we are ready to start merging our code back to master. So, let's switch back to master and get to it!

# switch back to master branch
git checkout master
 
# get latest commit hash and copy it to clipboard
git log --pretty=format:"%x1b[31m%h%x09%x1b[32m%d%x1b[0m%x20%s"
 
# merge into master
git merge my_branch
 
# start rebase tool
# git rebase --interactive <hash from previous command>
# in my case command is going to look like:
git rebase --interactive c10b956

NOTE: The command for acquiring hash was retrieved from this SO thread
NOTE2: It is also worth metioning that if you are working with a lot of people, you perform regular pulls from the parent branch into yours to prevent difficult merge conflicts.

After last command, you should be looking at your default text editor application with a screen similar to this:

pick 6b9a43e Commit #1
pick 48f5b0e Commit #2
pick ed62b41 Commit #3
 
# Rebase c10b956..ed62b41 onto c10b956
#
# 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

Here, what you'll want to do is change the first commit (in my case 6b9a43e) to edit and the rest to squash. When you're done with those changes, save your file and exit the editor.

Now, you'll be prompted to run git commit --amend. This command will allow you to change your commit message to the first commit. So, let's run it!

git commit --amend -m "merged 3 files"

Finally, complete your rebase by running:

git rebase --continue

Fill out the commit message and exit the editor. You should see something like this:

[detached HEAD e4c7e89] Merged branch my_branch into master while squashing commits
 3 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 f1
 create mode 100644 f2
 create mode 100644 f3
Successfully rebased and updated refs/heads/master.

Let's verify one last time that everything is good. You should see only 2 commits on a master branch now

$ git log
commit e4c7e89b5cca7382e41511c760d5620b6348d723
Author: Some User <some_user@some_domain.com>
Date:   Mon Dec 22 07:21:02 2014 -0800
 
    Merged branch my_branch into master while squashing commits
 
commit c10b95633b99f3520ae24b43c6403cce2646d1ba
Author: Some User <some_user@some_domain.com>
Date:   Mon Dec 22 07:20:45 2014 -0800
 
    initial commit

Lastly, we don't want to leave any mess behind, so let's delete our merged branch. This can be done by running:

# force delete branch
git branch -D my_branch

NOTE: I am not sure why, but if I use '-d' I get an error saying that my_branch has not been fully merged (while it has been). If anyone knows the answer, I would appreciate an explanation in the comments section!

Hope you have found this useful!

Comments