我可以覆盖拉取请求的目标文件吗?
Can I Override the Target Files of a Pull Request?
我有 2 个存储库 (A) 和 (B)。 (B) 是 (A) 的一个 Fork,并收到了文件目录重命名。 (B) 中的许多文件只是具有不同的父文件夹名称。现在我正在尝试将 (A) 的拉取请求放入 (B),但 Azure Repos 说目标文件已被删除。
有没有一种方法可以手动覆盖拉取请求,以便在 (A) 的文件映射到 (B) 的文件的地方可以发生从 (A) 到 (B) 的映射文件?同样,它们是相同的文件,只是具有不同的父文件夹。如果可以的话,我想避免更改 (A) 的文件夹结构。
简答是"no",但是问题本身有点问题。如果你问 正确的 问题,答案可能会变成 "yes".
首先,"pull request" 不是 Git 的东西——它是由各种网络服务提供的 add-on,例如 GitHub 或 Bitbucket 或(在您的情况下) )蔚蓝。 Git 真正拥有的是 fetch 提交的能力——从其他 Git 存储库中完整地获取提交——以及 merge.
当你获取其他人的提交时,你得到的实际上是 他们的 提交。 Universe 中的每个提交都有其独特的、唯一的哈希 ID。哈希 ID 是提交中所有内容的加密校验和:快照中的所有文件、 进行 提交的人员的姓名和电子邮件地址、他们的日志消息,以及—对于 Git 至关重要 — 所有 历史 导致了这个时间点。也就是说,为了将 this 提交到您的存储库中,您还必须获取所有提交——包括它们的快照、它们的作者和日志消息等等——导致 到这个提交。
现在您的存储库中有他们的提交,您有 他们的 提交。现在由您来决定您希望对这些提交做什么。您可以按原样保留它们,也可以制作它们的 副本 并在复制过程中(在提交副本之前)进行更改。这些副本可以有您喜欢的任何差异:请记住,副本将具有与原始副本 不同的 哈希 ID。只有原始提交才能使用原始哈希 ID。
如果保留原件,则保留其文件结构。没有办法解决这个问题。具有唯一哈希 ID 的提交将被永久冻结。没有人——不是你,不是他们,不是 Git——可以 更改 该提交。你要么拥有它,就是那样,要么你根本就没有。 (您可以通过决定在将这些提交塞入您的存储库之后不喜欢它们来实现 "don't have it at all" 状态。您只需停止使用它们并通过它们的哈希 ID 引用它们,最终您Git 丢弃它们。这里的引用和 reflogs 有一些技巧,但主要是删除所有引用并等待。)
如果您将这些原件复制到具有新文件结构的新提交中,那很好。无论是否保留原件,您都可以保留副本。但是,您的副本就是那个——你的——它们不会与他们将来的更新很好地结合,无论他们是谁。如果您打算与这些其他人一起持续工作,那可能不是一条好路子。
现在让我们看第二个,更有趣的部分:
Is there a way to manually override the pull request so that mapping files from (A) to (B) can occur where (A)'s files map to (B)'s files? Again they're the same files just with a different parent folder. I'd like to avoid changing (A)'s folder structure if I can help it.
现在我们知道 Git 中没有 pull request 这样的东西,我们可以把它变成正确的问题,即:
Now that I have their commits in my repository, can I merge their commits with my commits using parameters that loosen Git's matching rule for files?
这个问题的答案是是。您可能需要使用 command-line Git,而不是花哨的 Web 界面来执行此操作——例如,GitHub 的可点击按钮 Web 界面没有此功能。
当Git 执行合并时(如git merge otherbranch
),此合并有三个 个输入。三个输入之一是您当前的提交——您所在分支的尖端,或 HEAD
提交:这是同一提交的两个名称,其真实名称是其丑陋的大哈希 ID。其中一个输入是您指定的另一个提交——在本例中为 otherbranch
,但您也可以只提供原始哈希 ID; Git 只是将 name otherbranch
转换为 原始哈希 ID,以进行合并。
那是两个输入,那么第三个是什么?答案是由图暗示。请记住我在上面说过的,如果您从其他人那里获得一个特定的提交,您还必须获得 导致 该特定提交的所有提交。我们可以将这种情况图形化地画出来:
...--o--o--*--o--o--L <-- yourbranch (HEAD)
\
A--B--R <-- theirbranch (or theirrepo/branch or whatever)
这里L
代表你当前的(Left or Local or --ours
) commit, R
代表他们的 (Rright 或 otheR 或 obtained-from-Remote-Git 或 --theirs
) 提交。 A
和 B
代表您需要从他们那里获得的提交的哈希 ID,以便获得提交 R
,而 *
是哈希 ID你已经拥有的他们提交 A
的父项。1
git merge
的工作方式,对于这些真正的合并案例——你的肯定是这样的——是 Git 运行s two git diff
s,弄清楚你改变了什么和他们改变了什么。实际上,第一个差异是:
git diff --find-renames <hash-of-*> <hash-of-L>
注意这个 --find-renames
论点。第二个差异相当于
git diff --find-renames <hash-of-*> <hash-of-R>
如果您没有重命名文件夹,介于 *
和 L
之间,他们 确实 重命名了文件夹,介于 *
和 L
之间L
、Git 将在合并期间尝试匹配 *
和 R
中的文件,即使它们具有不同的名称。 此尝试取决于文件内容的相似性。
同时,如果 您 在 *
和 L
之间重命名了一个文件夹,而他们 没有重命名该文件夹,Git 做完全相同的事情。它会尝试将 *
中的基本名称与 L
中的名称相匹配。此尝试取决于文件内容的相似性。
如果你们都重命名了文件夹,那也没关系。 Git 尝试在提交 *
中找到原始文件,基于其 content 与每个文件的 contents 的相似性两个分支提示中 maybe-same-maybe-not 文件的新名称。
paired-up *
和 L
中的所有重命名文件,发现 *
中的文件 path/to/file.ext
现在是 path/different/file.ext
L
,Git知道你对file.ext
所做的修改是通过比较*
原来的file.ext
到 L
同一文件的新名称。它还知道您重命名了该文件。同样,paired-up 所有从 *
到 R
的重命名,Git 知道 更改 他们对 file.ext
是通过比较 *
的原始 file.ext
和 R
对同一文件的新名称获得的。
在所有情况下,一旦 Git 正确识别重命名的文件,合并将照常进行:Git 尝试合并您的更改和他们的更改,file-by-file。它还会尝试保留你们所做的任何重命名。
这可能会在几个方面出错:
如果你两个重命名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 会在其 diff 输出中告诉您相似性指数是多少。
您可以 运行 这种 git diff
手动 在 您 运行 git merge
之前,找到合适的相似性指数这使得 Git 匹配文件。然后你可以 运行 git merge -X find-renames=<em>number</em>
告诉 git merge
将该数字用于其两个 git diff --find-rename
操作。
当然,如果你必须将相似度阈值降低很多,合并操作本身很有可能会在这里发生冲突,因为这表明你对文件的更改太多以至于任何更改他们 make will clash 可能会与您所做的更改发生冲突。但这可能足以自动处理 更多 的合并。
所以,这里的秘诀就是手动进行合并。首先使用 git fetch
获取您要合并的提交。然后,使用 git merge-base --all
找到 git merge
将找到的共享 merge-base 提交。 运行 git diff --find-renames
使用此共享 merge-base 作为起点提交,并使用您的 and/or 他们的分支提示提交哈希 ID 或分支名称作为终点提交。将 --name-status
添加到此 git diff
以获得关于哪些文件已配对并被发现已修改,哪些文件被视为已删除的摘要。调整rename-finding阈值(--find-renames=<em>number</em>
,或-M<em>number</em>
如果你想使用短拼写)直到你得到最好的结果。然后使用 git merge
和 -X rename-threshold=<em>number</em>
选项使 git merge
传递相同的数字通过两个潜在的差异。
1是pos反正你已经有 A
和 B
了。提交 *
之所以重要,是因为它是 最佳共享提交: 在 您的分支和他们的所有提交中分支,这是其中最好的。从技术上讲,它是构成存储库的提交的有向无环图 (DAG) 中两个选定提交的最低公共祖先 (LCA) 提交。您可以使用以下命令找到此提交的哈希 ID:
git merge-base --all HEAD otherbranch
比如说。有时根本没有共同的提交,有时——很少——DAG 中的两个提交有一个以上的 LCA,但通常这只会产生一个哈希 ID,这就是合并基础。
我有 2 个存储库 (A) 和 (B)。 (B) 是 (A) 的一个 Fork,并收到了文件目录重命名。 (B) 中的许多文件只是具有不同的父文件夹名称。现在我正在尝试将 (A) 的拉取请求放入 (B),但 Azure Repos 说目标文件已被删除。
有没有一种方法可以手动覆盖拉取请求,以便在 (A) 的文件映射到 (B) 的文件的地方可以发生从 (A) 到 (B) 的映射文件?同样,它们是相同的文件,只是具有不同的父文件夹。如果可以的话,我想避免更改 (A) 的文件夹结构。
简答是"no",但是问题本身有点问题。如果你问 正确的 问题,答案可能会变成 "yes".
首先,"pull request" 不是 Git 的东西——它是由各种网络服务提供的 add-on,例如 GitHub 或 Bitbucket 或(在您的情况下) )蔚蓝。 Git 真正拥有的是 fetch 提交的能力——从其他 Git 存储库中完整地获取提交——以及 merge.
当你获取其他人的提交时,你得到的实际上是 他们的 提交。 Universe 中的每个提交都有其独特的、唯一的哈希 ID。哈希 ID 是提交中所有内容的加密校验和:快照中的所有文件、 进行 提交的人员的姓名和电子邮件地址、他们的日志消息,以及—对于 Git 至关重要 — 所有 历史 导致了这个时间点。也就是说,为了将 this 提交到您的存储库中,您还必须获取所有提交——包括它们的快照、它们的作者和日志消息等等——导致 到这个提交。
现在您的存储库中有他们的提交,您有 他们的 提交。现在由您来决定您希望对这些提交做什么。您可以按原样保留它们,也可以制作它们的 副本 并在复制过程中(在提交副本之前)进行更改。这些副本可以有您喜欢的任何差异:请记住,副本将具有与原始副本 不同的 哈希 ID。只有原始提交才能使用原始哈希 ID。
如果保留原件,则保留其文件结构。没有办法解决这个问题。具有唯一哈希 ID 的提交将被永久冻结。没有人——不是你,不是他们,不是 Git——可以 更改 该提交。你要么拥有它,就是那样,要么你根本就没有。 (您可以通过决定在将这些提交塞入您的存储库之后不喜欢它们来实现 "don't have it at all" 状态。您只需停止使用它们并通过它们的哈希 ID 引用它们,最终您Git 丢弃它们。这里的引用和 reflogs 有一些技巧,但主要是删除所有引用并等待。)
如果您将这些原件复制到具有新文件结构的新提交中,那很好。无论是否保留原件,您都可以保留副本。但是,您的副本就是那个——你的——它们不会与他们将来的更新很好地结合,无论他们是谁。如果您打算与这些其他人一起持续工作,那可能不是一条好路子。
现在让我们看第二个,更有趣的部分:
Is there a way to manually override the pull request so that mapping files from (A) to (B) can occur where (A)'s files map to (B)'s files? Again they're the same files just with a different parent folder. I'd like to avoid changing (A)'s folder structure if I can help it.
现在我们知道 Git 中没有 pull request 这样的东西,我们可以把它变成正确的问题,即:
Now that I have their commits in my repository, can I merge their commits with my commits using parameters that loosen Git's matching rule for files?
这个问题的答案是是。您可能需要使用 command-line Git,而不是花哨的 Web 界面来执行此操作——例如,GitHub 的可点击按钮 Web 界面没有此功能。
当Git 执行合并时(如git merge otherbranch
),此合并有三个 个输入。三个输入之一是您当前的提交——您所在分支的尖端,或 HEAD
提交:这是同一提交的两个名称,其真实名称是其丑陋的大哈希 ID。其中一个输入是您指定的另一个提交——在本例中为 otherbranch
,但您也可以只提供原始哈希 ID; Git 只是将 name otherbranch
转换为 原始哈希 ID,以进行合并。
那是两个输入,那么第三个是什么?答案是由图暗示。请记住我在上面说过的,如果您从其他人那里获得一个特定的提交,您还必须获得 导致 该特定提交的所有提交。我们可以将这种情况图形化地画出来:
...--o--o--*--o--o--L <-- yourbranch (HEAD)
\
A--B--R <-- theirbranch (or theirrepo/branch or whatever)
这里L
代表你当前的(Left or Local or --ours
) commit, R
代表他们的 (Rright 或 otheR 或 obtained-from-Remote-Git 或 --theirs
) 提交。 A
和 B
代表您需要从他们那里获得的提交的哈希 ID,以便获得提交 R
,而 *
是哈希 ID你已经拥有的他们提交 A
的父项。1
git merge
的工作方式,对于这些真正的合并案例——你的肯定是这样的——是 Git 运行s two git diff
s,弄清楚你改变了什么和他们改变了什么。实际上,第一个差异是:
git diff --find-renames <hash-of-*> <hash-of-L>
注意这个 --find-renames
论点。第二个差异相当于
git diff --find-renames <hash-of-*> <hash-of-R>
如果您没有重命名文件夹,介于 *
和 L
之间,他们 确实 重命名了文件夹,介于 *
和 L
之间L
、Git 将在合并期间尝试匹配 *
和 R
中的文件,即使它们具有不同的名称。 此尝试取决于文件内容的相似性。
同时,如果 您 在 *
和 L
之间重命名了一个文件夹,而他们 没有重命名该文件夹,Git 做完全相同的事情。它会尝试将 *
中的基本名称与 L
中的名称相匹配。此尝试取决于文件内容的相似性。
如果你们都重命名了文件夹,那也没关系。 Git 尝试在提交 *
中找到原始文件,基于其 content 与每个文件的 contents 的相似性两个分支提示中 maybe-same-maybe-not 文件的新名称。
paired-up *
和 L
中的所有重命名文件,发现 *
中的文件 path/to/file.ext
现在是 path/different/file.ext
L
,Git知道你对file.ext
所做的修改是通过比较*
原来的file.ext
到 L
同一文件的新名称。它还知道您重命名了该文件。同样,paired-up 所有从 *
到 R
的重命名,Git 知道 更改 他们对 file.ext
是通过比较 *
的原始 file.ext
和 R
对同一文件的新名称获得的。
在所有情况下,一旦 Git 正确识别重命名的文件,合并将照常进行:Git 尝试合并您的更改和他们的更改,file-by-file。它还会尝试保留你们所做的任何重命名。
这可能会在几个方面出错:
如果你两个重命名
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 会在其 diff 输出中告诉您相似性指数是多少。您可以 运行 这种
git diff
手动 在 您 运行git merge
之前,找到合适的相似性指数这使得 Git 匹配文件。然后你可以 运行git merge -X find-renames=<em>number</em>
告诉git merge
将该数字用于其两个git diff --find-rename
操作。当然,如果你必须将相似度阈值降低很多,合并操作本身很有可能会在这里发生冲突,因为这表明你对文件的更改太多以至于任何更改他们 make will clash 可能会与您所做的更改发生冲突。但这可能足以自动处理 更多 的合并。
所以,这里的秘诀就是手动进行合并。首先使用 git fetch
获取您要合并的提交。然后,使用 git merge-base --all
找到 git merge
将找到的共享 merge-base 提交。 运行 git diff --find-renames
使用此共享 merge-base 作为起点提交,并使用您的 and/or 他们的分支提示提交哈希 ID 或分支名称作为终点提交。将 --name-status
添加到此 git diff
以获得关于哪些文件已配对并被发现已修改,哪些文件被视为已删除的摘要。调整rename-finding阈值(--find-renames=<em>number</em>
,或-M<em>number</em>
如果你想使用短拼写)直到你得到最好的结果。然后使用 git merge
和 -X rename-threshold=<em>number</em>
选项使 git merge
传递相同的数字通过两个潜在的差异。
1是pos反正你已经有 A
和 B
了。提交 *
之所以重要,是因为它是 最佳共享提交: 在 您的分支和他们的所有提交中分支,这是其中最好的。从技术上讲,它是构成存储库的提交的有向无环图 (DAG) 中两个选定提交的最低公共祖先 (LCA) 提交。您可以使用以下命令找到此提交的哈希 ID:
git merge-base --all HEAD otherbranch
比如说。有时根本没有共同的提交,有时——很少——DAG 中的两个提交有一个以上的 LCA,但通常这只会产生一个哈希 ID,这就是合并基础。