繁体   English   中英

Git:如何为合并创建补丁?

[英]Git: How to create patches for a merge?

当我使用git format-patch时,它似乎不包括合并。 如何执行合并,然后将其作为一组补丁通过电子邮件发送给某人?

例如,假设我合并两个分支并在合并之上执行另一个提交:

git init

echo "initial file" > test.txt
git add test.txt
git commit -m "Commit A"

git checkout -b foo master
echo "foo" > test.txt
git commit -a -m "Commit B"

git checkout -b bar master
echo "bar" > test.txt
git commit -a -m "Commit C"

git merge foo
echo "foobar" > test.txt
git commit -a -m "Commit M"

echo "2nd line" >> test.txt
git commit -a -m "Commit D"

这将创建以下树:

    B
  /   \
A       M - D 
  \   /
    C

现在我尝试检查初始提交并重放上述更改:

git checkout -b replay master
git format-patch --stdout master..bar | git am -3

这会产生合并冲突。 在这种情况下, git format-patch master..bar只生成 3 个补丁,省略“Commit M”。 我该如何处理?

——杰弗里·李

似乎没有一个解决方案可以生成单独的提交,例如git format-patch ,但是FWIW,你可以格式化一个包含有效合并提交的补丁,适合/兼容git am

显然, Git参考指南提供了第一个提示:

git log -p show patch在每次提交时引入

[...]这意味着对于任何提交,您都可以获得提交给项目的提交补丁。 您可以通过使用特定提交SHA运行git show [SHA]来执行此操作,也可以运行git log -p ,它告诉Git在每次提交后放置补丁。 [...]

现在, git-log的手册页给出了第二个提示:

git log -p -m --first-parent

...显示包含更改差异的历史记录,但仅限于“主分支”透视图,跳过来自合并分支的提交,并显示合并引入的完整变更差异。 只有遵循严格的策略,在停留在单个集成分支上时合并所有主题分支才有意义。

这反过来意味着具体步骤:

# Perform the merge:
git checkout master
git merge feature
... resolve conflicts or whatever ...
git commit

# Format a patch:
git log -p --reverse --pretty=email --stat -m --first-parent origin/master..HEAD > feature.patch

这可以按预期应用:

git am feature.patch

同样,这将不包含单个提交,但它会从合并提交中生成一个git am兼容的补丁。


当然,如果你首先不需要兼容git am补丁,那么它就更简单了:

git diff origin/master > feature.patch

但我想你已经想到了,如果你在这里登陆这个页面,你实际上正在寻找我上面描述的解决方法/解决方案。 ;)

请注意,裸git log -p不会显示合并提交“M”的任何补丁内容,但使用git log -p -c会将其哄骗。 然而, git format-patch不接受类似于任何参数-c (或--combined-cc被接受) git log

我也是难过的。

扩展了sun的答案,我找到了一个命令,它可以生成一系列补丁,类似于git format-patch会产生git format-patch ,并且你可以提供给git am来生成个人提交的历史记录:

git log -p --pretty=email --stat -m --first-parent --reverse origin/master..HEAD | \
csplit -b %04d.patch - '/^From [a-z0-9]\{40\} .*$/' '{*}'
rm xx0000.patch

修补程序将命名为xx0001.patchxxLAST.patch

如果您检查前两个补丁的内容,您将看到问题:

diff --git a/test.txt b/test.txt
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-initial file
+foo

diff --git a/test.txt b/test.txt
index 7c21ad4..5716ca5 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-initial file
+bar

从您当时正在处理的分支(foo和bar)的角度来看,这两个提交都删除了“初始文件”行并完全替换了其他内容。 AFAIK,当你生成一个具有重叠变化的非线性进程的补丁时,没有办法避免这种冲突(在这种情况下你的分支提交B和C)。

人们通常使用补丁来添加单个功能或修复已知良好的先前工作状态的错误 - 补丁协议根本不够复杂,无法像Git本身那样处理合并历史。 如果你想让某人看到你的合并,那么你需要在分支之间推/拉而不是退回差异/补丁。

使用Philippe De Muyter的解决方案,我制作了一个版本,以与git-format-patch相同的方式格式化补丁(据我所知)。 只需将RANGE设置为所需的提交范围(例如origin..HEAD),然后执行:

LIST=$(git log --oneline --first-parent --reverse ${RANGE});
I=0;
IFS=$'\n';
for ITEM in ${LIST}; do
    NNNN=$(printf "%04d\n" $I);
    COMMIT=$(echo "${ITEM}" | sed 's|^\([^ ]*\) \(.*\)|\1|');
    TITLE=$(echo "${ITEM}" | sed 's|^\([^ ]*\) \(.*\)|\2|' | sed 's|[ -/~]|-|g' | sed 's|--*|-|g' | sed 's|^\(.\{52\}\).*|\1|');
    FILENAME="${NNNN}-${TITLE}.patch";
    echo "${FILENAME}";
    git log -p --pretty=email --stat -m --first-parent ${COMMIT}~1..${COMMIT} > ${FILENAME};
    I=$(($I+1));
done

请注意,如果您使用git-quiltimport,那么您将需要排除任何空的合并提交,否则您将收到错误“Patch为空。它是否分裂错误?”。

使用 Git 2.32(2021 年第二季度),“ format-patch ”文档更加清晰:它跳过了合并。

请参阅Jeff King ( peff )提交 8e0601f (2021 年 5 月 1 日)。
(由Junio C Hamano -- gitster --提交 270f8bf中合并,2021 年 5 月 11 日)

docs/format-patch :提及合并处理

签字人:杰夫·金

Format-patch 没有办法以git-am (或任何其他工具)可以应用的方式格式化合并,因此它只是省略了它们。
但是,对于不熟悉该工具如何工作的用户来说,这可能是一个令人惊讶的暗示。
让我们在文档中添加一个注释,使其更加清晰。

git format-patch现在包含在其手册页中:

准备每个非合并提交及其“补丁”

git format-patch现在包含在其手册页中:

注意事项

请注意, format-patch将忽略输出中的合并提交,即使它们是请求范围的一部分。
一个简单的“补丁”不包含足够的信息让接收端重现相同的合并提交。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM