git cherry-pick 重新应用一次提交
cherry-pick 把选定提交引入的改动应用到当前开发线,适合旧发布分支只需要一个修复、不希望同时接收整条开发分支的场景。它不会搬走或删除源提交,也不会为目标建立与源分支的合并关系。移植后的结果必须在目标环境中重新审阅;源分支上已经通过的测试,只能说明原来的组合,不能直接证明换了依赖与历史之后仍然正确。 cherry-pick 移植相对父级的变化 选择的是相对父提交的变化 对于普通的单父提交,相关变化是它相对父级的差异。Git 利用可用历史和合并机制,把这份变化应用到目标,而不是简单地用源提交的完整文件快照覆盖目标。目标原本已有的其他修改应继续保留,除非选定变化或人工解决明确要求改变它们。审阅时既要看搬来了什么,也要确认没有误把目标自己的工作一起替换掉。 通常,cherry-pick 会在目标末端后面记录新提交。父级不同,结果目录树也可能不同,因此对象身份会变化。原作者身份和作者日期通常保留,而提交者表示实际记录这次移植的人;消息不一定变化。不能把这项操作解释成必然换作者、必然改写消息,更不能根据对象标识不同就断言原作者署名已经丢失。 默认行为会创建提交,但不提交选项会把变化留在索引与工作树中,供审阅或组合;使用允许快进的选项时,满足条件的操作还可以直接前进到已有源提交。这些明确模式说明,“所有 cherry-pick 都一定生成新对象”也不是普遍成立的规则。解释一次具体结果时,应把实际参数与所处历史一起纳入判断。 移植之前检查依赖 修复可能依赖目标分支尚不存在的接口、数据结构、配置或前置重构。补丁在文本上干净落地,并不能证明行为正确。应阅读源变化及其前提,在真正的维护分支运行受影响行为测试,并只引入该修复实际必需的依赖。如果需要适配,说明为何不能原样采用,以及新分支上的实现如何满足同一修复目标。 在共享分支之间干净应用时,-x 会把源提交标识写进新消息,方便追踪,但它不是等价性或批准状态的密码学证明。手动解决冲突后也要检查最终消息,确认来源关系没有丢失。原提交的签名不会自动成为新对象的有效签名;目标要求签名时,应遵循目标发布流程,而不是把保留作者信息等同于继承了所有验证证据。 坏掉的样子 在默认创建提交的模式中,发生冲突时,HEAD 停在最后一个已成功创建的提交,Git 用 CHERRY_PICK_HEAD 记录当前难以应用的源提交。使用 --no-commit 时不会设置该引用,因此必须检查状态和未合并条目,不能仅凭这个标记是否存在来判断冲突。能够干净应用的路径可能已经更新,冲突路径则在索引中保留未合并条目。文本冲突标记便于查看,但二进制冲突及其他类型不能只靠搜索标记字符串识别,还应检查工作区状态和索引,确认当前到底有哪些未完成事项。 解决时应确定目标需要的行为,暂存具体已解决路径,运行相关测试,再继续序列。文件已经暂存只说明冲突被标记为解决,不证明代码逻辑正确。中止会返回序列开始之前的状态;退出序列管理则只是遗忘操作状态,并不执行相同的回退。开始前应保证工作树干净且状态明确,不能把中止功能当作保存无关未提交工作的替代品。 目标已经包含某项变化时,应用结果可能为空。跳过之前,要确认目标确实具有预期行为,而不是只看到类似代码就作判断。选取合并提交还会多一个语义选择:主线参数指定以编号中的哪个父级作为差异基准。这不是通用冲突绕过开关,必须依据想移植的实际变化选择,而且结果不会重现原合并提交的多父级拓扑。 快速验证 在 POSIX 系统上使用 Python 3 与 Git 2.28 或更新版本运行下面实验。它建立私有临时仓库,排除个人 Git 配置和继承的 Git 路由变量,使用示例身份,关闭钩子与签名,没有远端。一条目标线能够接收修复;另一条故意把同一行改成不同内容,用来观察冲突停止及中止恢复。全部修改都限于这份临时数据,不触碰当前项目的提交或文件。 ...