如何仅针对修改特定文件的提交生成/应用git补丁

5
我有两个git仓库,它们是相互fork的,我需要偶尔从一个导入提交到另一个。例如:git-repo1具有以下目录结构:repo1/project1/srcrepo1/project2/src,而git-repo2具有以下目录结构:repo2/src/。我想要的是获取一系列提交,并仅为更改特定子目录(例如repo1/project1/src)中的文件的提交生成补丁,并忽略仅更改其他任何位置的文件的所有提交。或者,为所有提交生成补丁,但仅在补丁更改特定目录中的文件时应用补丁。我需要保留有关提交的元数据,因此使用git diff不可行。这两个forked git仓库之间的目录结构不同。有没有简单的方法来做到这一点?更新1:我看到了如何处理不同目录结构的git补丁?这个问题。但如果补丁涉及修改不存在的文件怎么办?我希望忽略这样的更改。
3个回答

12

git rev-list --reverseseries-- repo1/project1/src/ \
| xargs -I@ git format-patch --stdout @^! >mystuff.patch

这段命令将会把影响到该子目录的series提交记录输出到mystuff.patch文件中。

然后,

cat >mystuff.sed <<\EOD
/^(From [0-9a-f]{40}|diff --git )/!{H;$!d}
x
/^From /b
${h;s,.*--,--,;x}
\,^diff[^\n]* [ab]/repo1/project1/src/,!{$!d;x;b}
${p;x}
EOD

sed -Ef mystuff.sed mystuff.patch >justmystuff.patch

会剥离掉该目录之外的所有补丁。您可以使用以下命令应用:

git am justmystuff.patch

按要求,使用-pn--directory=new/path/to进行必要的更新。

(编辑:EOD --> \EOD以使上面的猫不会尝试替换)


请使用“-E”代替,“-r”现在是历史拼写。已在答案中进行修正,谢谢。 - jthill
2
如果您能解释一下您的sed脚本,那就太好了! - Neil
使用您喜欢的文本编辑器进行搜索和替换也可以(至少对我来说有效)。我只是更改了路径并删除了涉及子目录外文件的部分。我没有触及提交哈希 - 我不知道上面的自动脚本是否会这样做,因为我像@Neil一样。 - user948581
如何教mystuff.sed魔法,以便在补丁提交消息中添加类似于“从哈希导入”的内容?也许需要在git amgit format-patch中都添加-k,以包括可能的[blah]在提交消息中。如果文件从repo1/project2/src移动到repo1/project1/src,添加--no-renames似乎可以使git am正常工作。 - vvassilev
https://stackoverflow.com/users/7767933/komar,sed irc 频道上的一个酷哥建议:```# 收集 hold 缓冲区中块的补丁头和主体 /^(From [0-9a-f]{40}|diff --git )/!{

检测头部结束并插入行“llvm-monorepo: HASH”从“From HASH”

/^---$/{ x s/(From )([0-9a-f]{40})(.*)/\1\2\3\n\nllvm-monorepo: \2/ x } H;$!d } x

输出头部中的“From”行

/^From /b

如果补丁在 '[ab]/lvm/' 中 - 输出它

,^diff[^\n]* [ab]/llvm/,!{$!d;x;b}

其他块将被跳过```

修复了提交日志中粘贴 diff 的情况。
- vvassilev
显示剩余2条评论

1
如果两个仓库有相同的历史记录(它们都是从同一个仓库派生出来的,但发展方向不同),您可以使用cherry-picking从一个分支有选择地导入提交到另一个分支。
创建一个具有两个远程仓库(您的两个分叉仓库)的本地仓库。
查找在repositoryA中涉及特定文件的提交。
 $ git checkout repoA/master
 $ git log sub/dir/ectory
 a34256f ...

将这些提交(cherry-pick)到repositoryB的分支中。
 git checkout repoB/master
 git cherry-pick a34256f

1
不好意思,它们没有共同的历史。 :( - EMiller

0

我尝试了这里和其他答案中找到的几种不同的方法,但没有找到适合我的解决方案。这是我发现的。

您可以将路径作为参数传递给git format-patch,以便仅为这些路径创建补丁。

git format-patch HEAD --root $(git rev-list --max-parents=0 HEAD) --stdout -- repo1/project1/src/ > git.patch

命令git rev-list --max-parents=0 HEAD用于从存储库中获取初始提交引用。

应用补丁可以使用以下方式完成:

git am git.patch

在我的情况下,我想要在应用补丁时跳过一个目录级别,因为我将一个整个目录从一个仓库移动到另一个仓库。 git applygit am 使用,它有一个标志 -p<n> 可以从差异路径中删除前导路径组件。默认值是 1,所以 git am -p2 git.patch 对我很有帮助。

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