Search code examples
gitgit-pullgit-extensionsgit-remote

Bad git pull creates invalid remote tracking branch


In my workplace, we just moved to Git for revision control after more than 10 years of using Source Safe (yay!). I now have to support our engineers in their learning of the new tool. It is important to note that we are all using the GitExtensions UI under Windows.

A couple of colleagues did the following (from the UI):

  • clone the central repository (origin)
  • create a new local branch (A) based on the main develop branch
  • create commits on the A branch
  • while having that A branch still checked out, they (mistakenly) did a pull of the main develop branch into their A branch

Here is what the UI of GitExtensions looks like when we do a pull (I sadly cannot post screen grabs from work):

Pull from: origin

Local branch: A

Remote branch: develop

Merge option: Merge remote branch into current branch

(Pull button is then pressed)

Here is what the command log of GitExtensions reveals it did for the pull:

git pull --progress "origin" +refs/heads/develop:refs/remotes/origin/A

After that command, the users saw a new origin/A remote tracking branch appear in their history, while we confirmed no branch A is present on origin.

I looked at the git pull manual to try and understand what happened, but I still cannot understand if what we are seeing is a GitExtensions bug or an misunderstanding on our part of what git pull should do.

Anyone can help me figure out what is happening here?

Thanks in advance

Edit

I know that pulling the remote develop branch into the local A branch does not make sense and I know how to resolve the situation by deleting the remote tracking branch. What I really want is to understand the logic behind the origin/A remote tracking branch being created in the first place, since no A branch was ever present on origin.

Thanks


Solution

  • It's hard to say whether this is a GitExtensions bug or not. In corporate environments, it's very unusual (almost always a mistake) to pull in anything other than the identically named remote branch, so I'm not surprised you got odd results. (I'd say it's a mistake to ever use git pull due to its horrific design, but that's another story.)

    The +refs/heads/develop:refs/remotes/origin/A part is called a refspec (reference specification). It says to make the local destination reference named refs/remotes/origin/A (abbreviated as origin/A) point to the same commit that the remote repository's refs/heads/develop reference (the develop branch in the remote repository) is currently pointing to. The plus (+) at the beginning says to force the ref to match even if the destination ref can't be fast-forwarded to the source ref.

    Thus, the pull command you mention causes Git to:

    1. figure out what commit the remote repository's develop branch is pointing to
    2. download that commit and its ancestor commits from the remote repository (except for the stuff already in the local repository)
    3. force the local refs/remotes/origin/A reference to point to that commit
    4. merge that commit into whatever is currently checked out

    Based on the UI mock-up you provided, this is not what I would have expected to happen. I would have expected GitExtensions to run the following instead:

    git pull --progress origin refs/heads/develop
    

    By omitting the local ref name (the part after the colon), Git does the following:

    1. figure out what commit the remote repository's develop branch is pointing to
    2. download that commit and its ancestor commits from the remote repository (except for the stuff already in the local repository)
    3. set the local FETCH_HEAD temporary reference to point to that commit
    4. merge that commit into whatever is currently checked out

    Notice the difference in step #3.

    I'm not familiar with GitExtensions, but here's what I think GitExtensions is doing: It is assuming that you want the given remote branch to be designated as the official upstream branch of the given local branch. With that assumption, creating origin/A as a remote-tracking branch for the remote's develop branch makes a moderate amount of sense. I'd be curious to know if GitExtensions also modifies the remote.origin.fetch or branch.A.merge configuration settings in .git/config to really make the designation official. If so, it's poor user interface design. If not, then it's probably a bug.