Evgeny Pokhilko's Weblog

Programmer's den

GIT TF: Undo shallow pull and pull squashed changeset


One of our projects is under TFVC (TFS Version Control) and some developers including myself use GIT TF to work with git. The project is large and we have many commits a day so git tf pull –deep works longer than I can tolerate. Therefore we have to use the default “–shallow” pull.

When working this way, sooner or later you will face the problem this post is trying to solve. Let’s say there are three new changesets in TFVC that have been checked in since you pulled last time A, B and C. You call git tf pull –shallow and they are squashed and stored as one commit in your git repository. Suddenly you need to work with changeset B. For example, a new bug assigned to you can only be reproduced in B and you need to investigate it to make sure it will not resurface again.


1. Brute force solution

A brute force solution is to clone a new git-tf repository with git tf clone –version=C<B changeset id> … other parameters. In our project it takes hours. It’s huge amount of downtime for such a small problem. The advantage of this solution is that you can have all your not checked in changes in your old repository and come back to it later without having to check them in or rebase them. In my situation the time is critical and this solution is not preferred.

2. Undo the squashed pull and pull the changeset again

Disclaimer: This option is for advanced git users only. It is dangerous and at your own risk. I don’t take any responsibility for broken repositories or anything going wrong.

Before you start you need to check-in, shelve or rebase your changes that haven’t been checked in. When I say rebase, I mean the following steps:

  • Commit and create a new branch for the commits.
  • Rebase the branch to the commit right before changeset A

You need to understand that we are going to discard everything in your git tree starting from the commit with A, B and C.

When everything is ready, we can start following the steps below. In our repository 682377c is sha2 of the commit we pulled before A, B and C. It is the commit we want to become latest in our repository after we undo the A, B and C pull. The steps follow:

  1. Move your master ref to 682377c.

    git checkout master
    git reset –hard 682377c

  2. Delete all other refs to commits after 682377c. Use git branch -D <branch-name> or git tag -D <tag-name> <other tag names>. So eventually nothing should reference the history after 682377c. We are doing this to avoid duplicate commits after we pull again.
  3. Now it’s time to reset the ref in our remote branch.

    git update-ref refs/remotes/origin_tfs/tfs 682377c

  4. If you open gitk now, everything looks exactly as before you pulled A,B and C changesets. However, there is still a problem with git-tf. If you pull changeset B now, you will see this error message assuming B changeset id is 99185:

    git tf pull –version=C99185
    Connecting to TFS…
    Fetching and merging changes in $/ProjectX/Trunk at changeset 99185: 0%
    git-tf: a later version has already been downloaded or version C99185 either does not exist for the mapped path

    The problem is that git tf still “thinks” that it has pulled C99185. We can fix it by editing .git\git-tf file. Before we edit the file content looks as below. In our example changeset C corresponds to commit id 3cc0f65 and its changeset id is 101068. The changeset with id 97798 is the changeset we pulled before pulling A,B and C.

    [git-tf “commits”]
    changeset-97798 = 682377c3c0723413173cd8a3192f862d1e671010
    changeset-101068 = 3cc0f65df9a5d3dd9405004b3418a22f5c440a62
    [git-tf “changesets”]
    commit-682377c3c0723413173cd8a3192f862d1e671010 = 97798
    hwm = 101068
    commit-3cc0f65df9a5d3dd9405004b3418a22f5c440a62 = 101068

    Now we simply delete all mapping information about changesets after 97798 and set hwm to 97798. See the file after we have made the change.

    [git-tf “commits”]
    changeset-97798 = 682377c3c0723413173cd8a3192f862d1e671010
    [git-tf “changesets”]
    commit-682377c3c0723413173cd8a3192f862d1e671010 = 97798
    hwm = 97798

  5. Finally we can invoke the same pull command again.

    git tf pull –version=C99185
    Connecting to TFS…
    Fetching and merging changes in $/ProjectX/Trunk at changeset 99185: 100%, done.

    All files were fetched to commit af0f591 and merged into master successfully.

  6. Done !!!

November 2, 2014 - Posted by | Source Control | , ,

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: