3

Git normally refuses pushing to the (single) currently-checked-out branch of a normal, non---bare repository:

$ git push upstream master
Counting objects: 1, done.
Writing objects: 100% (1/1), 188 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because ...

This is correct behavior. See Git push error '[remote rejected] master -> master (branch is currently checked out)'.

However, on that very same upstream, I have an added worktree with a different branch checked out. Just before the above error-demonstration, I ran:

$ git push upstream pytest

and it succeeded, when clearly it should not have, for the same reason master fails here. In particular, receive.denyCurrentBranch is indeed set to deny current branches.

On that upstream system, git worktree list says:

$ git worktree list
<path1>     9febb4c [master]
<path2>     406bef8 [pytest]

which shows that at least part of Git knows that it's checked out. But then it allowed the push anyway, which leads to two questions:

  1. Is this a bug?

  2. Now that I am in this mess, how can I recover?

(Additional note: this bug was fixed in Git 2.26, released in March 2020.)

torek
  • 448,244
  • 59
  • 642
  • 775

1 Answers1

4
  1. Yes, it is (obviously) a bug: Git's "is this a current branch" test, in the receive pack code, is using the pre-multiple-work-trees test, which is just to check the main work-tree's HEAD. It needs to be modified to check all work-trees the same way git worktree list does.

    (I'm working on two systems connected via VPN where only one of them has a server running at the moment, so I'm using an asymmetric fetch/push model here.)

  2. Fortunately, I had no uncommitted work, so recovery was trivial: in the secondary work-tree (on branch pytest), just run:

    git reset --hard HEAD
    

    which cleans up both the index and work-tree.

    If I were not, however, the easiest trick would be to force a new branch to exist at the previous value of the branch, then check out that branch. This can be done in one step:

    git checkout -b tempbranch pytest@{1}
    

    The work-tree would now be on a different (pre-push) branch, where temporary work can now be committed or stashed or rebased or whatever, and dealt with in the usual way.

torek
  • 448,244
  • 59
  • 642
  • 775