Git: How to consistently maintain local patches

We thought it’s time to share some tech and talk code! This post will explain a problem when dealing with “workaround” branches in Git.
Here is the situation: Imagine your local machine is a bit different than those of your colleagues or – as in our case – there are different prototypes of your product lying on the table. Here at SUPENTA we work with NFC technologies. Turns out the NFC tag I worked with had slightly different specs than the tag that might go in the final product. In both cases (different machines or different tags), one developer might have to change the code base slightly in order to make it work locally. Of course, these changes are only temporarily and should not affect the upstream code base.

So this is the situation: You want to maintain your local changes and still be able to fetch and push upstream patches. But you don’t want your “workaround” patches to go upstream. In this post, I will explain how you can achieve this using git cherry-pick.

The Setup

Let’s suppose you have cloned a fresh copy of the upstream repository. The commit history might look like this:

abc_init

A fresh clone of the repo

 

As you can see, we have only three commits in the master branch. Now, we’ll add the local patch (or patches) in a new branch which we do not want to push:
> git checkout -b workaround
> hack hack hack
> git add .
> git commit -m "Added Workaround"

The above commands result in the following picture:

abc_workaround_branch

Added your local workaround

 

Doing acutal work

So now that we’re settled and everything works locally, we can do some actually work. The following picture illustrates the state of our local repository after adding two patches:

abc_workaround+zy_branch

Added two patches to the workaround branch

 

 

Talking to the rest of the team

This is where it gets interesting. Until here, everything was nice and easy. Now, we face two challenges. First, we need to track upstream changes and incorporate them in our workaround branch. Secondly, we want to share the Z and Y patches with the rest of the world. First things first: Let’s get the latest changes from upstream and merge them into the local master branch. This is going to be fast-forward because we didn’t touch that branch:
> git checkout master
> git pull origin

Supposing someone else pushed D and E in the meantime, the result looks like this:

abcde_workaround+zy_branch

Fetched upstream patches

 

 

Due to the handicap on our local machine, we actually need to merge the local master in the workaround branch and not the other way around:
> git checkout workaround
> git rebase master

You could also use git merge master depending on your taste. Learn about the difference here. Make sure to resolve all conflicts and test that everything still works locally.

abcde_workaround+zy_branch_rebased

Merged/rebased upstream patches

 

We now have the latest upstream patches and all our local patches in the workaround branch. Now, let’s checkout the master branch and git cherry-pick everything we need:
> git checkout master
> git cherry-pick z
> git cherry-pick y

If you have a whole bunch of patches, you can also use git cherry-pick <fromSHA1>..<toSHA1>. In our case, we make sure to pick Z before Y to keep track of functional dependencies. This is what it looks like afterwards:

abcde-cherrypickedzy_workaround+zy_branch_rebased

Cherry-picked what should go upstream

 

Be proud of your work and tell the world about it:
> git push origin

abcde_workaround+zy_branch_rebased

Pushed Z & Y upstream

 

This is pretty much it. Remember to check out your workaround branch again. In order to keep things clear, it’s best to rebase the master branch again. Git will recognise that the commits are identical and just replay your workaround patch(es) on top of it:
> git checkout workaround
> git rebase master

abcde-cherrypickedzy-pushed-rebasedagain-done_workarond+zy_branch_rebased

 

Make sure to follow this recipe (adapted your needs, of course) step-by-step. I was tempted to automate parts of it but eventually dismissed the idea because it was too much overhead. However, to have something reproducible I use a bash script as my tutor:

#!/usr/bin/bash

echo “NOW: git checkout master”
read
echo “NOW: git pull origin/master”
read
echo “NOW: git checkout workaround”
read
echo “NOW: git rebase master”
read

This enables me to remember where I was even when the rebase/merge took some time or lunch was ready 😉
Furthermore, I used git format-patch -1 <workaround-sha1> to have a copy of the workaround patch. I put this file somewhere save to be able to reproduce my local branch if anything went wrong.

I hope this helps some of you out there and I will be more than happy to receive your comments on this.

Rolf,

facebooktwittergoogle_plusredditpinterestlinkedinmail

Have something to add?

Loading Facebook Comments ...

Leave a Reply

*