我有 2 个存储库 (A) 和 (B)。(B) 是 (A) 的分支,并且已收到文件目录重命名。(B) 中的许多文件只是具有不同的父文件夹名称。现在我正在尝试将 (A) 拉取请求到 (B),但 Azure 存储库说目标文件已被删除。
有没有办法手动覆盖拉取请求,以便在 (A) 的文件映射到 (B) 的文件的地方发生从 (A) 到 (B) 的文件的映射文件?同样,它们是相同的文件,只是父文件夹不同。如果可以的话,我想避免更改 (A) 的文件夹结构。
简短的回答是"否",但问题本身有点问题。 如果你问正确的问题,答案可能会变成"是"。
首先,"拉取请求"不是 Git 的东西——它是各种 Web 服务提供的附加组件,例如 GitHub 或 Bitbucket 或(在您的情况下)Azure。 Git 真正拥有的是获取提交的能力——从其他一些 Git 存储库获取完整的提交——并合并。
当你获取别人的提交时,你得到的实际上是他们的提交。 宇宙中的每个提交都有自己独特的、唯一的哈希 ID。 哈希 ID 是提交的所有内容的加密校验和:快照中的所有文件、提交人员的姓名和电子邮件地址、他们的日志消息,以及(对 Git 至关重要)导致此时间点的所有历史记录。 也就是说,为了将此提交放入存储库,您还必须获取导致此提交的所有提交(包括其快照、作者和日志消息等)。
现在,您的仓库中已经有了他们的提交,您就有了他们的提交。 现在由您决定您希望如何处理这些提交。 您可以保持原样,也可以复制它们并在复制过程中(在提交副本之前)进行更改。 这些副本可以有您喜欢的任何差异:请记住,副本将具有与原始副本不同的哈希 ID。 只有原始提交才能使用原始哈希 ID。
如果保留原件,则保留其文件结构。 这是没有办法的。 具有唯一哈希 ID 的提交将始终被冻结。 没有人 - 不是你,不是他们,不是 Git - 可以更改该提交。 你要么拥有它,而且就是这样,要么你根本没有它。 (在将这些提交塞入存储库后,您可以通过决定不喜欢它们来实现"根本没有它"状态。 您只需停止使用它们并通过它们的哈希 ID 引用它们,最终您的 Git 就会丢弃它们。 引用和引用日志在这里有一些棘手的问题,但大多数情况下,这只是删除任何引用并等待的问题。
如果您将这些原始文件复制到具有新文件结构的新提交中,那很好。 无论您是否保留原件,您都可以保留副本。 但是,您的副本就是您的副本,它们不会与它们的未来更新很好地结合在一起,无论它们是谁。 如果你打算和这些其他人一起做持续的工作,那可能不是一个好路。
现在让我们看看第二个更有趣的部分:
有没有办法手动覆盖拉取请求,以便在 (A) 的文件映射到 (B) 的文件的地方发生从 (A) 到 (B) 的文件的映射文件?同样,它们是相同的文件,只是父文件夹不同。如果可以的话,我想避免更改 (A) 的文件夹结构。
现在我们知道 Git 中没有拉取请求这样的东西,我们可以把它变成正确的问题,即:
现在我的仓库中有他们的提交,我可以使用放宽 Git 文件匹配规则的参数将他们的提交与我的提交合并吗?
这个问题的答案是肯定的。 您可能需要使用命令行 Git 而不是花哨的 Web 界面来执行此操作 - 例如,GitHub 的点击按钮 Web 界面没有这种能力。
当 Git 执行合并时(如git merge otherbranch
中所示),此合并有三个输入。 三个输入之一是您当前的提交 - 您所在分支的尖端,或HEAD
提交:这是同一提交的两个名称,其真实名称是其丑陋的大哈希ID。 其中一个输入是您指定的另一个提交 - 在这种情况下otherbranch
,但您也可以只提供原始哈希 ID;Git 只是将名称otherbranch
转换为原始哈希 ID,以便进行合并。
这是两个输入,那么第三个输入是什么? 答案是,它是由图表暗示的。 记住上面我说过的,如果你从别人那里接受一个特定的提交,你还必须接受导致该特定提交的所有提交。 我们可以用图形方式绘制这种情况:
...--o--o--*--o--o--L <-- yourbranch (HEAD)
A--B--R <-- theirbranch (or theirrepo/branch or whatever)
在这里,L
代表您当前的(Left 或Local 或--ours
)提交,R
代表他们的(R ight 或 othe R 或acquire-from-Remote-Git 或--theirs
)提交。A
和B
代表您需要从他们那里获取的提交的哈希 ID,以便获得提交R
,*
是您已经拥有的提交A
的父级的哈希 ID。1
对于这些真正的合并情况(你的合并情况肯定是这样),git merge
的工作方式是 Git 运行两个git diff
,以弄清楚你更改了什么以及它们更改了什么。实际上,第一个差异是:
git diff --find-renames <hash-of-*> <hash-of-L>
请注意这个--find-renames
参数。 第二个差异相当于:
git diff --find-renames <hash-of-*> <hash-of-R>
如果您没有在*
和L
之间重命名文件夹,并且他们确实重命名了*
和L
之间的文件夹,Git 将在合并期间尝试匹配*
和R
中的文件,即使它们具有不同的名称。此尝试取决于文件内容的相似性。
同时,如果您在*
和L
之间重命名了一个文件夹,并且他们没有重命名该文件夹,Git 会做完全相同的事情。 它尝试将*
中的基本名称与您在L
中的名字相匹配。 此尝试取决于文件内容的相似性。
如果你们都重命名了文件夹,那也没关系。 Git 尝试在提交*
中查找原始文件,基于其内容与两个分支提示中可能相同-也许不相同的文件的每个新名称的内容的相似性。
在*
和L
中将所有重命名的文件配对后,发现*
中的文件path/to/file.ext
现在在L
中path/different/file.ext
,Git 知道您对file.ext
所做的更改是通过将*
的原始file.ext
与同一文件的L
的新名称进行比较而获得的更改。 它还知道您重命名了该文件。 类似地,在将所有从*
重命名配对到R
后,Git知道他们对file.ext
所做的更改是通过比较*
的原始file.ext
和R
的新名称获得的相同文件。
在所有情况下,一旦 Git 正确识别了重命名的文件,合并就会照常进行:Git 会尝试逐个文件合并您的更改及其更改。 它还尝试保留你们中的任何一个所做的任何重命名。
这都可能以多种方式出错:
如果你们都重命名了
file.ext
,Git 不知道要保留哪个新名称。 您将获得一个rename/rename
冲突,您必须手动解决。 这与您还必须自行解决的任何其他合并冲突是分开的。 完成解析后,如有必要,git mv
文件,为其指定要保留的名称,并在要保留的名称下git add
组合更改。
如果更改文件名的人也更改了太多内容,Git 将无法配对新旧文件。 多少才算太多? 好吧,Git 有一个相似性阈值的概念。 当 Git 执行
git diff old-commit new-commit
操作的--find-renames
部分时,Git 将针对似乎已从旧提交中删除的每个文件,将已删除文件的内容与似乎在新提交中从头开始创建的每个文件的内容进行比较。 如果旧file.ext
与新different.ext
相似度为30%,与新other.ext
相似度为70%,则70%相似的匹配获胜。 但是,如果没有文件达到"50%匹配",则默认设置是确定该文件毕竟被删除了。如果您自己运行
git diff --find-renames
,则可以添加重命名阈值因子,该因子默认为 50%,但可调。 根据需要向上或向下调整它,以使 Git 配对正确的文件。 Git 会在其差异输出中告诉您相似性指数是什么。您可以在运行
git merge
之前手动运行此类git diff
,并找到使 Git 匹配文件的正确相似性索引。 然后,您可以运行git merge -X find-renames=number
以告知git merge
将该数字用于其两个git diff --find-rename
操作。当然,如果您必须大大降低相似性阈值,则合并操作本身很有可能在这里发生冲突,因为这表明您已经更改了文件,以至于他们所做的任何更改都会与您所做的更改发生冲突。 但这可能足以自动处理更多的合并。
因此,可以说,这里的配方是手动进行合并。 首先使用git fetch
获取建议合并的提交。 然后,使用git merge-base --all
查找git merge
将找到的共享合并基础提交。 使用此共享合并基础作为起始点提交,并将您和/或其分支提示提交哈希 ID 或分支名称作为结束点提交来运行git diff --find-renames
。 向此git diff
添加--name-status
,以获取哪些文件已配对并被发现已修改的摘要,哪些文件被视为已删除。 调整重命名查找阈值(--find-renames=number
,如果要使用短拼写,则-Mnumber
),直到获得最佳结果。 然后将git merge
与-X rename-threshold=number
选项一起使用,使git merge
相同的数字传递到两个基础差异。
1无论如何,您可能已经拥有A
和B
。 使提交*
重要的原因是它是最好的共享提交:在您的分支及其分支上的所有提交中,它是其中最好的。 从技术上讲,它是构成存储库的有向无环图 (DAG) 提交中两个选定提交的最低共同祖先 (LCA) 提交。 您可以使用以下命令找到此提交的哈希 ID:
git merge-base --all HEAD otherbranch
例如。 有时根本没有公共提交,有时(很少)DAG 中有多个包含两个提交的 LCA,但通常这只会生成一个哈希 ID,这就是合并基础。