Repo 及其克隆不同意 HEAD 指针

Repo and its clone disagree about HEAD pointer

我正在尝试将我的主分支重命名为 'liveBranch',创建一个新分支 ('devBranch'),然后将存储库(称为存储库 A)克隆到同一台计算机上的另一个文件夹中(称之为回购 B)。但是当我这样做时,如果我在回购 B 上执行 git branch -a,它显示回购 A 的 HEAD 指向 'devBranch',而回购 A 上的 git branch -a 声称 'liveBranch' 已签出。

以下是我的具体步骤(注意:repoA 是一个非空目录):

cd path/to/repoA
git init
git add .
git commit
git branch -m master liveBranch
git branch devBranch
git clone path/to/repoA path/to/repoB 
cd path/to/repoB

运行 git branch -a 回购 B returns:

* devBranch
  remotes/origin/HEAD -> origin/devBranch
  remotes/origin/devBranch
  remotes/origin/liveBranch

虽然 运行 git branch -a 在回购协议 A returns 中:

  devBranch
* liveBranch

我认为这可能是因为这两个分支实际上都指向同一个提交,所以从技术上讲,这两个 repo 都没有错。所以我在 repo A 中的一个分支上提交了一个 commit 来推进分支并在 repo B 中做了一个 git pull,但是断开连接仍在发生(repo B 和 repo A 不同意 repo A 签出的分支).

您的 "exact steps" 有点问题,因为如果我开始尝试重现问题:

cd path/to/repoA
git init
git add .
git commit

我的系统上有这个:

$ cd /tmp; mkdir btest; cd btest
$ mkdir repoA; cd repoA
$ git init
Initialized empty Git repository in /tmp/btest/repoA/.git/
$ git add .
$ git commit
On branch master

Initial commit

nothing to commit
$ 

似乎您可能正在一个已经存在并且有一些提交的存储库中执行 git init,否则此时 master 仍然是一个未生成的分支。不管怎样,现在我稍微改变一下你的步骤:

$ echo 'dummy repo for testing' > README
$ git add .
$ git commit -m initial
[master (root-commit) 82f36fb] initial
 1 file changed, 1 insertion(+)
 create mode 100644 README
$ git branch -m master liveBranch
$ git branch devBranch
$ git branch
  devBranch
* liveBranch
$ 

现在让我们尝试将其克隆到 /tmp/btest/repoB:

$ git clone /tmp/btest/repoA /tmp/btest/repoB
Cloning into '/tmp/btest/repoB'...
done.
$ cd ../repoB
$ git status
On branch liveBranch
Your branch is up-to-date with 'origin/liveBranch'.
nothing to commit, working directory clean
$ 

它正在按您希望的方式工作。

让我们采用不同的方法来重复这个问题,首先删除两个测试存储库,然后创建一个 HEAD 指向 devBranch 的新存储库,然后克隆该存储库:

$ cd /tmp/btest
$ rm -rf *
$ mkdir repoA; cd repoA; git init
Initialized empty Git repository in /tmp/btest/repoA/.git/
$ echo > README; git add README; git commit -m initial
[master (root-commit) 8278cc4] initial
 1 file changed, 1 insertion(+)
 create mode 100644 README
$ git branch -m master devBranch
$ cd ..; git clone repoA repoB; (cd repoB; git status; git branch -A)
Cloning into 'repoB'...
done.
On branch devBranch
Your branch is up-to-date with 'origin/devBranch'.
nothing to commit, working directory clean
* devBranch
  remotes/origin/HEAD -> origin/devBranch
  remotes/origin/devBranch
$ 

所以我们的 repoB 处于合适的状态。现在我们改变 repoA 以便它有 HEAD 指向 liveBranch:

$ (cd repoA; git checkout -b liveBranch; git branch)
Switched to a new branch 'liveBranch'
  devBranch
* liveBranch
$ 

如果我们在 repoB 中请求 git 到 git pull,我们现在 期望 会发生什么?好吧,让我们看看 发生了什么(注意,这是 Git 版本 2.8.1;1.8.4 之前的行为在某些情况下会有点不同):

$ cd repoB; git pull
From /tmp/btest/repoA
 * [new branch]      liveBranch -> origin/liveBranch
Already up-to-date.
$ git branch -a
* devBranch
  remotes/origin/HEAD -> origin/devBranch
  remotes/origin/devBranch
  remotes/origin/liveBranch
$ 

现在让我们尝试一些不同的东西,即 repoB 中的 运行 git fetch,以及 git ls-remote:

$ git fetch
$ git branch -a
* devBranch
  remotes/origin/HEAD -> origin/devBranch
  remotes/origin/devBranch
  remotes/origin/liveBranch
$ git ls-remote
From /tmp/btest/repoA
8278cc44d45cad50f34dc2c788cd9df7bf9375ec    HEAD
8278cc44d45cad50f34dc2c788cd9df7bf9375ec    refs/heads/devBranch
8278cc44d45cad50f34dc2c788cd9df7bf9375ec    refs/heads/liveBranch

很明显,git pullgit fetch 都没有读取新的远程 HEAD 状态,或者如果是,则退回到名称到 ID 的转换。让我们用新的提交更新 repoA 并重新获取:

$ (cd ../repoA; git commit -m update --allow-empty)
[liveBranch 2234cf1] update
$ git fetch
remote: Counting objects: 1, done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (1/1), done.
From /tmp/btest/repoA
   8278cc4..2234cf1  liveBranch -> origin/liveBranch
$ git branch -a
* devBranch
  remotes/origin/HEAD -> origin/devBranch
  remotes/origin/devBranch
  remotes/origin/liveBranch
$ git ls-remote
From /tmp/btest/repoA
2234cf14c9f7c63785e8fe31b7e5f37bcaf51823    HEAD
8278cc44d45cad50f34dc2c788cd9df7bf9375ec    refs/heads/devBranch
2234cf14c9f7c63785e8fe31b7e5f37bcaf51823    refs/heads/liveBranch
$ 

所以,是的,Git 在初始克隆后根本无法更新 remotes/origin/HEAD,至少在使用绝对路径时是这样。将 URL 更改为 file:///tmp/btest/repoA 没有区别:

$ git config remote.origin.url file:///tmp/btest/repoA
$ git fetch
$ git branch -a
* devBranch
  remotes/origin/HEAD -> origin/devBranch
  remotes/origin/devBranch
  remotes/origin/liveBranch

快速查看源代码表明,在初始 clone 步骤之后,git 永远不会更新 remotes/origin/HEAD

It seems as though perhaps you are doing your git init in a repository that already exists and has some commit(s), otherwise master would still be an unborn branch at this point.

即使 master 未出生,您仍然可以(现在,Git 2.30,Q1 2021)重命名它:

这是用户培训的一部分,让他们为将来更改 init.defaultBranch 配置变量做好准备。

commit 675704c, commit cc0f13c, commit cfaff3a, commit 1296cbe (11 Dec 2020) by Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- in commit 772bdcd 合并,2020 年 12 月 18 日)

branch -m: allow renaming a yet-unborn branch

Signed-off-by: Johannes Schindelin

In one of the next commits, we would like to give users some advice regarding the initial branch name, and how to modify it.

To that end, it would be good if git branch -m <name>(man) worked in a freshly initialized repository without any commits.
Let's make it so.

测试表明在行动中:

test_expect_success 'branch -m with the initial branch' '
    git init rename-initial &&
    git -C rename-initial branch -m renamed &&
    test renamed = $(git -C rename-initial symbolic-ref --short HEAD) &&
    git -C rename-initial branch -m renamed again &&
    test again = $(git -C rename-initial symbolic-ref --short HEAD)