你如何使用 git format-patch 将提交压缩到一个补丁中?

Posted

技术标签:

【中文标题】你如何使用 git format-patch 将提交压缩到一个补丁中?【英文标题】:How do you squash commits into one patch with git format-patch? 【发布时间】:2010-10-11 14:54:03 【问题描述】:

我在一个分支上有 8 个提交,我想通过电子邮件发送给一些尚未了解 git 的人。到目前为止,我所做的一切要么给了我 8 个补丁文件,要么从一开始就开始给我分支历史中的每个提交的补丁文件。我使用 git rebase --interactive 来压缩提交,但现在我尝试的一切从一开始就给了我无数的补丁。我做错了什么?

git format-patch master HEAD # yields zillions of patches, even though there's 
                             # only one commit since master

【问题讨论】:

我很好奇您最终会在以下命题中使用哪种方法。让我们知道;) 我将按照 Rob Di Marco 的建议使用 git diff。但是我下班两周了,昨晚刚刚见证了我的第二个女婴的出生,所以我还需要一段时间才能使用它! :) 我很想看看 git format-patch --squash master HEAD 尝试 master..HEAD 指定转速范围。 【参考方案1】:

我建议在一次性分支上执行此操作,如下所示。如果您的提交在“换行”分支中并且您已经切换回“主”分支,那么这应该可以解决问题:

[adam@mbp2600 example (master)]$ git checkout -b tmpsquash
Switched to a new branch "tmpsquash"

[adam@mbp2600 example (tmpsquash)]$ git merge --squash newlines
Updating 4d2de39..b6768b2
Fast forward
Squash commit -- not updating HEAD
 test.txt |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

[adam@mbp2600 example (tmpsquash)]$ git commit -a -m "My squashed commits"
[tmpsquash]: created 75b0a89: "My squashed commits"
 1 files changed, 2 insertions(+), 0 deletions(-)

[adam@mbp2600 example (tmpsquash)]$ git format-patch master
0001-My-squashed-commits.patch

【讨论】:

这是我想在本地保存历史记录时使用的(以防我需要编辑补丁)。否则我只使用 rebase -i 并压缩提交。 对我来说,它与git comit -m "My squashed commits" 一起工作更可靠,否则它会添加其他未跟踪的文件 另外,您可以切换到特定提交而不是主提交,并执行此操作以创建从提交到提交的补丁:git checkout 775ec2a && git checkout -b patch && git merge --squash branchname【参考方案2】:

只是在锅中再添加一种解决方案: 如果你改用这个:

git format-patch master --stdout > my_new_patch.diff

那么它仍然是 8 个补丁......但它们都将在一个补丁文件中,并将作为一个补丁应用:

git am < my_new_patch.diff

【讨论】:

我喜欢这个解决方案。值得注意的是,它有时可能会创建比@Adam Alexander 描述的方法大得多的补丁。这是因为某些文件可能通过提交被多次编辑。即使某些文件被还原,此方法也会单独处理每个提交。但大多数时候这不是问题。 如果您想避免合并,这个选项非常棒! (例如,您想跳过一些提交)。您仍然可以git rebase -i ... 并将它们全部压缩为一个。【参考方案3】:

我总是使用 git diff 所以在你的例子中,像

git diff master > patch.txt

【讨论】:

除非您丢失所有提交消息和元数据。 git format-patch 的美妙之处在于它可以从一组补丁中重建整个分支。 create-branch-and-squash 方法的好处是可以将所有提交消息合并为一个,但是如果您想编写自己的提交消息,此方法要快得多 实际上,可以使用git log 完成合并消息,示例见my answer 不错的选择,但考虑到 'git diff' 不能包含二进制差异(也许有一个选项可以这样做)。【参考方案4】:

如您所知,git format-patch -8 HEAD 将为您提供八个补丁。

如果您希望您的 8 个提交显示为一个,并且不介意重写您的分支的历史记录 (o-o-X-A-B-C-D-E-F-G-H),您可以:

git rebase -i
// squash A, B, C, D, E ,F, G into H

或者,这将是一个更好的解决方案,在新分支上重放来自 X 的所有 8 次提交(8 次提交之前的提交)

git branch delivery X
git checkout delivery
git merge --squash master
git format-patch HEAD

这样,你在“delivery”分支上只有一个提交,它代表了你最近的 8 个提交

【讨论】:

git merge master 只是快进,并没有将提交压缩为一个。更改为 git merge --squash master 以使所有提交都被压缩并在暂存区域中可见。您的流程将适用于此更改。 (我使用的是 git 版本 2.1.0。)【参考方案5】:

这是对 Adam Alexander 答案的改编,以防您的更改在 master 分支中。执行以下操作:

从我们想要的点创建一个新的一次性分支“tmpsquash”(查找运行“git --log”或使用 gitg 的 SHA 密钥。选择您想成为 tmpsquash 头的提交,之后的提交master 将是压缩的提交)。 合并从 master 到 tmpsquash 的更改。 将压缩的更改提交到 tmpsquash。 使用压缩的提交创建补丁。 回到主分支
laura@rune:~/example (master)$ git branch tmpsquash ba3c498878054e25afc5e22e207d62eb40ff1f38
laura@rune:~/example (master)$ git checkout tmpsquash
Switched to branch 'tmpsquash'
laura@rune:~/example (tmpsquash)$ git merge --squash master
Updating ba3c498..40386b8
Fast-forward
Squash commit -- not updating HEAD

[snip, changed files]

11 files changed, 212 insertions(+), 59 deletions(-)
laura@rune:~/example  (tmpsquash)$ git commit -a -m "My squashed commits"
[test2 6127e5c] My squashed commits
11 files changed, 212 insertions(+), 59 deletions(-)
laura@rune:~/example  (tmpsquash)$ git format-patch master
0001-My-squashed-commits.patch
laura@rune:~/example  (tmpsquash)$ git checkout master
Switched to branch 'master'
laura@rune:~/example  (master)$

【讨论】:

如果您不将它们推送到远程自己,则在 master 中进行更改是一种反模式。如果愿意,您可能不需要为它们生成补丁文件。【参考方案6】:

最简单的方法是使用git diff,如果您想要squash 方法输出的组合提交消息,则添加git log。例如,在提交 abcd1234 之间创建补丁:

git diff abcd..1234 > patch.diff
git log abcd..1234 > patchmsg.txt

那么在应用补丁的时候:

git apply patch.diff
git add -A
git reset patch.diff patchmsg.txt
git commit -F patchmsg.txt

在处理非文本文件时,不要忘记 --binary 参数 git diff,例如图片或视频。

【讨论】:

【参考方案7】:

两个标签之间的格式补丁:

git checkout <source-tag>
git checkout -b <tmpsquash>
git merge --squash <target-tag>
git commit -a -m "<message>"
git format-patch <source-tag>

【讨论】:

【参考方案8】:

根据亚当亚历山大的回答:

git checkout newlines
## must be rebased to master
git checkout -b temporary
# squash the commits
git rebase -i master
git format-patch master

【讨论】:

以上是关于你如何使用 git format-patch 将提交压缩到一个补丁中?的主要内容,如果未能解决你的问题,请参考以下文章

git format-patch的使用

“git format-patch”和“git diff”有啥区别?

痞子衡嵌入式:第一本Git命令教程- 提交(commit/format-patch/am)

git merge --squash & git format-patch 将多个commit生成一个patch

利用 git format-patch 和 git send-email 把修改的 patch 文件发送给 ffmpeg-devel

将提琴手与 Windows 身份验证一起使用