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

Git:如何为合并创建补丁?,git,merge,patch,Git,Merge,Patch,当我使用git格式补丁时,它似乎不包括合并。如何执行合并,然后将其作为一组修补程序通过电子邮件发送给其他人 例如,假设我合并了两个分支,并在合并的基础上执行另一个提交: 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格式补丁时,它似乎不包括合并。如何执行合并,然后将其作为一组修补程序通过电子邮件发送给其他人

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

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”。我该怎么处理


-Geoffrey Lee

注意,裸的
git log-p
不会显示合并提交“M”的任何补丁内容,但使用
git log-p-c
确实可以解决这个问题。但是,
git格式补丁
不接受任何类似于
git日志
接受的
-c
(或
-组合
-cc
)的参数


我也很困惑。

如果你检查前两个补丁的内容,你会发现问题:

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)的角度来看,这两个提交都删除了“初始文件”行,并将其完全替换为其他内容。顺便说一句,当您生成一个具有重叠更改的非线性进程补丁时,无法避免这种冲突(在本例中,您的分支提交了B和C)


人们通常使用补丁来添加单个功能或修复已知良好的先前工作状态的bug——补丁协议根本不足以像Git那样处理合并历史。如果您希望有人看到您的合并,那么您需要在分支之间推/拉,而不是后退diff/patch。

似乎没有一个解决方案生成单独的提交,即
git格式的补丁
,但是FWIW,您可以格式化一个包含有效合并提交的补丁,该补丁适用于/兼容
git am

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

git log-p显示每次提交时引入的补丁

[…]这意味着对于任何提交,您都可以获得提交引入到项目中的补丁。您可以通过运行带有特定提交SHA的
git show[SHA]
来实现这一点,也可以运行
git log-p
,它告诉git在每次提交后放置补丁。[……]

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

git log-p-m——第一个父级

。。。显示历史记录,包括更改差异,但仅从“主分支”的角度,跳过来自合并分支的提交,并显示合并引入的更改的完整差异。这只有在遵循一个严格的策略,即在单个集成分支上合并所有主题分支时才有意义

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

# 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

但是我想你已经计算了很多,如果你在这里登陆,你实际上是在搜索我上面描述的解决方法

扩展
sun
的答案,我得到了一个命令,它可以生成一系列补丁,类似于
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。补丁
xxLAST。补丁

根据Philippe De Muyter的解决方案,我制作了一个版本,该版本的补丁格式与git格式的补丁格式相同(据我所知)。只需将范围设置为所需的提交范围(例如,原点..头部),然后执行:

请注意,如果在git quiltimport中使用此选项,则需要排除任何空的合并提交,否则将出现错误“修补程序为空。是否拆分错误?”。

对于git 2.32(2021年第2季度),“
格式修补程序”
”文档更清晰:它跳过合并

参见(2021年5月1日)作者。
(于2021年5月11日合并)

:提及合并的处理 签字人:杰夫·金

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

git格式修补程序
现在在其中包括:

在中准备每个非合并提交及其“修补程序”

git格式修补程序
现在在其中包括:

警告 请注意,
format patch
将忽略输出中的合并提交,甚至 如果它们是请求范围的一部分。
一个简单的“补丁”并不能解决这个问题 包括足够的信息,以便接收端复制相同的信息 合并提交


很难证明是否定的,但像seh一样,我对这个问题进行了尝试,我觉得你是对的。是的,我理解补丁文件的问题。我希望有一个解决办法,因为人们会假设Linux或Git项目遇到了类似的情况,它们完全依赖于通过电子邮件提交补丁,而不是推/拉。我将ping Git邮件列表,看看他们是否有其他反馈。谢谢。如果你用$git merge-squash foo$git commit-a-m“commit m”替换了上面的合并行,那么你的补丁就会被干净地应用……是的,我本来可以压缩提交,但是这会破坏历史,通常不是实现分布式版本控制的好方法,imho。幸运的是,Git邮件列表中有人向我指出了“Git bundle”,它允许您打包和传输Git对象
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