不小心创建了一个大小写不同的重复分支(Master),如何将它移回master?

Accidentally created a duplicate branch with different capitalization (Master), how to move it backto master?

在将数据从子分支移回 master 分支时,我不小心创建了一个 Master 分支(大写 M)。
Windows 上的工具集似乎没有注意到大小写的差异,在我的本地回购中我只有两个分支:

 Directory of C:\Users\Me\source\repos\Me\MyProject\.git\logs\refs\remotes\origin

[.]                 [..]                64ThreadsPerBlock   HEAD                master

但是在远程我有三个分支:

64threadsPerBlock
Master
master

如何解决问题,使远程没有 Master 并且与本地 master 同步?

如果你在 Linux 系统上,这很容易,因为 Git always case-sensitive: readmeREADME是两个不同的文件masterMaster是两个不同的分支。

如果您使用的是典型的 Windows 或 macOS 文件系统,使用 case-insensitive 本机文件名,1 它得到凌乱:Git认为masterMaster两个不同的名字但是有时 使用 这些名称作为文件系统中的普通文件。然后它希望这两个不同的名称保持不同,但 OS 坚持不这样做。由于 Git 仅在 有时 这样做,但有时它会按照 Git 认为应该的方式工作。

您可以利用两个事实:

  1. other 系统显然将这两个名称视为不同。
  2. Git 实际上并不关心 什么 分支名称是什么——它是如何拼写的等等——它只关心存储的 哈希 ID 每个名字。

因此,解决问题的第 1 步是 运行:

git ls-remote origin

这将吐出几行或多行输出,例如:

e2850a27a95c6f5b141dd88398b1702d2e524a81    HEAD
898f80736c75878acc02dc55672317fcc0e0a5a6    refs/heads/maint
e2850a27a95c6f5b141dd88398b1702d2e524a81    refs/heads/master
5d2a92d10f831493d4b0b258ee3cd18d4a7ae995    refs/heads/next
4141ae219919d2e29f3939983be8501770f247a3    refs/heads/seen
1637c5d31a0b0df8e3dcef7072720fe0381520ac    refs/heads/todo
d5aef6e4d58cfe1549adef5b436f3ace984e8c86    refs/tags/gitgui-0.10.0
3d654be48f65545c4d3e35f5d3bbed5489820930    refs/tags/gitgui-0.10.0^{}
[... tons of tags snipped here]

每一列左侧的内容是原始哈希 ID。 这个原始哈希 ID 是 Git 关心的。 右边的东西是 其他 Git 存储库存储的名称原始哈希 IDrefs/heads/* 名称是它们的分支名称。

您可能希望将所有这些输出保存在一个文件中。另请注意,我们指望其他 Git 存储库在我们工作时不会对其进行任何 更改 。如果它是一个高度活跃的存储库,你最好启动一个 Linux 系统(或 Linux VM)并在其中做普通的 Git 事情。

(旁注:git ls-remote -h origin 将输出修剪为 只是 分支名称,如果有很多标签,这会非常方便。)


1从技术上讲,case-sensitivity 是 每个文件系统 。在 macOS 上,您可以轻松设置 case-sensitive 磁盘映像,挂载它,在那里克隆,然后在那里修复所有内容,甚至不必费心使用 VM。也可能有 case-sensitive 文件系统可用于 Windows 系统,但我不知道,也不知道是否有任何东西像 macOS “.dmg” 技巧一样相对简单.


完成第 1 步后的操作

如果他们的 masterMaster 都存储了 相同的 原始哈希 ID,您现在可以让他们 删除 两个名字之一:

git push --delete origin Master

(假设你要去掉的是第一个大写的M)。你现在已经完成了。

如果他们的 masterMaster 存储 不同的 原始哈希 ID,您现在必须弄清楚如何解决这个问题。您 将要删除这两个名称之一。如果他们的 master 领先于 他们的 Master,您可以执行与他们相等时相同的操作:只需删除他们的 Master。但是如果他们的 master 落后于 他们的 Master,或者两者都在彼此的前面和后面,你不能完全这样做,至少不安全。

理解这一点需要理解“前面”和“后面”的实际含义 。 Git 中的提交哈希 ID 是 有向无环图 DAG 的一部分。我们需要简要介绍一下图论和偏序集。

偏序集合

大多数人都熟悉 总阶,我们得到的是整数:1 在 2 之前,2 在 3 之前。毕竟任何大于 3 的数字都会出现其中

然而,在图表中,我们可以有一条简单的线:

A--B--C--D--...

分叉:

     C   <-- last
    /
A--B
    \
     D   <-- also-last

在这里,A在B之前,B在C和D之前,但我们不能说C在D之前或之后。从某种意义上说,它们都在同一个位置,就在B.

Git 次提交也是如此。如果每个大写字母代表一些原始提交哈希,并且 lastalso-last 实际上是 masterMaster,那么我们有一个情况 删除 两个 names 之一将使 Git 无法找到两个“最后”提交。 Git 实际上是通过具有标识每个“最后一次提交”的名称并从那里向后工作来工作的,因此 Git 需要 两个名称 find 都在此处提交 CD

将偏序应用到 Git

在 Git-practical 术语中,这意味着如果您可以确定 master 确实“在”Master 之后——如果他们有:

...--G--H   <-- Master
         \
          I   <-- master

然后你就可以安全地删除Master了。他们现在将拥有:

...--G--H--I   <-- master

而且他们仍然能够找到他们所有的提交。

但如果那安全,您应该让他们为稍后提交创建一个新名称。也就是说,如果他们有这个:

...--G--H   <-- master
         \
          I   <-- Master

或者这个:

       H   <-- master
      /
...--G
      \
       I   <-- Master

然后你应该做的是在他们的 Git 存储库中创建一个 新名称 来标识 相同 提交作为他们的 Master。为此,您可以 运行:

git push origin <hash>:<new-name>

例如,如果他们 master 的哈希 ID 是 deadbeef,并且您选择名称 save-for-now 作为新的分支名称,您将 运行 :

git push origin deadbeef:save-for-now

这将在他们的 Git 存储库中创建这种情况:

       H   <-- master
      /
...--G
      \
       I   <-- Master, save-for-now

运行现在安全了:

git push origin --delete Master

要求他们删除他们的名字Master:

       H   <-- master
      /
...--G
      \
       I   <-- save-for-now

请注意,如果您处于“安全”情况下,例如当两个名称标识相同的最后一次提交时,您仍然可以执行以下安全操作:

git push origin <hash>:save-for-now

创建:

...--G--H   <-- Master, master, save-for-now

然后删除 Master 会得到:

...--G--H   <-- master, save-for-now

(当然都在 他们的 Git 存储库中)。

既然你已经解决了问题,只需 运行 git fetch --prune origin

您现在可以拥有您的 Git re-synchronize 您的存储库和他们的 Git 存储库。您 可能 必须 运行 这个 git fetch --prune origin 两次,因为您的本地 Git 可能会在更新 [=29] 后删除名称 Master =],并且您的 Git 将该名称存储在文件中,您的 Git 可能会删除这两个名称,但这是安全的,因为 second git fetch 将恢复它。 (通常 Git 似乎是按 ASCII 顺序执行这些操作,这样一次提取就可以正常工作,但我不知道这是否对过去和将来的所有 Git 版本都有保证。)

如果之前所有的提交都是安全的,这样你就可以安全地删除它们 Master,你现在有一个正常的设置,可以正常解决所有问题。如果他们之前安全,你现在在你自己的[=250=中有一个origin/safe-for-nowremote-tracking名字 ] 存储库。这个名字和你的origin/master名字不冲突,所以正常解决一切都没有问题。

无论如何,换句话说,现在可以安全地回去工作了。