【问题标题】:Create Git patches for two files across several renames跨多个重命名为两个文件创建 Git 补丁
【发布时间】:2017-04-27 11:25:23
【问题描述】:

我想将两个文件从一个存储库移动到另一个存储库。这些文件最初添加为:

  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

我尝试通过this description 为这些文件创建一组补丁,但我不确定如何传递文件所在的六个不同路径。

我已设法找到所有影响 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
  • 另外,您不必在命令中运行git log --all ...。一个简单的git format-patch -o /tmp/pricevaluepatches dfeeb 6966b 9f882 … 就足够了。
  • @NilsWerner,感谢您的建议。不过,这只解决了一半的问题。从我拥有的 ID 识别的提交中,我如何在重命名和移动中仅应用影响 PriceValue.csPriceValueTests.cs 的更改?
  • 正如 cmets 在下面所说的格式补丁不能保留所有历史记录。如果有人感兴趣,有一种更简单的方法可以保存历史并将文件移动到另一个 repo:将这个 repo 合并到另一个 repo。这意味着所有的历史都在那里,无需处理单个提交。
  • @JanZerebecki,这会不会弄得一团糟,包括对所有 other 文件所做的所有更改?还是您的意思是在将源存储库与目标合并之前对源存储库执行 --filter-branch

标签: git patch file-rename git-patch file-move


【解决方案1】:

你可以获取一些特定文件的补丁但不能早于提交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?
  • 不,只是您想要的第一个。如果您想要这些文件的所有提交,只需输入 origin 作为 sha1。
  • @AsbjørnUlsberg:format-patch 将生成一系列补丁,这将允许重新创建一个线性历史。它不允许您通过合并“移植”分支的历史——一个单独的补丁不包含有关其父级的信息。
  • @AsbjørnUlsberg:所以format-patch 的输入是针对“描述单个线性历史”:git format-patch [hash] 将采用从[hash] 到您当前提交的提交序列,@987654332 @ 将采用从ab 的提交序列。它也会尝试解释其他输入,但结果可能与您的预期不符。
  • origin 是上游的最后一次提交,而不是第一次,所以如果你想要第一次,你需要用例如查找它git log --oneline |tail -1.
【解决方案2】:

嗯.....假设我想保持补丁文件的原样,我要做的是将补丁文件应用到一个分支上,这样我就可以在正确的分支上挑选它。

所以,假设我的主分支上有一个名为 /tests/moved/PriceValueTests.cs 的文件,我想在其上应用一个名为 /tests/init/PriceTests.cs 的补丁。假设我想要破解补丁文件,我会做的是:

  • 从我的主人创建一个临时分支
  • 结帐临时分支
  • 将文件重命名为与补丁文件相同的路径(当然还有提交)
  • 在临时分支上应用补丁文件(现在应该可以工作,因为文件路径有匹配的文件)
  • 提交临时分支
  • 结帐大师
  • 从临时分支中挑选最后一个修订版

这是为了让 git 可以跟踪名称更改并能够成功应用它。我已经做过很多次了,git 的文件重命名算法往往是正确的。

【讨论】:

  • 这回答了其他问题,但没有回答这个问题。该问题没有需要修补的文件,只有从一个存储库移动到另一个存储库的文件。
【解决方案3】:

为了通过合并实现问题的目标(而不是按照问题要求通过格式补丁移动单个补丁),可以删除新提交中的所有其他文件,然后将该提交跨存储库合并到目标存储库中(改编自https://stackoverflow.com/a/10548919/7496656):

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 不会保持不变,因此很难找到跨存储库的相同提交。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2014-11-25
    • 2013-02-06
    相关资源
    最近更新 更多