On my latest project, my team was having problems committing large changesets to the server, leaving the local branch in inconsistent states, causing a bit of frustration and delays. Having .Net developers working with open source solutions can always generate some heat, and I wanted to try to keep it smooth for them.
It turns out that in some instances, you could end up with dirty branches because of incomplete commits, leaving multiple heads on the server.
Here’s why this can happen and how to manage similar cases.
The Timeout Problem
The first problem we detected, was that sometimes the server would close the connection while doing a commit. I’m still not sure why this happens, since I would expect the server to support large changeset commits. It might be because of slow network connections or large media files. The normal workflow for a commit would be something like this:
hg add .
hg commit –m “commit comment”
You should always make sure that your local branch is up to date to avoid conflicts before doing a push. So before executing the sequence above, you should update:
Ok. So the problem is that when you are committing the large changeset, the server could timeout, leaving your branch in an inconsistent state. If you try to push again, you might get an error like the following:
pushing to https://*************
searching for changes
abort: push creates new remote heads on branch ‘default’!
(did you forget to merge? use push –f to force)
So you try to update:
abort: outstanding uncommitted changes
And now it starts to get messy. You can try to pull again and merge, and you will get the same problem. You try pushing again, and is the same. So, you end up trying to force it.
The Dirty Head Consequence
Here’s how you force the push in Mercurial:
hg push –f
Now, this will actually create a new head on the server, which is not what you intended. But even worse, you will get the same timeout error, leaving the new head dirty, and your local branch in an inconsistent state.
Now you need to solve two problems: finish the initial commit problem, and then get rid of the dirty branch.
Solving the Push First
In our case, we noticed that the problem was the server timeout during the commit. This was do to large media files being used in our project. So what we did was to do the commit in parts.
First we rolled back to the previous revision after creating a backup of the latest changes in another folder. To know which revision you need to update two, just list the head lists:
That will give you the list of heads:
summary: Dirty Head
summary: Previous clean head
If you compare this to your Kiln commit history you should be able to identify the correct head you need to rollback to. Then you execute the commands to rollback to that revision:
hg update –r 132
Now you can start committing the large changeset in batches, following the same push workflow.
This worked and solved the first problem. We were able to finish the commit with all the required files. Which will move the head forward. Let’s say, 154.
Beheading the Dirty Head
Now, you need to close the dirty branch we pushed before the last successful commit. Basically you need to close the branch, so is no longer listed in the open heads. You need to first update your local branch to the dirty head:
hg update –r 145
hg commit –close-branch –m “reason to close dirty branch”
hg update –C default
Done. You have closed the dirty branch, and updated your local branch to the latest clean changeset. You can now take a break and have a coffee before your next commit!
Hope it helps. Please feel free to share your experiences, or to point out any corrections if you think this should be solved differently.