By: Lars Kruse
On: Friday, October 03. 2014
Long-lived development branches
This recipe explains how you can reuse a development branch after you have delivered and integrated it using the squashed strategy. The issue here is that the squashed commit on the integration branch is detached from your development branch, so Git doesn’t know that it’s safe to continue working on your development branch and deliver again. It will complain and say that you need to merge the integration branch in first.
This setup mimics what the Pretested Integration Plugin does when squashing integrations.
You can take the code below and run it directly into your shell.
# Setup a small git repo to represent origin mkdir lab cd lab mkdir longlivedbranch cd longlivedbranch/ echo foo > foo.txt git init git add . git commit -m "initial" cd .. # Clone it to represent a a development clone git clone longlivedbranch longlivedbranch-dev cd longlivedbranch-dev/ # branch to a development branch and make a few commits git checkout -b my-dev echo changed > foo.txt git commit -am "changed foo.txt" echo changed again > foo.txt git commit -am "changed foo.txt again" # Push your changes to a "ready" branch on origin git push origin my-dev:ready/my-dev # In origin squash content of dev branch onto master and clean up cd ../longlivedbranch git checkout master git merge --squash ready/my-dev git commit -m "Integrated ready/my-dev" git branch -D ready/my-dev cd ../longlivedbranch-dev/
At this point you have two commits on your
my-dev branch in your local clone which are delivered as just one squashed commit to the master.
The problem in a nut shell
If you continue working on
my-dev and then deliver again - Git will get mad at you. The merge in line #5 below will fail:
echo new undelivered change > foo.txt git commit -am "3rd change in foo.txt" git push origin my-dev:ready/my-dev cd ../longlivedbranch git merge --squash ready/my-dev git commit -m "Integrated ready/my-dev" git branch -D ready/my-dev
The reason is that git will squash
my-dev up to last common ancestor and that means that you are trying to deliver your two already-delivered commits again.
How to do it
Before you continue to reuse your
my-dev branch you have to sync it with the new commit on
You should simply do this by merging (while having
my-dev as the checked out branch)
git pull origin master
if you do this first, you’ll see that the running the commands listed above will work just fine.
If you are a perfectionist that’s bothered by the fact that using
- Clear out your dangling
- Doesn’t sync your local
Then the following commands will also allow you to reuse the
my-dev branch and in the process updating the picture to perfect:
git fetch origin --prune git checkout master git merge origin/master git checkout my-dev git merge master
Please note, that even if you might be fond of using
rebase rather than a plain
pull it would be very wrong to do in this situation.
First of all because
rebase rewrites your history and you already made a delivery. But also simply because running the rebase would fail.
The following commands will fail:
git fetch origin git rebase origin/master
The reason is the same as before: You are using a squashed merge. It’s detached from it’s original context (the
my-devbranch), so essentially the rebase will start a 3-way merge and since you implicitly are trying to re-apply the changes already in the 2 original commits, you’ll get a merge conflict error.