Git:在多个提交之间生成单个补丁

71

这是现状:

  1. 我们基于一个已有的开源git仓库(称为source-repo)创建了一个“私有”仓库(称为our-repo)。

  2. 我们一直在开发代码,并在我们的仓库中进行了约20次合并。因此,该仓库从“State_Initial”状态转移到了“State_Current”状态。

现在,出于业务原因,我们希望将所有开发工作移交给第三方。由于涉及一些法律问题,唯一的选择是提供一个包含我们所有更改的“单个”补丁文件。这是从“State_Initial”到“State_Current”之间的压缩补丁。

我四处寻找,并找到了

git format-patch -X

但是,它会生成“n”个.patch文件。

是否有一种方法可以创建一个单独的补丁文件,以便如果我们基于“source-repo”创建一个仓库并应用补丁,它会将我们带到“State_Current”?

8个回答

103
下面的命令创建一个包含多个提交的单个.patch文件。
git format-patch cc1dde0dd^..6de6d4b06 --stdout > foo.patch

然后你可以像这样应用它:

git am foo.patch

注意: 如果您想要包括第一个提交的SHA,请确保使用^..而不是..


36
警告:这将生成一个包含多个独立补丁的文件。它不会像问题中要求的那样压缩历史记录。如果在中间有敏感提交,它们将被公开。 - John Kugelman
4
如果你想将多个提交压缩成一个补丁,则可以通过变基合并它们,然后将压缩后的提交作为一个补丁。 - Priyanshu
6
只是为了澄清(也许仅适用于Windows用户?)执行“git am foo.patch”部分时,此命令对我而言导致出现错误。我收到了一个“Patch:format detection failed”的错误。可行的方法是使用以下命令创建补丁: git format-patch cc1dde0dd^..6de6d4b06 --output=foo.patch - ThermoX
3
对于好奇的人,这是有关使用“..”和“^”语法基于提交范围选择提交的官方git文档。 - Addison Klinke
1
如此回答所述,相对于git diff而言,git format-patch更具优势,因为它包含了改动的元数据(如提交者身份、时间戳、提交信息等)。 - Addison Klinke
显示剩余3条评论

20

您可以通过以下方式生成两个提交之间的单文件补丁,<COMMIT1> 和 <COMMIT2>:

有提交历史记录时

生成补丁:

git format-patch <COMMIT1>^..<COMMIT2> --stdout > commits.patch

应用补丁:

git am commits.patch

没有提交历史记录

生成补丁:

git diff <COMMIT1>^..<COMMIT2> > diff.patch

应用补丁:

git apply diff.patch

注意事项

  • On Windows, you'd better run the above command in Git Bash. If you use PowerShell and get error when apply the patch, you can try to convert the patch file to UTF-8 encoding.

  • If you for some reason want to include the first commit into the patch. You can do:

    git format-patch --root --stdout > all.patch
    

    or

    git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD > diff.patch
    

    The 4b825dc642cb6eb9a060e54bf8d69288fbee4904 represents the "empty tree", see How to get Git diff of the first commit?


3
你可以使用 git diff 命令: git diff 0f3063094850 > ./test.patch

有用,但有点简洁。我认为这意味着,"要获取当前状态和特定提交之间差异的补丁,请执行git diff [otherCommitSHA]> test.patch"--因为这似乎是它所做的。该命令似乎不会创建与patch命令相同的补丁格式,但您可以了解此处可使用哪些补丁格式 - ruffin

3
创建一个名为 squashed 的新分支,它只有一个压缩的提交(commit)。这个分支将与你的普通分支具有完全相同的内容,但没有历史记录。
仔细检查后,如果满意,请使用format-patch创建补丁文件。
$ git checkout -b squashed $(git commit-tree HEAD^{tree} -m 'Squashed history')
$ git format-patch --root HEAD

这是一项不会破坏数据的操作,你可以在完成后切换回正常的开发分支。你可以通过给“压缩”后的分支打标签来保存对其的引用,或者使用“branch -D”命令将其删除(如果你不再需要它)。
$ git branch -D squashed

3
你如何指定提交范围的起点呢?看起来这里捕获了分支中自初始提交以来所有文件的单个提交。 - GuyPaddock

2

0

如果由于某种原因您不想创建一个临时分支并压缩在state_initialstate_current之间的所有提交,然后使用git format-patch,那么有一种替代方法。如果您从state_initial分支中分支出来,并将您的分支重新定位到源分支的顶部:

git format-patch source_branch <patch_file_name>

当您执行git am <patch_file_name>时,它不会重建整个提交链。但是,此补丁文件将是一系列顺序更改的列表。如果您跨多个提交更改并再次更改,如果有人检查补丁文件,仍然可以看到。


-1

使用以下命令:

$ git format-patch -n <commit string>

-n 表示你要为此 patch 生成多少个 commits,而 代表你要从哪个 commit 开始生成。


1
这将为每个提交生成一个补丁。 - Farid

-2

git format-patch 可以接受一个修订范围作为参数。请参见 git help format-patch

概述

git format-patch [-k] [(-o|--output-directory) <dir> | --stdout]
                   ...(省略多个标志)
                   [--progress]
                   [<common diff options>]
                   [ <since> | <revision range> ] 

描述

有两种方法可以指定要操作的提交。

  1. 单个提交 <since>,指定要输出到当前分支末端的提交,这些提交不在导致 <since> 的历史记录中。

  2. 通用的 <revision range> 表达式(参见 gitrevisions(7) 中的“指定修订”一节)表示指定范围内的提交。

例如,以下命令为当前分支上最近的三个提交生成一个补丁:
git format-patch HEAD~3..HEAD

6
这将为每个提交生成一个.patch文件。提问者希望有一个单独的.patch文件,其中包含给定范围内的所有提交。 - aleclarson

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