Beau Simensen
Beau Simensen

Reputation: 4578

How do I get a remote tracking branch to stay up to date with remote origin in a bare Git repository?

I am trying to maintain a bare copy of a Git repository and having some issues keeping the remote tracking branches up to date. I create the remote tracking branches like this:

git branch -t 0.1 origin/0.1

This seems to do what I need to do for that point in time. However, if I make changes to origin and then fetch with the bare repo, things start to fall apart. My workflow looks like this:

git fetch origin

It looks like all of the commits come in at that point, but my local copy of 0.1 is not being updated. I can see that the changes have been brought into the repository by doing the following:

git diff 0.1 refs/remotes/origin/0.1

What do I need to do to get my tracking branch updated with the remote's updates? I feel like I must be missing a step or a flag somewhere.

Updated: Additional Clarification

Normally one would push into a bare repository, rather than run git fetch from within it. If you can arrange to do that, life will be much easier.

Here is a bit of clarification on the workflow.

The project's public git repository is hosted on GitHub. I am managing the project (wiki, issues, forums) using Redmine. Redmine requires a local bare repository in order to operate. When GitHub receives changes it pings Redmine. Redmine then attempts to fetch changes from its origin (GitHub).

This works great if I'm just working with master but it was not working with my tracking branches. The changes were being imported but were not being listed in the branch in Redmine repository browser because the local branches were not being updated.

I'm sure I could have worked this out another way but finding a (generic) solution to getting the tracking branches up and running was definitely my preference as most of the git related plugins for Redmine assume things like "git fetch origin" is all that needs to be done.

See accepted answer for the complete solution. The --mirror solution seems to be exactly what is needed in this case.

Upvotes: 9

Views: 7097

Answers (2)

Mark Longair
Mark Longair

Reputation: 467023

Normally one would push into a bare repository, rather than run git fetch from within it. If you can arrange to do that, life will be much easier.

If you have to fetch rather than push, and you just want to create a mirror of origin in your bare repository, you could avoid having remote-tracking branches entirely. If you're starting this mirror from scratch, Charles Bailey points out below that git will do all of this setup for you if you initially clone the repository with git clone --mirror.

Otherwise, you could get a similar effect with:

git fetch origin +refs/heads/*:refs/heads/*

The + means that this will overwrite the state of your local branches with those from the remote. Without the +, updates that aren't fast-forward merges will be rejected. However, if this is just a mirror, that shouldn't matter. (You can configure this to be the default action on git fetch origin by setting the config variable remote.origin.fetch to +refs/heads/*:refs/heads/*. If you want to mirror tags and remote-tracking branches from origin as well, you could use +refs/*:refs/* instead.)

However, if, as you originally ask, you want to maintain remote-tracking branches and selectively merge them into local branches, you can use the following steps, but I don't necessarily recommend them unless you're the only person using this bare repository. (Note: originally here I suggested using "git symbolic-ref HEAD refs/heads/whatever" to switch branch and "git reset --soft" to change the branch ref - however, Charles Bailey pointed out in the comments out that one can use "git update-ref refs/heads/whatever refs/remotes/origin/whatever", which doesn't require one to switch branch first.)

First, you might want to check that updating the branch would have been a fast-forward merge, and after that update the branch. Note that there is a race condition here, which is why I say you should only do this if you are the only person using your local bare repository - the check would be useless if someone else moves on the branch between those steps.

  1. You can check that an equivalent merge would be a fast-forward (i.e. that the history of origin/master includes master) by comparing git merge-base master origin/master with git show-ref master - they should be the same. Or you could use the is-ancestor script in this question, which does those commands with some additional checks.)
  2. Finally, you can update your current branch (master in this case) to point to origin/master with git update-ref refs/heads/master refs/remotes/origin/master

However, as I said at the beginning, I imagine that the real solution you want is either:

  • Pushing into your bare repository instead or
  • Mirroring the remote bare repository exactly, as I described above

I hope that's of some use.

Upvotes: 18

Francois G
Francois G

Reputation: 11985

When you fetch, remote changes are updating your own local reference for the remote, origin/0.1 (this never causes any conflicts, since origin/0.1 never hosts any local changes). To import those changes, you can git reset refs/remotes/origin/0.1 from 0.1, which will make the HEAD branch (0.1) point to the same commit as the remote branch.

Note that by doing so you will lose any changes to 0.1 not coming from the remote.

Upvotes: 1

Related Questions