跳至主要内容

Git Cherry-Pick:如何选择并应用特定提交

通过分步示例、最佳实践与故障排查技巧,学习如何使用 git cherry-pick!
· 15分钟 阅读

使用 AI 探索

在协作式软件开发中使用分支有助于隔离功能、修复 Bug 或进行实验。然而,有时您需要从一个分支中取出特定更改,并将其应用到另一个分支,而不是合并整个分支。这时,git cherry-pick 就派上用场了。 

本教程旨在全面介绍如何高效使用 git cherry-pick。您将学习该命令的语法、了解如何处理冲突,并掌握最佳实践与常见陷阱。我们开始吧!

什么是 Git Cherry-Pick?

git cherry-pick 是一个基础的 Git 命令,为开发者提供对源代码的精细化控制。 

与其他 Git 操作不同,例如 merge 或 rebase 会处理整个分支,cherry-pick 则允许您从一个分支中挑选特定提交并将其应用到另一个分支。这在只需集成某些特定变更、而非分支中的所有修改时,尤为精准。

git cherry-pick 的工作方式是复制所选提交的内容,并在目标分支上创建新的提交,同时保持提交历史的完整性。

何时使用 git cherry-pick

git cherry-pick 的使用场景包括: 

  • 回移 Bug 修复:您在开发分支中解决了一个 Bug,需要在稳定或发布分支中应用相同修复。Cherry-pick 允许您仅迁移该修复,而不带来无关更改。
  • 应用热修复:当生产环境需要关键修复,而开发分支仍在演进时,cherry-pick 可将该修复提取并应用到生产分支。
  • 为测试隔离功能:在测试期间,可能只需与某功能相关的特定提交。与其合并整个功能分支,不如 cherry-pick 所需提交,使测试分支更干净高效。
  • 纠正放错位置的提交:如果某次提交被误推送到错误分支,您可以将该提交 cherry-pick 到正确分支,而不破坏项目历史。
  • 在多个分支复用更改:当您需要在多个分支中应用同一更新时,cherry-pick 可以在不同分支中复制这些更改,避免重复劳动或引入分支复杂度。

Git Cherry-Pick 的语法

理解 git cherry-pick 的语法是高效使用该命令的关键。它不仅是选择提交,更是要精准地应用这些提交以达到预期结果。

用于挑选单个提交的基本语法为:

git cherry-pick <commit-hash>
  • git cherry-pick:启动该操作的命令。
  • <commit-hash>:您要 cherry-pick 的提交的唯一标识符(SHA-1 哈希)。可以通过运行 git log 查看提交历史来获取该哈希。

执行上述命令时,Git 会将指定提交的更改应用到当前分支,并创建一个包含相同更改但哈希不同的新提交。 

需要注意的是,该命令只会转移提交本身,而不会带上原分支的上下文或父级历史。

刚接触 Git 和 GitHub?从这篇面向初学者的 GitHub 与 Git 教程入门版本控制。

如何使用 Git Cherry-Pick:分步示例

既然已了解 git cherry-pick 的基本语法,接下来看看它的实际用法。

本节通过动手示例演示 cherry-pick 在常见基础场景与更复杂场景中的用法。每个示例展示如何将一个或多个提交的更改应用到另一个分支。 

示例 1:Cherry-pick 单个提交

假设您在功能分支上做了一个修复,想要在不合并整个功能分支的情况下将其应用到主分支。

  • 首先,运行以下命令查找要 cherry-pick 的提交哈希:
git log
  • 定位该提交的哈希值。 
  • 切换到主分支:
git checkout main
  • 运行 cherry-pick 命令(假设哈希为 abc1234):
git cherry-pick abc1234

执行该命令后,Git 会将 abc1234 指定的提交更改应用到您当前分支(此处为 main)。Git 会在主分支上创建一个新提交,包含与原始提交相同的更改,但具有一个新的提交哈希。

示例 2:Cherry-pick 多个提交

有时,您可能需要将一个分支中的多个独立提交应用到另一个分支。假设您的功能分支中有三次提交需要带到主分支。

  • 使用 git log 找到要 cherry-pick 的每个提交哈希:
git log
  • 切换到主分支:
git checkout main
  • 运行 cherry-pick 命令,列出这些提交:
git cherry-pick abc1234 def5678 ghi7890

示例 3:Cherry-pick 一个提交范围

假设您在功能分支上进行了一系列提交,想一次性将它们应用到主分支,而不逐个指定。该怎么做?

  • 使用 git log 确定要 cherry-pick 的起止提交(例如从 abc1234 到 ghi7890)。
  • 切换到主分支:
git checkout main
  • 使用提交范围运行 cherry-pick 命令:
git cherry-pick abc1234...ghi7890

示例 4:从远程分支 cherry-pick 一个提交

有时关键修复存在于远程分支,您希望在不合并整个分支的情况下将其应用到本地分支。步骤如下:

  • 从远程仓库获取最新更改
git fetch origin
  • 列出远程分支中的提交以找到所需哈希:
git log origin/feature_branch --oneline
  • 假设所需提交哈希为 abc1234。
  • 切换到本地主分支:
git checkout main
  • 从远程分支 cherry-pick 该提交:
git cherry-pick abc1234

这样即可在不合并整个分支的情况下,应用远程分支的某个提交。

需要检出远程分支?请按这篇分步指南操作:Git 检出远程分支。

示例 5:Cherry-pick 后再进行修改

如果您 cherry-pick 了一个提交,但在提交前需要做些微调,可以使用 Git 的交互方式。步骤如下:

  • 切换到目标分支:
git checkout main
  • cherry-pick 但在提交前暂停:
git cherry-pick -n abc1234

-n(或 --no-commit)标志会应用更改,但不会创建提交。

  • 按需修改文件。
  • 手动暂存并提交更改:
git add .
git commit -m "Modified cherry-picked commit from feature_branch"

当您需要在最终提交前微调 cherry-pick 的提交时,这种方法很实用。

在 Git Cherry-Pick 过程中处理冲突

在分支之间进行 cherry-pick 时,冲突在所难免,尤其当代码库分歧较大时。 

虽然 cherry-pick 旨在干净地应用更改,但并非总能自动协调差异。此时需要手动解决冲突。理解冲突的产生方式以及如何处理,是成功完成 cherry-pick 的关键。

冲突如何产生

当 cherry-pick 的提交与目标分支中已存在的更改有重叠或矛盾时,通常会产生冲突。例如:

  • 同一行在两个分支均被修改:如果源分支与目标分支都修改了同一行代码,Git 无法判断采用哪个版本。
  • 某分支删除、另一分支修改同一文件:如果一个分支删除了文件,而 cherry-pick 的提交修改了该文件,Git 不确定应保留文件还是应用修改。
  • 同一文件中的不相关更改:即便更改彼此无关,只要发生在同一文件内,Git 也可能将其标记为潜在冲突。

发生冲突时,Git 会暂停 cherry-pick 操作,让您的工作区处于冲突状态,您需先解决冲突再继续。

解决冲突

一旦发生冲突,Git 会标示出有冲突的文件,您需要手动处理差异。处理步骤如下:

1. 检查冲突文件:运行以下命令查看哪些文件发生冲突:

git status

该命令会显示发生冲突的文件列表。 

2. 解决冲突:您可以手动编辑冲突文件以解决问题。移除冲突标记(<<<<<<<, =======, >>>>>>>),并决定保留哪些更改或如何合并它们。

3. 使用 Git Mergetool(可选):如果手动解决较为繁琐,可使用合并工具辅助可视化解决冲突:

git mergetool

根据您的配置,上述命令会打开一个可视化合并工具,便于审查与解决冲突。

4. 标记冲突已解决:冲突解决后,使用以下命令标记文件为已解决:

git add <conflicted-file>

5. 完成 cherry-pick:当所有冲突解决且文件已暂存后,运行以下命令完成 cherry-pick:

git cherry-pick --continue

许多现代 IDE 和工具(如 Visual Studio Code 与 GitHub)都内置合并冲突解决功能。GitHub 的网页界面允许您在拉取请求中直接解决冲突,便于在共享仓库中协作,而无需切换到本地环境。

在冲突后跳过某个提交

有时冲突过于复杂,或您意识到该提交其实并不必要。在这种情况下,您可以完全跳过该提交。

1. 跳过当前 cherry-pick 步骤:如果冲突过于复杂且您不想应用该提交,可运行:

git cherry-pick --skip

这会放弃该冲突提交并继续处理下一个(若您正在 cherry-pick 多个提交)。

2. 放弃整个 cherry-pick:如果您想彻底中止 cherry-pick 操作,可运行:

git cherry-pick --abort

该命令会将工作区恢复到开始 cherry-pick 之前的状态。 

想清理提交历史?通过这篇Git squash 教程学习如何将多个提交合并为一个。

使用 Git Cherry-Pick 的最佳实践

不当使用 cherry-pick 可能导致历史复杂且团队困惑。遵循最佳实践有助于高效使用 cherry-pick,同时避免给代码库增加不必要的复杂度。

保持小而具体

cherry-pick 最适合用于小而具体、任务明确的提交,如 Bug 修复或轻量功能增强。 

避免 cherry-pick 体量大、变更多且交织的提交,因为这更易引发冲突并加大代码库管理难度。目标越明确、范围越小的提交,越容易无副作用地应用。

记录您的 cherry-pick 行为

为保持清晰历史,cherry-pick 时务必补充必要背景信息。这可以通过详细的提交信息或文档注释实现。 

关键在于说明为何需要 cherry-pick。尤其在跨长期存在的分支或协作环境中,这有助于后续贡献者理解为何选择性地应用了这些更改。

审查提交历史

在 cherry-pick 前,请审查源分支与目标分支的提交历史。这有助于识别您要 cherry-pick 的提交是否依赖其他更改。缺失依赖可能导致功能不完整或出现 Bug,因此要确保不会引入“半成品”的功能或更新。

避免过度使用 cherry-pick

虽然 cherry-pick 便于应用特定更改,但过度使用会导致历史支离破碎、同一提交在多个分支重复出现。这样会让变更来源难以追溯、整体开发脉络不清晰。 

在 cherry-pick 前,总要评估是否更适合使用合并(merge)或变基(rebase)。谨慎、少量地使用 cherry-pick,以免污染提交历史。

为无关文件困扰?通过这篇Git ignore 教程学习如何高效使用 .gitignore。

Git Cherry-Pick 常见问题排查

排查 cherry-pick 中的问题,需要对 Git 的底层机制有清晰认识。本节将介绍 cherry-pick 常见问题及其解决方法。

cherry-pick 一个不存在的提交

有时,您尝试 cherry-pick 的提交在当前分支不可达,或已被合并。这通常会报错,提示找不到该提交或无法应用。

1. 找不到提交:当您要 cherry-pick 的提交哈希在当前仓库或分支上下文中不存在时会发生。请在该提交所在分支运行 git log 核对提交历史,确保引用正确。解决方法:

  • 仔细核对提交哈希是否正确。
  • 确认该提交位于您可访问的分支上。
  • 若提交位于远程分支,确保已使用 git fetch 获取。

2. 提交已被应用:若该提交已被合并或 cherry-pick 到目标分支,Git 会阻止重复应用。此保护可保持历史整洁,避免冗余更改。解决方法:

  • 使用 git log 检查该提交是否已在目标分支中。
  • 若已应用,请跳过该 cherry-pick 操作。

在 rebase 或 merge 之后进行 cherry-pick

在 rebase 或 merge 之后进行 cherry-pick 可能带来复杂性,因为这两种操作会改变分支历史:rebase 会重写历史、merge 会合并分支,都会影响 cherry-pick 的适用性。

1. 由于 rebase 修改提交引发冲突:rebase 会重写提交历史,如果您尝试 cherry-pick 在 rebase 过程中被改写的提交,可能会遇到冲突,因为 cherry-pick 正在尝试应用与新历史不匹配的更改。解决方法:

  • 在 rebase 后用 git log 仔细检查历史,确保您要 cherry-pick 的提交没有被修改过。
  • 像标准 cherry-pick 流程一样,使用 git status 并手动编辑解决冲突。

2. merge 后出现重复提交:合并分支可能导致您想 cherry-pick 的提交其实已包含在合并历史中。再次 cherry-pick 会造成重复提交,令历史杂乱、难以追踪。解决方法:

  • 在 cherry-pick 前,检查两个分支的提交历史,确认该提交是否已被合并。
  • 若该提交已在目标分支中,避免再次 cherry-pick。

需要回退 Git 中的更改?在这篇教程中了解何时使用 git reset 与 git revert:Git reset 与 revert 教程。

结语

git cherry-pick 提供了一种强大的方式,在不合并整个分支的情况下,将特定提交从一个分支应用到另一个分支。无论您是在迁移 Bug 修复、功能更新,还是仅选择性地应用更改,它都能帮助保持 Git 历史干净、聚焦。

本文介绍了如何 cherry-pick 单个与多个提交、解决冲突,并遵循最佳实践以避免常见陷阱。明智地使用 cherry-pick,能在保持仓库井然有序的同时,优化您的工作流。

若想系统提升 Git 技能,请查看Foundations of Git 作为良好起点。您也可以学习Introduction to GitHub Concepts,或选择结构化路径GitHub Foundations 技能路径。

既然您已掌握 cherry-pick 的正确姿势,不妨在下一个项目中试试!

FAQs

如果我对同一提交进行两次 cherry-pick 会怎样?

如果您尝试再次 cherry-pick 已应用到目标分支的提交,Git 可能会创建一个具有新哈希的重复提交。要检查某个提交是否已被应用,请使用:

git log --oneline

或者,如果不确定,可使用:

git cherry-pick -n <commit-hash>

这会在不提交的情况下应用更改,便于您在最终确认前进行审查。

我能从已删除的分支中 cherry-pick 提交吗?

可以!即使分支已被删除,这些提交也可能仍存在于 Git 的历史中。

使用 git reflog 查找提交哈希,然后按常规进行 cherry-pick。

但如果 Git 已经对该分支进行垃圾回收,则这些提交可能会丢失。

如何从其他仓库 cherry-pick 一个提交?

要从其他仓库进行 cherry-pick,先将其添加为远程:

git remote add other-repo <repository-url>
git fetch other-repo

然后定位提交并进行 cherry-pick:

git cherry-pick <commit-hash>
能否 cherry-pick 包含文件删除的提交?

可以,cherry-pick 会保留该提交中的所有更改,包括文件删除。如果某次提交删除了文件,cherry-pick 后会在目标分支中删除该文件。

如何撤销一次 cherry-pick?

如果您误做了 cherry-pick,且尚未推送,可以使用以下命令撤销:

git reset --hard HEAD~1

如果已推送,请使用:

git revert <commit-hash>

这会创建一个新提交,以撤销 cherry-pick 带来的更改。

git cherry-pick 与 git apply 有何区别?
  • git cherry-pick 会复制某个特定提交并将其应用到目标分支,同时保留提交元数据。
  • git apply 用于应用补丁文件(由 git diff 生成),但不会创建新提交,也不保留提交历史。
如何在 cherry-pick 时保留原作者?

默认情况下,git cherry-pick 会将您设为作者。若要保留原提交作者,请使用:

git cherry-pick -x <commit-hash>

这会在新提交信息中添加对原始提交的引用。

能否 cherry-pick 多个不连续的提交?

可以!无需指定范围,直接列出各个提交哈希:

git cherry-pick <commit1> <commit2> <commit3>

当您只需要分支中的部分提交时,这很有用。

如何在跨分支 cherry-pick 时预防冲突?

在 cherry-pick 前,确保您的分支是最新的:

git pull origin main

同时,审查提交历史,确认这些更改不依赖其他缺失的提交。若发生冲突,请手动解决并继续:

git cherry-pick --continue
何时应避免使用 git cherry-pick?

过度使用 git cherry-pick 会导致提交历史凌乱、多个分支出现重复提交。以下情况应尽量避免:

  • 更改由多个相互依赖的提交构成——此时 merge 或 rebase 可能更合适。
  • 需要经常同步分支——rebase 或 merge 更能保持历史整洁。
  • 提交包含大规模重构,因为 cherry-pick 只移动特定更改,而非更广泛的分支上下文。
Kurtis Pykes

作者

Kurtis Pykes

主题