10

I'm trying to synchronize periodically a git bare repository, my local branches are created using the "--track" option. here is my config (without unnecessary things):

[core]
        bare = true
[remote "origin"]
        url = git@github.com:Ummon/D-LAN.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
[branch "website"]
        remote = origin
        merge = refs/heads/website

I must use the 'cp' command to update the local branches:

 git fetch --all
 cp -r refs/remotes/origin/* refs/heads

Is there a more elegant solution?

simont
  • 68,704
  • 18
  • 117
  • 136
gburri
  • 154
  • 1
  • 2
  • 9

1 Answers1

19

Instead of copying the ref files around (bad bad!) use git update-ref

Note that you can have what you wanted pretty easily to a bare repository when pushing instead of pulling.

Use this:

 git clone --mirror . /tmp/bareclone

to create the bare repository that you want to keep completely in synch Now, to synch all the branches to the bareclone mirror, use

 git push /tmp/bareclone --mirror

NOTE that this will also remove any heads that aren't in the source repo but (still) are in the bareclone. Also note that you can use send-pack instead of push there because this interface is rather lowlevel and actually implemented by send-pack

HTH


As the commenter noted, I slighly avoided the subject that it doesn't seem possible at once to do the inverse. However, you can get a long ways towards the goal by doing something slightly advanced like:

1. Simple, safe and pretty unexciting.

git fetch $url -- $(git ls-remote -h $url |
    while read sha ref; do echo "$ref:refs/remotes/fetched/${ref#refs/heads/}"; done)

Set `url=git://your.server/project.git' e.g.

I made sure that the refs are created in a moderately safe location. The effect very similar to doing.

git remote add fetched $url --fetch

So there is nothing much, but it shows you how to use plumbing commands to achieve it, so we can now adapt them to our needs:

2. Fetch directly into local branches (heads)

Now if you are sure you know what you're doing you can use the following style to get the branches duplicated locally:

git init --bare .
git fetch $url -- $(git ls-remote -h $url |
    while read sha ref; do echo "$ref:$ref"; done)

Note: if you want to force update (e.g. when there is no fast-forward pull because of an upstream rebase, reset, filter-branch, or amended commit (in general -- any rewritten history) replace echo "$ref:$ref" by echo "+$ref:$ref"; (Thanks Dov)

3. The full monty

This could also be combined, so you can have a remote definition too. I'd recommend that because it will mean that the local branches are actually tracking the ones from the remotes, and you'll have better insulation/recovery options if you accidentally clobber some of the local commits on these branches with these powerful fetch commands

git init --bare .
git remote add fetched $url --fetch
git for-each-ref --format='%(refname:short)' -- 'refs/remotes/fetched/' |
    while read ref; do git branch -t "$(basename "$ref")" "$ref"; done

Have fun, HTH

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    Thx, I do that for each branch: `git update-ref refs/heads/master refs/remotes/origin/master` – gburri Apr 06 '11 at 09:17
  • Is there a way to do the same thing as the `git push --mirror` but initiating from the receiving repository. If the source repository is hosted elsewhere such as github, you can't use git push. – Mark May 13 '11 at 20:09
  • @Mark: I had evaded that issue in the original answer. I have updated the answer with hints and tricks that you may like :) – sehe May 13 '11 at 21:21
  • Thanks. I ended up just doing `git fetch` then doing `git update-ref` to move from refs/remotes/remotename/$ref to refs/heads/$ref. Not exactly the safest thing to do, but in my case it's a read only mirror and did the job. Your solutions look much better though, and I may end up changing it to the second method you used. – Mark May 19 '11 at 21:48
  • Yeah - actually, you'd need to take your `update-ref` approach any subsequent update when doing the 3rd approach (the full monty), so it is valuable info, I sort of forgot about updating things :) – sehe May 19 '11 at 21:53
  • Thanks a lot for this! I was looking for a way of updating a clone. Just one comment about your example 2. above, I added a + to the reference so that it reads `do echo "+$ref:$ref"`. – Dov Grobgeld Jul 05 '11 at 08:17
  • Shouldn't a `git clone --mirror` with `git fetch --all` be sufficient to create a bare r/o repo which tracks all remote branches? – Michael Renner Sep 13 '12 at 07:45
  • Just wanted to note that the last solution should be `git for-each-ref`. (I tried to edit it myself but "Edits must be at least 6 characters" and I was only adding 4 :p). This worked great for me. Thanks very much – DanielM Jan 13 '14 at 15:13
  • 1
    Rather than using `basename`, I'd go for parameter expansion and use `git branch -t ${ref#fetched/} $ref` so that you can still have branches names `foo/bar` both on `fetch` and locally. – Vser Nov 28 '17 at 13:38