Git patch的使用方法和场景

本文介绍了Git中diff/apply、format-patch/am和cherry-pick三种补丁操作方法,包括如何生成patch、检查和应用补丁,以及它们在不同场景下的使用。重点在于对比这些工具在版本控制中的灵活性和适用性。

Git补丁在同步不同分支的代码时应用较多,且比较灵活,比如修复了基线的一个bug,同步到各个版本的发布分支。本文介绍两种生成和应用补丁的方案。

1. diff/apply方案

此方案使用diff命令生成patch文件,后使用apply命令应用patch到分支,从而实现修改复刻。其大致流程如下:

# 生成补丁
git diff > commit.patch
# 检查补丁
git apply --check commit.patch
# 应用补丁
git apply commit.patch

下面具体介绍操作方法:

1.1 diff生成patch

先看下git diff的帮助文档:

usage: git diff [<options>] [<commit>] [--] [<path>...]
   or: git diff [<options>] --cached [<commit>] [--] [<path>...]
   or: git diff [<options>] <commit> [<commit>...] <commit> [--] [<path>...]
   or: git diff [<options>] <commit>...<commit>] [--] [<path>...]
   or: git diff [<options>] <blob> <blob>]
   or: git diff [<options>] --no-index [--] <path> <path>]

common diff options:
  -z            output diff-raw with lines terminated with NUL.
  -p            output patch format.
  -u            synonym for -p.
  --patch-with-raw   output both a patch and the diff-raw format.
  --stat        show diffstat instead of patch.
  --numstat     show numeric diffstat instead of patch.
  --patch-with-stat  output a patch and prepend its diffstat.
  --name-only   show only names of changed files.
  --name-status show names and status of changed files.
  --full-index  show full object name on index lines.
  --abbrev=<n>  abbreviate object names in diff-tree header and diff-raw.
  -R            swap input file pairs.
  -B            detect complete rewrites.
  -M            detect renames.
  -C            detect copies.
  --find-copies-harder   try unchanged files as candidate for copy detection.
  -l<n>         limit rename attempts up to <n> paths.
  -O<file>      reorder diffs according to the <file>.
  -S<string>    find filepair whose only one side contains the string.

可见支持单个commit或多个commit生成patch,也支持对某个文件单独生成patch。这么多命令参数,该怎么去记住呢?实际上,diff本质上是对两个版本之间文件做数据比较得出差异,重点在于两个版本。下面我们逐一分析下不同命令参数下,这两个版本是指什么:

  • git diff
    没有指定任何版本,那默认就是对lastCommitworking copy之间作比较。

这里就会出现两种情况:如果当前的working copy已经commit过了,那么lastCommit就是目前working copy自身,所以diff不会输出任何内容;如果当前的working copy未commit,那么diff就会输出本次修改的内容。

  • git diff commitId
    指定了一个commitId,那就是对commitIdworking copy之间作比较。同样的,如果你的working copy已经commit过了,那么这个命令会等价于git diff commitId lastCommitId

  • git diff commitId1 commitId2
    指定了两个commitId(注意把时间早的commitId放在前面),这种情况就是对commitId1commitId2之间作比较。

捋清楚这其中的关系,我们下面对日常开发中常见的场景进行介绍。

本地修改未commit

希望把修改的内容生成patch,可以如下操作:

git diff > commit.patch

对于已经add但是未commit的修改:

git diff --cached > commit.patch

本地修改已commit

希望把最近一次的修改生成patch:

# 注意:commitId为倒数第二个提交ID
git diff commitId > commit.patch

1.2 apply应用patch

生成patch文件后,我们切换到希望应用patch的分支,然后按下面的步骤操作:

# 检查patch是否可以应用
git apply --check commit.patch
# 打单个补丁
git apply commit.patch
# 打多个补丁
git apply ../patch/*.patch

打完补丁后再add/commit进行提交。

看下apply命令的帮助文档:

usage: git apply [<options>] [<patch>...]

    --exclude <path>      应用补丁时排除指定的目录/文件
    --include <path>      apply changes matching the given path
    -p <num>              remove <num> leading slashes from traditional diff paths
    --no-add              ignore additions made by the patch
    --stat                仅展示补丁差异内容
    --numstat             show number of added and deleted lines in decimal notation
    --summary             仅对补丁生成总结报告
    --check               仅检查补丁是否可用
    --index               make sure the patch is applicable to the current index
    -N, --intent-to-add   mark new files with `git add --intent-to-add`
    --cached              apply a patch without touching the working tree
    --unsafe-paths        accept a patch that touches outside the working area
    --apply               also apply the patch (use with --stat/--summary/--check)
    -3, --3way            attempt three-way merge if a patch does not apply
    --build-fake-ancestor <file>  build a temporary index based on embedded index information
    -z                    paths are separated with NUL character
    -C <n>                ensure at least <n> lines of context match
    --whitespace <action>  detect new or modified lines that have whitespace errors
    --ignore-space-change  ignore changes in whitespace when finding context
    --ignore-whitespace   ignore changes in whitespace when finding context
    -R, --reverse         apply the patch in reverse
    --unidiff-zero        don't expect at least one line of context
    --reject              leave the rejected hunks in corresponding *.rej files
    --allow-overlap       allow overlapping hunks
    -v, --verbose         be verbose
    --inaccurate-eof      tolerate incorrectly detected missing new-line at the end of file
    --recount             do not trust the line counts in the hunk headers
    --directory <root>    prepend <root> to all filenames

2. format-patch/am方案

这个方案是使用format-patch命令生成一系列patch文件,然后使用am命令批量应用patch。可以类比上一个方案进行理解,尤其是两个版本的概念是相同的。其大致流程如下:

# 生成patch
git format-patch -1
# 检查patch
git apply --check 0001-made-some-change.patch
# 应用patch
git am 0001-made-some-change.patch

为什么有了diff还要有format-patch呢?主要还是使用场景的不同:

  1. diff仅保留了文件重A状态变成B状态的差别,而不会保留commit记录消息等信息,而format-patch则是完整保留了每次提交的完成信息,这也解释了diff可以多个commit生成单个patch,而format-patch则是每个commit都生成一个patch文件。
  2. 两者是成对使用的,即git apply使用的补丁必须是git diff生成的;git am的补丁必须是git format-patch生成的。当然补丁检查都是用git apply --check/--stat

2.1 format-patch生成patch

format-patch命令是对指定版本范围内每次commit的修改生成对应的patch文件。

举个列子,现有如下提交历史的Git仓库:

D:\git_ws\test1>git log --pretty=oneline
4f499fc07597dcb5c7bb337116998a3e3e1f9116 (HEAD -> master) add third line
76a90ee80c67f310f6b6dc4b797cceedfa310368 add second line
ff53dd9d346dd3ab0013c0983160351d49a25024 Add simple.txt

我们对它执行如下命令:

D:\git_ws\test1>git format-patch ff53dd
0001-add-second-line.patch
0002-add-third-line.patch

可见生成了以下两个patch:

  1. 76a90e -> 0001-add-second-line.patch
  2. 4f499f -> 0002-add-third-line.patch

文件名格式:序号-消息第一行.patch。另外值得说明的是,这个仓库其实本地已经修改了文件,但是没有commit,进一步验证了,format-patch仅对commit后的修改生成patch。这其实也比较好理解,没有提交的话,连commit msg都没有,patch文件名都没法定了。

看下format-patch命令的帮助文档:

usage: git format-patch [<options>] [<since> | <revision-range>]

    -n, --numbered        use [PATCH n/m] even with a single patch
    -N, --no-numbered     use [PATCH] even with multiple patches
    -s, --signoff         add Signed-off-by:
    --stdout              指定输出位置,如所有patch输出到一个文件
    --cover-letter        generate a cover letter
    --numbered-files      文件名只有编号,不包含提交信息
    --suffix <sfx>        指定patch文件名后缀,默认'.patch',比较常用的还有`.diff`
    --start-number <n>    start numbering patches at <n> instead of 1
    -v, --reroll-count <n> mark the series as Nth re-roll
    --rfc                 Use [RFC PATCH] instead of [PATCH]
    --cover-from-description <cover-from-description-mode> generate parts of a cover letter based on a branch's description
    --subject-prefix <prefix> Use [<prefix>] instead of [PATCH]
    -o, --output-directory <dir> 指定patch的存放目录
    -k, --keep-subject    don't strip/add [PATCH]
    --no-binary           don't output binary diffs
    --zero-commit         output all-zero hash in From header
    --ignore-if-in-upstream don't include a patch matching a commit upstream
    -p, --no-stat         show patch format instead of default (patch + stat)

当前分支所有超前master的提交

git format-patch -M master

某次提交以后的所有patch

git format-patch commitId

从根到指定提交的所有patch

git format-patch --root commitId

某两次提交之间的所有patch

git format-patch commitId1..commitId2

某次提交(含)之前的几次提交

# -n指patch数
git format-patch -n commitId 

# 比如单次提交,生成commitId这次提交的patch
git format-patch -1 commitId

# 生成commitId前一次到commitId这两次修改的patch
git format-patch -2 commitId

2.2 am应用patch

git am可以一次合并一个文件,或者批量合并一个目录下所有的patch。在使用git am之前, 你要首先git am --abort一次,来放弃掉以前的am信息,这样才可以进行一次全新的am。不然会遇到这样的错误:

.git/rebase-apply still exists but mbox given.

具体操作如下:

  • 打单个补丁
# 先检查patch文件:
git apply --stat newpatch.patch
# 检查能否应用成功:
git apply --check newpatch.patch
# 打补丁:使用-s或--signoff选项,可以commit信息中加入Signed-off-by信息
git am --signoff < newpatch.patch
  • 批量应用补丁
git am --abort
git am patch/*.patch

am命令的使用说明如下:

usage: git am [<options>] [(<mbox> | <Maildir>)...]
   or: git am [<options>] (--continue | --skip | --abort)

    -(<mbox> | <Maildir>)... 补丁路径

    -i, --interactive     run interactively
    -3, --3way            allow fall back on 3way merging if needed
    -q, --quiet           be quiet
    -s, --signoff         add a Signed-off-by line to the commit message
    -u, --utf8            recode into utf8 (default)
    -k, --keep            pass -k flag to git-mailinfo
    --keep-non-patch      pass -b flag to git-mailinfo
    -m, --message-id      pass -m flag to git-mailinfo
    --keep-cr             pass --keep-cr flag to git-mailsplit for mbox format
    --no-keep-cr          do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
    -c, --scissors        strip everything before a scissors line
    --whitespace <action>  pass it through git-apply
    --ignore-space-change  pass it through git-apply
    --ignore-whitespace   pass it through git-apply
    --directory <root>    pass it through git-apply
    --exclude <path>      pass it through git-apply
    --include <path>      pass it through git-apply
    -C <n>                pass it through git-apply
    -p <num>              pass it through git-apply
    --patch-format <format>  format the patch(es) are in
    --reject              pass it through git-apply
    --resolvemsg ...      override error message when patch failure occurs
    --continue            continue applying patches after resolving a conflict
    -r, --resolved        synonyms for --continue
    --skip                skip the current patch
    --abort               restore the original branch and abort the patching operation.
    --quit                abort the patching operation but keep HEAD where it is.
    --show-current-patch[=(diff|raw)]  show the patch being applied
    --committer-date-is-author-date  lie about committer date
    --ignore-date         use current timestamp for author date
    --rerere-autoupdate   update the index with reused conflict resolution if possible
    -S, --gpg-sign[=<key-id>]  GPG-sign commits

3. cherry-pick方案

比如需要将feature分支的修改同步到master分支上:

# 切换到master分支
git checkout master
# 选择和应用具体需要同步的提交
git cherry-pick <commitId>...

参考资料

<think>我们正在讨论如何使用Git应用patch文件来更新代码。根据引用[3]中的信息,gitpatch的应用过程主要涉及以下几个步骤:1.**生成patch文件**:使用`gitformat-patch`命令可以生成patch文件。不过用户的问题是关于如何应用patch,所以我们重点放在应用步骤上。2.**应用patch文件**:-**检查patch文件**:在应用之前,可以先检查patch文件将会修改哪些文件,使用命令:```gitapply--stat<patch文件>```这个命令不会真正应用patch,只是显示这个patch将会影响的文件以及更改的统计信息。-**检查patch是否可以成功应用**:使用命令:```gitapply--check<patch文件>```如果没有任何输出,说明patch可以干净地应用到当前分支。如果有冲突,会显示错误信息。-**应用patch**:有两种方式应用patch:-使用`gitapply`命令:```gitapply<patch文件>```这种方式会应用patch,但不会创建提交(类似于`patch`命令的效果),需要手动提交。-使用`gitam`命令(推荐):```gitam--signoff<<patch文件>```或者直接指定文件:```gitam--signoff-i<patch文件>```这个命令会应用patch并自动创建提交(包括提交信息)。`--signoff`选项会在提交信息中添加签名(Signed-off-by行)。`-i`选项是交互模式,但通常可以省略。注意:`gitam`命令要求patch文件是由`gitformat-patch`生成的,因为它包含提交信息。如果patch文件是通过其他方式(如`diff`命令)生成的,则使用`gitapply`。**步骤总结**:1.首先,确保你当前位于需要应用patchGit仓库目录中。2.使用`gitapply--check`检查patch是否可以无冲突应用。3.如果没有问题,使用`gitam`来应用patch(如果patch包含提交信息)或者使用`gitapply`(如果不希望自动创建提交)。**示例**:假设你有一个名为`fix-bug.patch`的patch文件,你可以按照以下步骤应用:```bash#检查patch文件gitapply--statfix-bug.patch#检查是否可以应用gitapply--checkfix-bug.patch#应用patch并创建提交gitam--signoff<fix-bug.patch```如果应用过程中出现冲突,`gitam`会暂停,并提示你解决冲突。解决冲突后,使用`gitadd`将解决后的文件标记为已解决,然后使用`gitam--continue`继续。如果你想跳过这个patch,可以使用`gitam--skip`,或者中止整个am过程(将仓库恢复到am之前的状态)使用`gitam--abort`。§§相关问题§§1.如何生成Gitpatch文件?2.应用Gitpatch时出现冲突如何解决?3.除了patch文件,还有哪些Git协作方式?</think>### 如何使用Git应用Patch文件更新代码 以下是应用Git patch文件的标准流程(基于引用[3]的权威指导): #### **1. 检查Patch文件内容** ```bash git apply --stat patch_file.patch ``` ▸ 显示patch将修改的文件列表更改统计(不实际应用) ▸ 示例输出: ``` src/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) ``` #### **2. 验证Patch可应用性** ```bash git apply --check patch_file.patch ``` ▸ 返回空 = 可安全应用 ▸ 出现错误 = 存在冲突需解决 #### **3. 正式应用Patch** **方式一:创建新提交(推荐)** ```bash git am --signoff < patch_file.patch ``` ▸ 自动生成包含原提交信息的Git提交 ▸ `--signoff`添加开发者签名 ▸ 成功输出:`Applying: [原提交信息]` **方式二:仅修改文件(不创建提交)** ```bash git apply patch_file.patch ``` ▸ 直接修改工作区文件 ▸ 需手动`git add``git commit` #### **4. 冲突处理流程** 当`--check`报错时: 1. 手动应用并标记冲突: ```bash git apply --reject patch_file.patch ``` 2. 在生成`.rej`文件中解决冲突 3. 手动标记已解决: ```bash git add resolved_file.c ``` 4. 完成am过程: ```bash git am --continue ``` > **关键提示**: > - 使用`git am`要求patch由`git format-patch`生成[^3] > - 普通diff生成的patch只能用`git apply` > - 跨仓库应用时需确保文件路径一致 #### **应用场景示例** ```mermaid graph LR A[收到patch文件] --> B{是否含提交信息?} B -- 是 --> C[git am] B -- 否 --> D[git apply] C --> E[自动创建提交] D --> F[手动提交] ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值