8

Is there a way to create a backup using git bundle where there is no linkage to the bundle file after cloning from the backup? I'd like to backup my repositories to a single file with all history and restore them, exactly, later.

At the moment, I'm creating a backup using the following methodology:

$ mkdir here $ cd here $ git init Initialized empty Git repository in /tmp/here/.git/ $ touch stuff $ git add stuff $ git commit -m "This is a file" [master (root-commit) c867bcf] This is a file 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 stuff $ git bundle create myrepo.bundle --all Counting objects: 3, done. Writing objects: 100% (3/3), 212 bytes | 212.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) 

Alright, so at this point we have a bundle with a simple repository. Now, when we try to copy this repository from the bundle:

$ cd .. $ mkdir there $ cd there /tmp/there $ git clone ../here/myrepo.bundle . Cloning into '.'... Receiving objects: 100% (3/3), done. /tmp/there $ git remote -v origin→ /tmp/there/../here/myrepo.bundle (fetch) origin→ /tmp/there/../here/myrepo.bundle (push) 

This shows that we have a link to the original bundle files, which I don't want. I also tried to create a mirror, but the problem remains the same:

$ cd .. $ mkdir there $ cd there $ git clone --mirror ../here/myrepo.bundle ./.git Cloning into bare repository './.git'... Receiving objects: 100% (3/3), done. $ git config --bool core.bare false $ git reset --hard HEAD HEAD is now at c867bcf This is a file /tmp/there $ git remote -v origin→ /tmp/there/../here/myrepo.bundle (fetch) origin→ /tmp/there/../here/myrepo.bundle (push) 

Although we could just copy the entire repository as is to back things up, I really only want to backup a single file that only contains the files currently under revision. I've been backing up the directories and it's been causing a mess with all of the leftover files. As such, I'd really only like to archive a single file, which is why I'm using bundle, and I'd like the recovery from the backup to be clean as in no linkages at all to the bundle file. Thanks for the help.

6
  • Why not just do git remote remove origin after cloning? Commented Jan 21, 2018 at 20:55
  • @phd That sort of works except that when there are multiple branches they are also deleted since they're linked to origin. I suppose that I could pull each of them and then delete origin, but that seems more clumsy that it should be unless I'm missing something. Commented Jan 21, 2018 at 21:06
  • The root of the problem is that you're using (misusing?) a transport mechanism (git bundle) as a backup mechanism. There's nothing inherently wrong with this, they just don't play well together, as you've discovered. You can paper over the "don't play well together" part, or use a real backup system, or whatever other solution you like. Note that the nature of a DVCS is that any one reop, once updated from another, is a superset of (if it has its own commits that the other doesn't), or is equal to, the other repo, so in some sense there's no need to make backups. But only in some sense.:-) Commented Jan 21, 2018 at 21:26
  • @torek I'll certainly admit that I may be misusing bundle. However, if that's the case, is there a better way to backup a repository where we grab the full history of the repository and only backup the files under revision to a single file? Commented Jan 21, 2018 at 21:32
  • Not really, no—git bundle amounts to running git fetch or git push without the other (second) Git around to answer have/want queries, so the git-rev-list-args argument has to provide your best guesses about what they don't have (and hence want). Then the data stream that fetch or push would construct is instead made into a bundle-file. The result is that single file you'd like. You could make an archive of the .git directory with any archiver; that's probably similar enough if you always want everything, i.e., if your rev-list arguments are just --all. Commented Jan 21, 2018 at 21:41

1 Answer 1

11

Instead of cloning from the bundle, create a new repository and fetch using a wildcard refspec. I have added a second branch feature and a tag v1.0 to the example to make this more illustrative:

$ mkdir there $ git init . $ git fetch --update-head-ok ../here/myrepo.bundle '*:*' Receiving objects: 100% (5/5), done. From ../here/myrepo.bundle * [new tag] v1.0 -> v1.0 * [new branch] master -> master * [new branch] feature -> feature $ git checkout 

The *:* refspec will fetch all remote refs into equally named local refs, creating them if they don't exist. The --update-head-ok argument is needed since git init will create a master branch which is likely also present in the remote.

Since this is a fetch with a direct URL, no remotes are needed at all. Note that this would work equally for any kind of source repo URL, not just for bundles.

The equality of the refs of the old and new repository can be checked by comparing the output of git show-ref in both repositories, they should be identical.

Sign up to request clarification or add additional context in comments.

2 Comments

I can also confirm that after using this trick and creating a new bundle of the new repository that it's identical to the original bundle. Thanks a bunch!
This answer was very helpful to me, but there's a gap I'm trying to fill: The bundle was created from a bare repository, and I'm doing this git fetch of a bundle file also on a bare repository to later clone that bare repository. However, after fetching from the bundle, the bare repository's HEAD file still shows refs/heads/master, even though the bundle contains no master and git bundle list-heads shows HEAD pointing to refs/heads/main. How do I get the HEAD file correct after this fetch? (Can't use git checkout on a bare repository.)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.