1

I have a central repo with, say, three branches, branchA, branchB, and branchC. I make some commits in, say, branchB. Then I need to copy the commits to another platform that I can't reach over a network. So I make a bundle. The last bundle I made was at, say, commit 123456f. So I create my bundle by:

> git bundle create myrepo.bundle 123456f..HEAD

Now, remembering that this was done on branchB, at my other platform, if I have checked out branchB, I try to incorporate the changes in the bundle with

> git fetch myrepo.bundle HEAD:branchB

but I am told

fatal: Refusing to fetch into current branch refs/heads/replace_pipeline_codes of non-bare repository

I can fix this by checking out out branchA (or C) instead, and running the same fetch command from there. But this is awkward and immediately raises the question of what do I do if I have a repo with only one branch?

So what do I do in that case, and is there a way to convince git to fetch a bundle into an active branch?

By the way, this is similar to this question, but in that question there is no mention of bundling, so I don't see how the answers there apply here.

bob.sacamento
  • 6,283
  • 10
  • 56
  • 115

2 Answers2

2

The reason git fetch refuses to update your current branch is that git fetch is an operation that updates remote references only, not your local checked out copy.

You could insist and fetch over the current branch by adding the --update-head-ok switch, but the result is not good: the checked out files are not changed, only the HEAD, and git status reports changes reverting all those new commit. That's probably not what you want. (But a git reset --hard will fix things at this point, as @jthill pointed out in the comments, as long as you didn't have any unsaved work in your sandbox.)

Recommended method 1

As you said, checkout another branch before you do the fetch, and run

git checkout some-other-branch
git fetch myrepo.bundle HEAD:branchB
git checkout branchB

This way, an update is not related to the current state of your sandbox, so things work well.

If your repo has no other branches, you can just checkout any commit in detached-head mode instead of some-other-branch (e.g. from @jthill in the comments: git checkout @^0 checks out the current commit).

Recommended method 2

Now, maybe the operation you really wanted was:

git pull myrepo.bundle HEAD:branchB

When you do this while on branchB, that operation will work in a reasonable way, updating both your branch and the files in your checked out sandbox, and giving you this accurate warning: "warning: fetch updated the current branch head."

Method 3: roundabout but more verbose

A third method I sometimes use with bundles lets me inspect it with my preferred graphical git log tools before merging anything in. It is to add the bundle as another remote:

git remote add mybundle <path-to-bundle-file>
git fetch mybundle

Then, in the git logs, the branches in the bundle will show up at mybundle/<branch-name> and you can merge it in as if it came from a connected remote.

While more work, this method can be useful if you want to carefully analyze what the bundle provides before applying it.

joanis
  • 10,635
  • 14
  • 30
  • 40
  • So `git pull` instead of `git fetch` sounds like what I want. Thanks. I'm kind of new at this. – bob.sacamento Apr 16 '21 at 21:39
  • 1
    Applying bundles is not super intuitive. I used to have to do this all the time between disconnected networks. I wish I remembered the commands I used, it wasn't exactly this. If I find it, I'll add it here. – joanis Apr 16 '21 at 21:40
  • "Applying bundles is not super intuitive." So it's not just me, then. I feel better. :-) – bob.sacamento Apr 16 '21 at 21:43
  • 1
    No, it's really not just you! Just found my old notes, and I used three methods, the first two being those discussed here: checkout a different branch, or use `git pull`. The third, more cumbersome, was to add the bundle as a remote (`git remote add bundle `) and then fetch from it like any other remote. This is more round-about, but what I liked about it was that I could do `git log --all` (with graph and oneliner formatting) to view where the bundle fit in my local commit tree, and validate stuff before merging it in. – joanis Apr 16 '21 at 21:46
  • 1
    Forcing the fetch isn't that bad, you just have to `git reset --hard` afterwards; or `git checkout @^0; git fetch bundle HEAD:branchB;git checkout branchB` to disconnect the ref update from your checkout before the fetch. – jthill Apr 16 '21 at 21:54
  • Thanks for the feedback @jthill. I worked your comments into my answer. – joanis Apr 16 '21 at 22:00
  • Thanks much to both of you guys! – bob.sacamento Apr 16 '21 at 22:26
  • 1
    I recommend your "method 3": treat the bundle as a remote. It *is* one, after all—the point of `git bundle` is to split `git fetch` into its two component parts. One part happens *on* the remote, where the bundle itself gets built ("counting ... compressing"). Then the bundle gets downloaded ("receiving ... resolving") and is used to add new commits to your repository. When using `git fetch` to do this, the bundle creation (and removal after fetch) is automatic. – torek Apr 17 '21 at 01:46
2

At the factory default setting of merging your pulls, fast-forwarding if possible, git pull does the fetch-and-merge for you,

git pull your.bundle HEAD

or if you've got some pull configs set up you can be explicit with

git -c pull.ff=true -c pull.rebase=false pull your.bundle HEAD

either of which wind up doing the equivalent of

git fetch your.bundle HEAD
git merge FETCH_HEAD
jthill
  • 55,082
  • 5
  • 77
  • 137