跨越多个重命名操作,为两个文件创建Git补丁。

8
我想把两个文件从一个代码库移到另一个代码库。原始添加的文件如下所示:
  1. /src/init/Price.cs
  2. /tests/init/PriceTests.cs
这两个文件后来被重命名为:
  1. /src/init/PriceValue.cs
  2. /tests/init/PriceValueTests.cs
然后移动到:
  1. /src/moved/PriceValue.cs
  2. /tests/moved/PriceValueTests.cs
我尝试按照这篇文章的描述创建了一组补丁文件,但我不确定如何传递这些文件的六个不同路径。
我已经找到了影响PriceValue.cs(经过重命名和移动)的所有提交ID,但将这些ID传递给Git会出现以下错误消息:
$ git format-patch -o /tmp/pricevaluepatches $(git log --all dfeeb 6966b 9f882 …)
-bash: /usr/local/bin/git: Argument list too long

那么,我该如何创建一个补丁集,仅包含对所提到的文件的更改,但跨越每个文件的一次重命名和一次移动?


你尝试过将所有提交 ID 放入一个名为 ids.txt 的文件中(每行一个),然后运行 cat ids.txt | xargs git format-patch -o /tmp/pricevaluepatches 吗? - Nils Werner
2
此外,您不必在命令中运行git log --all ...。简单的 git format-patch -o /tmp/pricevaluepatches dfeeb 6966b 9f882 … 即可满足需求。 - Nils Werner
@JanZerebecki,这样做不会变成一团糟吗?包括所有对其他文件所做的更改?或者你是指在将源存储库与目标合并之前,在源存储库上执行--filter-branch操作? - Asbjørn Ulsberg
是的,如果你不进行过滤,它将包括所有文件的历史记录。然而,在合并之前通过添加一个新的提交来删除其他所有文件,就不会对任何人造成困扰。如果你多次这样做,可能还会在另一个方向上进行,不进行过滤会使得历史记录保持更清晰,因为共同的历史只出现一次。然而,与图形视图相比,线性化视图在不相关提交数量增加时可能变得更加混乱。 - Jan Zerebecki
@JanZerebecki:虽然赏金已经授予Nils Werner,因为我太忙了无法测试和验证解决方案,你介意写出你自己的答案,列出我需要执行的步骤吗?如果你的答案有效,我会接受它作为解决方案。 - Asbjørn Ulsberg
显示剩余2条评论
3个回答

6
你可以使用以下方法获取一些特定文件的补丁,但是这些补丁的提交时间不能早于特定的 sha1 提交:
git format-patch sha1 -- file1 file2 ...

任何一个文件都可能是旧文件(重命名之前)或现有文件。
如果您想要“直到提交 sha1”的所有提交,可以使用以下命令:
git format-patch --root sha1 -- file1 file2 ...

在您的情况下,您六个文件直到现在(HEAD)的所有提交:

git format-patch --root HEAD -- /src/init/Price.cs /src/init/PriceValue.cs /src/moved/PriceValue.cs /src/init/PriceTests.cs /src/init/PriceValueTests.cs /src/moved/PriceValueTests.cs

那么我需要首先知道所有位置上影响所有文件的提交ID吗? - Asbjørn Ulsberg
不,只需要你想要的第一个。如果你想要这些文件的所有提交,只需将origin输入为sha1即可。 - Nils Werner
@AsbjørnUlsberg:format-patch将生成一系列补丁,这将允许重新创建一个线性历史记录。它不会允许您“移植”具有合并的分支的历史记录--一个单独的补丁不包含其父级的信息。 - LeGEC
@AsbjørnUlsberg:因此,“format-patch”的输入面向“描述单个线性历史”:git format-patch [hash]将获取从[hash]到您当前提交的提交序列,git format-patch a..b将获取从ab的提交序列。它也会尝试解释其他输入,但结果可能与您期望的不符。 - LeGEC
origin 是上游的最后一次提交,而不是第一次提交,所以如果你想要第一次提交,你需要使用例如 git log --oneline |tail -1 来查找它。 - Jan Zerebecki
显示剩余2条评论

1

要通过合并(而不是按照问题所问的单独移动补丁通过format-patch)实现问题的目标,可以在一个新提交中删除所有其他文件,然后将该提交跨存储库合并到目标存储库中(改编自https://dev59.com/O3M_5IYBdhLWcg3wUxdB#10548919):

cd path/to/project-a
git checkout -b for-b master # or whichever branch you want to merge from
git rm -r .
git checkout HEAD -- src/moved/PriceValue.cs tests/moved/PriceValueTests.cs
git commit
cd path/to/project-b
git remote add project-a path/to/project-a
git fetch project-a
git merge --allow-unrelated-histories project-a/for-b
git remote remove project-a
cd path/to/project-a
git branch -D for-b

这种方法的优点是所有历史记录都在那里,提交ID保持不变,无需处理单个提交或查找它们。但是,随着不相关提交数量的增加,线性视图(如git log)可能比图形视图(如gitk)更加混乱。

在合并之前,还可以过滤存储库project-a,以隐藏不相关的文件或提交。然而,这样做的缺点是:如果您在存储库合并中多次执行此操作,可能还会在另一个方向上执行此操作,那么它会使历史记录变得不太干净,因为共同的历史记录会多次出现(每次合并一次)。这也将使这种解决方案比您最初尝试的解决方案更加困难。这也会有一个缺点,即提交ID不会保持不变,因此不容易找到哪些提交在存储库之间是相同的。


1

假设我想保留补丁文件的原样,我会将补丁文件应用到一个分支上,然后在正确的分支上挑选它。

因此,假设我在我的主分支上有一个名为 /tests/moved/PriceValueTests.cs 的文件,我想在它上面应用一个名为 /tests/init/PriceTests.cs 的补丁。假设我想修改补丁文件,我会执行以下步骤:

  • 从我的主分支创建一个临时分支
  • 检出临时分支
  • 将文件重命名为补丁文件具有相同路径的名称(当然要提交)
  • 在临时分支上应用补丁文件(现在应该可以工作了,因为文件路径匹配了)
  • 在临时分支上提交
  • 检出主分支
  • 从临时分支挑选最后一个版本

这样 git 就能跟踪名称更改并成功应用它。我已经做过很多次,git 的文件重命名算法往往能够得到正确的结果。


这个回答解决了其他问题,但并没有回答这个问题。这个问题不需要修补文件,只需要将它们从一个存储库移动到另一个存储库。 - Jan Zerebecki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接