61

What is the best way to create a local backup of a git repository hosted on GitHub, given the following requirements?:

  1. The local backup should be a bare repo.

  2. The backup should include all branches.

  3. It should be easy to (incrementally) update the backup.

Basically, I want a perfect mirror, with the possibility to update easily. As such, the command

git clone --mirror git://github.com/...

comes to mind, but as far as I can tell, that doesn't allow for an easy update (I'd have to delete and recreate my local backup). Also, the mirror option for git clone seems quite recent, I don't have it on some of the systems I'm working on (which have slightly older versions of git running).

What is your recommended solution for this kind of problem?

Michael Goerz
  • 4,300
  • 3
  • 23
  • 31

4 Answers4

50

I am not sure it could cover all your requirements, but you could check out git bundle

git bundle

This command provides support for git fetch and git pull to operate by packaging objects and references in an archive at the originating machine, then importing those into another repository using git fetch and git pull after moving the archive by some means

What I like about that solution is the single file produced, with exactly what I want in it

git bundle will only package references that are shown by git-show-ref: this includes heads, tags, and remote heads.

machineA$ git bundle create file.bundle master

Note: Kent Fredric mentions in the comments a subtlety from git rev-list:

--all

Pretend as if all the refs in $GIT_DIR/refs/ are listed on the command line as <commit>.

He adds:

your current bundle will only bundle parents of the commit, you'd probably need to specify --all to get a complete bundle of everything (branches that are descendant of master).

To see the difference:

$ git bundle create /tmp/foo master
$ git bundle create /tmp/foo-all --all
$ git bundle list-heads /tmp/foo
$ git bundle list-heads /tmp/foo-all
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 4
    nb: your current bundle will only bundle parents of the commit, you'd probably need to specify --all to get a complete bundle of *everything* ( branches that are descendant of master ). git bundle create /tmp/foo master ; git bundle create /tmp/foo-all --all ; git bundle list-heads /tmp/foo ; git bundle list-heads /tmp/foo-all . Small, but significant. – Kent Fredric Aug 09 '09 at 17:01
  • 2
    I tried all of these but never got branches to save in the bundle. – haysclark Sep 12 '12 at 05:47
  • @Infinite would http://stackoverflow.com/a/3639182/6309 works better? (at least the `hostA$ git bundle create hostA.bundle --branches --tags` part at the beginning?) – VonC Sep 12 '12 at 05:51
  • 3
    See also: [restoring git repository from bundle backup](http://stackoverflow.com/q/9807367/562769) – Martin Thoma Oct 29 '14 at 11:47
49

To create the mirror:

git clone --mirror git://github.com/user/project.git

To update:

cd project.git
git remote update

To update without changing the current directory:

git --git-dir project.git remote update
roskakori
  • 3,139
  • 1
  • 30
  • 29
Fernando Correia
  • 21,803
  • 13
  • 83
  • 116
  • 5
    This procedure, is the correct way to get a complete local copy of a remote repo; which can then be used with `git bundle create --all` to export the entire repo as a single file. – Lee Jan 18 '13 at 16:45
  • @Lee I'm getting `error: rev-list died` when I try to execute `git bundle create --all ../project.bundle` from inside of `project.git/`. – Jonathon Reinhart Feb 04 '15 at 18:12
  • 1
    @Lee Ah, nevermind. The `--all` is an argument to `git-rev-list`. So the correct order in the command would be `git bundle create ../project.bundle --all` – Jonathon Reinhart Feb 04 '15 at 18:15
10

but as far as I can tell, that doesn't allow for an easy update (I'd have to delete and recreate my local backup).

Not sure what you mean by that, updating it should be as simple as

git fetch

git clone as it is is supposed to fetch all refs/commits that are visible on the remote branch.

git clone --mirror is also not very different to git clone --bare [source]

the only relevant difference is the shorthanded git remote add --mirror

( See git help add for the different behaviour )

If you're really worried, you can do this:

git clone --no-hardlinks --mirror $original $dest 

Which will only do anything different if they were on the same filesystem anyway.

And if you're really paranoid, you can tar.(gz|bz2) the whole directory and back that up.

Kent Fredric
  • 56,416
  • 14
  • 107
  • 150
0

What are you are asking is quite difficult to do within the constraints of git. The problem is that neither cloning nor fetching will give you all the branches by default. See this question:

For an example of cloning a repo with multiple branches, here is a transcript:

% git clone -o tufts linux.cs.tufts.edu:/r/ghc/git/experimental.git
Initialized empty Git repository in /usr/local/nr/git/ghc/experimental/.git/
% cd experimental/
% git fetch
% git branch -a
* head
  tufts/HEAD
  tufts/experimental
  tufts/head
  tufts/norman
% git branch --track experimental tufts/experimental
Branch experimental set up to track remote branch refs/remotes/tufts/experimental.
% git branch --track norman tufts/norman
   ...

You can see that cloning each branch programmatically is going to be a little tricky.

If github provides access to rsync or Unison these are better tools for the job. Otherwise, you'll have to write some scary scripts...

Community
  • 1
  • 1
Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
  • Note that it's easy to automate the tracking. Suppose you want to track all branches from origin. In bash: `git branch -r | grep "^ *origin[^ ]*$" | while read remote_branch; do branch=${remote_branch#*/}; git branch --track $branch $remote_branch; done` – Cascabel Aug 09 '09 at 21:43
  • @Jefromi : thats essentially what git remote add --mirror does :) – Kent Fredric Aug 09 '09 at 23:50