【git的摸鱼技巧】之工欲善其事
Git Cherry-pick 操作与常见问题处理指南(增强版)
本文档综合了 git cherry-pick 操作的完整指南,涵盖基本用法、常见问题、跨仓库场景及替代方案。
1. 基本用法
# 应用一个或多个提交
git cherry-pick <commit1> <commit2> ...
# 应用一个提交范围(不包含起始提交)
git cherry-pick <older-commit>..<newer-commit>
# 应用更改但不自动提交(可合并多个提交的改动)
git cherry-pick -n <commit>
# 允许生成空提交(通常不推荐)
git cherry-pick --allow-empty <commit>
2. 常见错误与解决
2.1 “一个拣选或还原操作已在进行”
错误信息:
error: 一个拣选或还原操作已在进行
提示:尝试 "git cherry-pick (--continue | --quit | --abort)"
原因: 上一次 cherry-pick 未正常结束(可能因冲突或空提交而暂停)。
解决方法:
| 场景 | 命令 | 说明 |
|---|---|---|
| 放弃操作,回到操作前状态 | git cherry-pick --abort | 推荐,干净回退 |
| 已解决冲突,继续操作 | git add <文件> → git cherry-pick --continue | 继续后续提交 |
| 退出但保留工作区状态 | git cherry-pick --quit | 不常用,保留索引和工作区 |
2.2 空提交(Empty Commit)
现象:
无文件要提交,干净的工作区
之前的拣选操作现在是一个空提交...
如果您想跳过这个提交,使用命令:git reset
然后执行 "git cherry-pick --continue"
原因: 该提交的变更内容已存在于当前分支,应用后没有产生实际改动。
解决方法:
git reset
git cherry-pick --continue
或直接跳过(若 Git 支持):
git cherry-pick --skip
注:某些旧版本 Git 可能不支持
--skip,请使用reset+--continue。
2.3 冲突(Conflict)
现象: cherry-pick 过程中提示冲突,并列出冲突文件。
解决方法:
# 1. 手动编辑冲突文件,解决冲突
vim <冲突文件>
# 2. 标记已解决
git add <冲突文件>
# 3. 继续
git cherry-pick --continue
# 或放弃本次操作
git cherry-pick --abort
3. 连续 cherry-pick 多个提交
Git 支持一次指定多个提交,按顺序依次应用。
git cherry-pick abc123 def456
- 如果中间某个提交遇到空提交或冲突,操作会暂停,需要人工处理后再用
--continue继续。 - 处理完当前问题后,Git 会自动应用后续提交,直至全部完成。
典型工作流示例
# 1. 开始 cherry-pick
git cherry-pick abc123 def456
# 2. 若遇空提交,按提示处理
git reset
git cherry-pick --continue
# 3. 若遇冲突,解决后继续
vim <冲突文件>
git add <冲突文件>
git cherry-pick --continue
# 4. 完成所有提交后,查看状态
git status
4. 跨仓库 Cherry-pick
从另一个仓库的远程分支中 cherry-pick 某个特定提交。
操作步骤
# 1. 添加远程仓库并获取最新信息
git remote add <remote-name> <仓库URL>
git fetch <remote-name>
# 2. 查看远程分支的提交历史,找到目标 commit
git log <remote-name>/<branch> --oneline
# 3. 切换到目标本地分支(不存在则创建)
git checkout -b <local-branch>
# 4. 应用指定提交
git cherry-pick <commit-hash>
# 5. 若遇冲突,解决后继续
git add .
git cherry-pick --continue
# 6. 完成后可推送
git push -u origin <local-branch>
完整示例
git fetch first
git log first/qicongjie --oneline
# 输出:abc1234 Fix something
# def5678 Update config
git checkout first_dev
git cherry-pick abc1234
5. 其他与 Cherry-pick 同步的方法对比
当 cherry-pick 单个提交不适合时,可考虑以下替代方案:
| 场景 | 推荐方法 | 保留历史 |
|---|---|---|
| 跨仓库同步全部代码 | git merge --allow-unrelated-histories | ✅ 是 |
| 跨仓库同步(按补丁) | git format-patch + git am | ✅ 是 |
| 跨仓库同步(仅代码) | git diff + git apply | ❌ 否 |
| 应用外部补丁文件 | git am < patch-file | ✅ 是 |
| 仅应用代码差异 | git apply < patch-file | ❌ 否 |
| 本地分支迁移 | git bundle | ✅ 是 |
| 本地分支迁移 | 添加本地路径为远程 | ✅ 是 |
format-patch + am 方式
# 在源仓库生成补丁
git format-patch --stdout 1.74..1.75 > changes.patch
# 在目标仓库应用补丁
git am < changes.patch
diff + apply 方式(不保留历史)
# 生成差异
git diff 1.74 1.75 > full.patch
# 应用差异
git apply full.patch
# 若冲突,使用 --reject 生成 .rej 文件
git apply --reject full.patch
6. 跨仓库标签同步(无共同历史场景)
当需要将仓库 A 的标签 1.74~1.75 之间的修改同步到仓库 B(无共同历史)时:
准备工作
cd /path/to/repo-B
git remote add old-repo <仓库A的URL>
git fetch old-repo --tags
方法一:merge(保留完整历史)
git merge --allow-unrelated-histories refs/tags/1.75
方法二:bundle(适用于未推送的分支)
# 在旧仓库中
git bundle create myPro.bundle myPro
# 在新仓库中
git bundle unbundle myPro.bundle
git checkout -b myPro FETCH_HEAD
方法三:添加本地路径为远程
git remote add old-local /path/to/old-repo
git fetch old-local
git checkout -b myPro old-local/myPro
git remote remove old-local
7. 补丁应用冲突处理
git am 冲突
# 查看失败补丁内容
git am --show-current-patch
# 手动编辑冲突文件后标记已解决
git add <冲突文件>
git am --continue
# 跳过当前补丁(谨慎)
git am --skip
# 放弃整个 am 操作
git am --abort
git apply 冲突
# 使用 --reject 选项生成 .rej 文件
git apply --reject full.patch
# 根据 .rej 文件手动修改代码,然后删除 .rej 文件
rm -f *.rej
git add .
git commit -m "Apply changes"
8. 验证同步是否彻底
# 直接对比当前分支与目标标签/提交
git diff refs/tags/1.75
# 对比期望差异与实际差异
git diff 1.74 1.75 > expected.patch
git diff 1.74 HEAD > actual.patch
diff -u expected.patch actual.patch
# 忽略空白差异
git diff --ignore-space-change 1.74 1.75 > expected.patch
git diff --ignore-space-change 1.74 HEAD > actual.patch
无输出表示完全一致。
9. 操作前备份
git branch backup-before-sync
10. Git Submodule 常见问题
10.1 子模块更新失败:未找到子模块 URL
场景: 进入子目录执行 git submodule update --init --recursive 时失败。
cd apps
git submodule update --init --recursive
# fatal: 在 .gitmodules 中未找到子模组 'dh' 的 url
原因: apps/.gitmodules 中缺少某些子模块的 URL 配置(如子模块 dh 的配置项缺失或不完整)。
解决方法:
方法一(推荐):手动更新指定子模块,跳过配置有问题的子模块。
git submodule update --init --recursive XXX/baseline ZZZ/baseline
只更新有 URL 配置的子模块,忽略缺失配置的子模块。
方法二:修复 .gitmodules 文件,补全缺失的子模块配置。
# 查看当前 .gitmodules 内容
cat .gitmodules
# 手动添加缺失的子模块配置
git config -f .gitmodules submodule.dh.path <path>
git config -f .gitmodules submodule.dh.url <url>
# 或直接编辑 .gitmodules 文件
vim .gitmodules
# 更新后重新执行
git submodule update --init --recursive
方法三:彻底重置子模块。
# 清理子模块缓存并重新初始化
git submodule deinit -f .
git submodule init
git submodule update --recursive
提示:方法一适用于仅需要部分子模块的场景,可快速绕过配置问题继续工作。
10.2 子模块添加失败:“已经存在于索引中”
git submodule add -b master <url> baseline
# 错误:'baseline' 已经存在于索引中
解决方法:
git rm -f --cached baseline
rm -rf .git/modules/baseline
rm -rf baseline
git config -f .gitmodules --remove-section submodule.baseline 2>/dev/null
git config --remove-section submodule.baseline 2>/dev/null
git add .gitmodules
git commit -m "Remove broken submodule baseline"
git submodule add -b master <正确的URL> baseline
git submodule update --init --recursive
10.3 子模块内文件无法直接 add 到主仓库
git add /path/to/baseline/CMakeLists.txt
# fatal: 路径规格 '...' 在子模组 'baseline' 中
解决方法: 子模块是独立仓库,需在子模块内提交,再到主仓库更新引用。
cd baseline
git add CMakeLists.txt
git commit -m "Update files"
cd ..
git add baseline
git commit -m "Update submodule baseline"
10.4 合并冲突导致子模块索引损坏
error: 'baseline' appears as both a file and as a directory
fatal: cannot drop to stage #0
解决方法:
git merge --abort # 如果是 merge 冲突
git rebase --abort # 如果是 rebase 冲突
# 或强制重置
git reset --hard HEAD
11. 完整错误参考表
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
| 一个拣选或还原操作已在进行 | 上次 cherry-pick 未正常结束 | --abort 或 --continue |
| 无文件要提交,干净的工作区 | 空提交(已存在相同改动) | git reset → --continue |
| 冲突 | 代码冲突 | 解决后 git add → --continue |
fatal: 在 .gitmodules 中未找到子模组 'dh' 的 url | .gitmodules 缺少某子模块 URL 配置 | 手动更新指定子模块,或修复 .gitmodules |
fatal: 路径规格 '...' 在子模组 '...' 中 | 主仓库直接 add 子模块内部文件 | 在子模块内提交,再到主仓库更新引用 |
'xxx' 已经存在于索引中 | 子模块残留或重复添加 | 执行子模块清理步骤 |
'baseline' appears as both a file and as a directory | 合并冲突导致索引损坏 | git merge --abort 或 git reset --hard HEAD |
fatal: 有歧义的参数 | 标签引用格式错误 | 使用 refs/tags/ 或提交哈希 |
error: 打补丁失败 | 补丁上下文不匹配 | 手动修改文件后 --continue |
Couldn't find remote ref | 远程无此分支 | 检查 git branch -r,从本地恢复 |
不能打开 .git/FETCH_HEAD: 权限不够 | 文件所有者错误 | sudo chown -R $(whoami) .git |
merge: xxx - 不能合并 | 当前不在分支上 | 先切换分支 |
12. 注意事项
- 空提交通常表示该改动已存在,跳过即可,无需强行保留空提交。
- 连续 cherry-pick 时,提交顺序影响最终结果,建议从旧到新依次应用。
- 若出现反复的空提交,先用
git show <commit>查看该提交的实际内容,确认是否已包含。 - 若冲突太多,可考虑使用
git merge或git rebase替代 cherry-pick。 - 操作前务必备份当前分支。
- 处理子模块时,务必确保索引干净,及时提交
.gitmodules的更改。 - 合并冲突导致索引损坏时,优先尝试
git merge --abort或git reset --hard HEAD。 - 远程跟踪引用无效时,使用
git remote prune origin清理。
13. 命令速查
| 命令 | 说明 |
|---|---|
git cherry-pick <commit> | 应用指定提交 |
git cherry-pick -n <commit> | 应用但不自动提交 |
git cherry-pick --allow-empty | 允许空提交 |
git cherry-pick --skip | 跳过当前提交 |
git cherry-pick --abort | 放弃操作,回到操作前状态 |
git cherry-pick --quit | 退出操作,保留工作区和索引 |
git cherry-pick --continue | 解决冲突/空提交后继续 |
git submodule update --init --recursive <path> | 只更新指定子模块路径 |
git submodule deinit -f . | 清理所有子模块缓存 |
git config -f .gitmodules submodule.<name>.<key> <value> | 修改 .gitmodules 配置 |
git rm -f --cached <path> | 从索引中移除子模块 |
git am < patch-file | 应用补丁(保留历史) |
git apply < patch-file | 应用差异(不保留历史) |
git format-patch <range> | 生成补丁文件 |
git merge --allow-unrelated-histories | 合并无共同历史的分支 |
git bundle | 打包分支迁移 |
git remote prune origin | 清理无效远程引用 |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)