Skip to content

Instantly share code, notes, and snippets.

@dwurf
Last active March 8, 2018 12:26
Show Gist options
  • Save dwurf/5354094 to your computer and use it in GitHub Desktop.
Save dwurf/5354094 to your computer and use it in GitHub Desktop.
A short article on git branching for new git users.

Branching with git for safe merging

Version Control is a critical tool in any software development project. Despite this, many of us suffer from a crippling fear of one terrifying event: The Dreaded Merge Conflict. Here I'll show you a risk-free way of merging with an easy way to back out if things go belly up.

The scenario

You've been working for hours on a bug fix. Your changes were due an hour ago and you've just finished your unit tests. It's time to upload your changes...

c:\dev>git status
# On branch master
# Changes not staged for commit:
#       modified:   cool_program.py
#
jno changes added to commit (use "git add" and/or "git commit -a")

c:\dev>git commit -a -m "Issue #4247: computer randomly restarts"
[4ec8279] Issue #4247: computer randomly restarts
 1 file changed, 1 insertion(+)

c:\dev>git pull
   cc87f05..7fd0f6b  master     -> origin/master
Auto-merging cool_program.py
CONFLICT (content): Merge conflict in cool_program.py
Automatic merge failed; fix conflicts and then commit the result.

Oh no! What went wrong? Someone else modified the code while you were working. You run git merge and attempt to resolve the conflict, but there are dozens of conflicts and the other developer's code makes no sense. Worse, the tool partially merged your changes so your original file is toast. Your repo is in a bad state and you can't push any changes until it's fixed. It should be possible to retrieve the file but how? Your manager is coming over to check on your progress. Why does this always happen at the last minute???

Enter the Safety Merge

This approach ensures that your working copy isn't damaged by a pull, so you can quickly recover to your current state in the case of a merge conflict. The idea is simple: before you pull the latest changes you create a local branch and switch to it. In this local branch you're free to commit, merge and rebase all you like; you can switch back to your local master any time you want to get back to a known good state.

First, commit your changes as per normal:

c:\dev>git status
# On branch master
# Changes not staged for commit:
#       modified:   cool_program.py
#
no changes added to commit (use "git add" and/or "git commit -a")

c:\dev>git commit -a -m "Issue #4247: computer randomly restarts"
[4ec8279] Issue #4247: computer randomly restarts
 1 file changed, 1 insertion(+)

Now create a branch where you can attempt a merge. I called mine "sandbox":

c:\dev>git checkout -b sandbox
Switched to a new branch 'sandbox'

c:\dev>git branch -v
  master  5db7394 [ahead 1] Issue #4247: computer randomly restarts
* sandbox 5db7394 Issue #4247: computer randomly restarts

Now that we are in our sandbox we can attempt a merge:

c:\dev>git pull origin master
   cc87f05..7fd0f6b  master     -> origin/master
Auto-merging cool_program.py
CONFLICT (content): Merge conflict in cool_program.py
Automatic merge failed; fix conflicts and then commit the result.

Uh oh, things went wrong! How can we get back to where we were?

c:\dev>git checkout master
cool_program.py: needs merge
error: you need to resolve your current index first

That didn't work. Luckily we're in our sandbox and we can destroy any changes we have.

c:\dev>git reset
Unstaged changes after reset:
D       cool_program.py

c:\dev>git checkout cool_program.py

c:\dev>git checkout master
Switched to branch 'master'
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commit each, respectively

c:\dev>git branch -D sandbox
Deleted branch sandbox (was 5db7394).

c:\dev>git branch -v
* master 5db7394 [ahead 1, behind 1] Issue #4247: computer randomly restarts

You still haven't pushed your changes but at least all your code is there! It's time to go find the other developer, together you'll decide what the merged version should look like.

On the other hand, if your merge went well:

c:\dev>git checkout -b sandbox
Switched to a new branch 'sandbox'

c:\dev>git branch -v
  master  5db7394 [ahead 1] Issue #4247: computer randomly restarts
* sandbox 5db7394 Issue #4247: computer randomly restarts

c:\dev>git pull origin master
From c:\repos\project1
 * branch            master     -> FETCH_HEAD
Already up-to-date.

Success! Time to push your changes and delete your branch.

c:\dev>git checkout master
Switched to branch 'master'

c:\dev>git merge sandbox
Updating 9937449..7516d9b
Fast-forward
 cool_program.py | 1 +
 1 file changed, 1 insertion(+)

c:\dev>git push
To c:\repos\project1
   9937449..7516d9b  master -> master

c:\dev>git branch -v
* master  7516d9b Issue #4247: computer randomly restarts
  sandbox 7516d9b Issue #4247: computer randomly restarts

c:\dev>git branch -D sandbox
Deleted branch sandbox (was 7516d9b).

c:\dev>git branch -v
* master 7516d9b Issue #4247: computer randomly restarts

Notes: There are other, faster ways to back out of a merge, but this method highlights a key feature of modern version control systems: branching. Branching is a key feature of most large development teams and understanding it will help you master tools such as git and mercurial. Mercurial users: this technique is possible under mercurial as well! I will try to do a write-up of branching under mercurial, but for now you can use hg rollback to fix a merge conflict: http://stackoverflow.com/questions/3630053/how-to-undo-hg-pull

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