Friday, 9 January 2015

Git-TFS Pull Failure - error running command: git rebase --preserve-merges

I’ve been using git-tfs in anger now for the past 6 months and there was an occasional problem that cropped up that had me scratching my head a couple of times. Somehow I managed to fix it the first time by fumbling around; the second time it happened I decided it was time to fix it “scientifically” and then write it up for when it happens again...

TFS Gated Builds

I’m working in a team that has separate TFS projects, some of which I can push commits to directly with git-tfs rcheckin, and others that go through a TFS gated build. The latter requires use of git-tfs checkintool which pops up the standard TFS check-in dialog, but only after squashing any un-pushed commits in the Git repo into a TFS shelveset. It’s this mismatch between the Git master branch, which has separate commits, and the single squashed commit that comes back down on the TFS remote master branch, that causes the error.

As an aside, if I’m working on a repo that I can’t use git-tfs rcheckin with to keep my Git history intact then I manually push after every Git commit to avoid the squashing behaviour of git-tfs checkintool. As such the only times I’ve seen this problem is when I’ve forgotten to push a commit to TFS and am now pushing two commits (squashed as one) without realising it. I normally do all my code reviewing when I commit to my Git repo, I only take a cursory glance at the changes I’m pushing to TFS because I know the changes themselves are what I want; it’s just the impedance mismatch between Git and TFS causing the extra friction.

Git-TFS Pull Failure

After git-tfs checkintool has run the use of a TFS gated build causes the check-in to appear to abort. However in the background the TFS shelveset is passed onto the gated build where it’s built and then merged if successful. Once the gated build completes we can re-sync the local Git repo with the remote TFS repo using git-tfs pull. But sometimes it fails like this:

> git tfs pull --rebase
Working with tfs remote: default
Fetching from TFS remote 'default'...
C12345 = c3b3f9a1339366b918b2bb5c508590bbdcce4c60
rebase in progress; onto c3b3f9a
You are currently rebasing branch 'master' on 'c3b3f9a'.
nothing to commit, working directory clean
The previous cherry-pick is now empty, possibly due to conflict resolution.
If you wish to commit it anyway, use:
    git commit --allow-empty
Otherwise, please use 'git reset'
error running command: git rebase --preserve-merges refs/remotes/tfs/default
Command exited with error code: 1

Squashing the Git Master

If you compare the Git master and TFS remote master branches you’ll probably notice that the remote branch has a squashed version of your last few commits on master. Consequently what we need to do is squash the commits on master to make the local and remote branches look the same before we do the pull. First we need to abort the current rebase so that we put master back to where it was before we attempted the pull:

> git rebase --abort

Once this is done we can squash the commits on master to match the TFS remote. I happen to use TortoiseGit for this because it’s really easy and I’ve already got it open to compare the tips of the two branches (local and remote masters).

Pull Again

With the master and remote now back in sync it’s a simple matter of executing the git-tfs pull again at which point the rebase should now succeed and master will now match the TFS remote again.

> git tfs pull --rebase
Working with tfs remote: default
Fetching from TFS remote 'default'...
Rebasing (1/1)

Note: There shouldn’t be anything to rebase (unless you’ve added more commits since) so the only observable difference will be that the newly squashed commit will have the extra git-tfs-id property as per norm to marry the Git and TFS revision changeset numbers.

No comments:

Post a Comment