05-Git常用高阶操作:reset/rebase/cherry-pick/merge冲突解决
05-Git常用高阶操作:reset/rebase/cherry-pick/merge冲突解决
前言
做开发三年,Git 命令只会add、commit、push三件套?这在团队协作里可不够用。当你需要撤销提交、整理分支历史、或者把某个功能单独摘到另一个分支时,reset、rebase、cherry-pick这些高阶操作就派上用场了。
本文从实际场景出发,把这些命令掰开揉碎讲清楚,顺带把最让人头疼的 merge 冲突解决流程走一遍。
一、git reset:三种模式,三种后果
git reset的核心作用是移动 HEAD 指针(当前分支的最新提交位置)。根据模式不同,对暂存区和工作区的影响也不同。
1.1 --soft:只动 HEAD
gitreset--softHEAD~1效果:HEAD 回退到上一个提交,但暂存区和工作区保持不变。
适用场景:提交后发现 commit message 写错了,或者想把几个连续的提交合并成一个。代码改动都还在暂存区里,直接重新commit即可。
1.2 --mixed(默认):动 HEAD + 暂存区
gitreset--mixedHEAD~1# 或者直接gitreset HEAD~1效果:HEAD 回退,暂存区被重置,但工作区代码不变。
适用场景:提交后发现多 add 了文件,想重新选择要提交的内容。改动还在,只是从暂存区退回到了工作区。
1.3 --hard:动 HEAD + 暂存区 + 工作区
gitreset--hardHEAD~1效果:HEAD 回退,暂存区和工作区全部被重置到指定提交的状态。
适用场景:本地写的代码完全不要了,回到某个干净的版本。注意:此操作不可逆(除非用 reflog 找回),生产环境慎用。
一个记忆技巧:soft → 只动 HEAD;mixed → 动 HEAD + 暂存区;hard → 动 HEAD + 暂存区 + 工作区。从轻到重,逐步覆盖。
1.4 三种模式对比
| 模式 | HEAD | 暂存区 | 工作区 | 风险等级 |
|---|---|---|---|---|
--soft | 移动 | 不变 | 不变 | 低 |
--mixed | 移动 | 重置 | 不变 | 中 |
--hard | 移动 | 重置 | 重置 | 高(不可逆) |
二、git rebase:变基原理与黄金法则
2.1 什么是变基
rebase(变基)的意思是:把当前分支的提交"搬"到目标分支最新提交的后面,让提交历史变成一条直线。
# merge 之后的提交历史(有分叉) A---B---C---D (main) \ E---F (feature) # rebase 之后的提交历史(直线) A---B---C---D---E'---F' (feature)变基过程中,E 和 F 会被重新应用到 D 之后,生成新的提交 E’ 和 F’(SHA 值变了,但内容相同)。
2.2 常用命令
# 将 feature 分支变基到 maingitcheckout featuregitrebase main# 交互式变基,整理最近3个提交(合并、修改message等)gitrebase-iHEAD~3交互式变基可以做的操作:
pick:保留该提交squash:将该提交合并到上一个提交reword:修改提交信息drop:丢弃该提交
2.3 rebase 的黄金法则
永远不要对已经推送到远程仓库的公共分支执行 rebase。
原因很简单:rebase 会重写提交历史,生成新的 SHA 值。如果别人基于旧的提交历史在开发,你 rebase 后一 push,别人的本地分支就全乱了。
简单记:rebase 只用于本地分支整理,公共分支用 merge。
三、git cherry-pick:精准挑选提交
有时候你只想把某个分支上的某几个提交拿到当前分支,而不是合并整个分支。比如 feature 分支上修了一个 bug,你想把这个修复单独提到 main 分支,但不想把其他还没完成的提交带过来。
3.1 基本用法
# 挑选单个提交gitcherry-pick<commit-hash># 挑选多个提交gitcherry-pick<hash1><hash2><hash3># 挑选一个范围(不包含起始提交,包含结束提交)gitcherry-pick<start-hash>..<end-hash>3.2 实战场景
无人售货柜项目中,固件分支firmware-v2上修了一个传感器读取的 bug,提交 hash 是a3f5b2c。主干分支main也需要这个修复:
gitcheckout maingitcherry-pick a3f5b2cgitpush origin main就这么简单,精准拿走,不多带一个提交。
cherry-pick 也会产生冲突,解决方式和 merge 冲突一样,后面统一讲。
四、git merge 冲突:产生与解决流程
4.1 冲突是怎么产生的
当两个分支修改了同一个文件的同一行代码,Git 无法自动判断保留哪个版本,就会产生冲突。
gitcheckout featuregitmerge main# Auto-merging src/main.java# CONFLICT (content): Merge conflict in src/main.java4.2 冲突文件长什么样
打开冲突文件,你会看到类似这样的标记:
public void checkSensor() { <<<<<<< HEAD int threshold = 50; // feature分支的修改 ======= int threshold = 80; // main分支的修改 >>>>>>> main if (sensorValue > threshold) { alert(); } }<<<<<<< HEAD到=======之间是当前分支(feature)的代码=======到>>>>>>> main之间是传入分支(main)的代码
4.3 解决冲突的标准流程
第一步:查看冲突文件列表
gitstatus# Unmerged paths:# both modified: src/main.java第二步:打开冲突文件,手动编辑
保留正确的代码,删除冲突标记。比如这里我们判断threshold应该取 80:
publicvoidcheckSensor(){intthreshold=80;if(sensorValue>threshold){alert();}}第三步:标记冲突已解决
gitaddsrc/main.java第四步:完成合并
gitcommit-m"merge: 合并main分支,解决传感器阈值冲突"或者用git merge --continue一键完成。
4.4 冲突解决的三条原则
- 看懂再改:理解两段代码各自的意图,别盲目保留一端
- 改完测试:解决冲突后必须跑一遍编译和测试,确保逻辑没有断裂
- 小步合并:频繁同步主干分支,减少长期分叉带来的大面积冲突
4.5 rebase 冲突的解决
rebase 产生冲突时,解决流程类似,但命令不同:
# 解决冲突后gitadd<冲突文件>gitrebase--continue# 如果想中止 rebase,回到开始前gitrebase--abort五、命令速查表
| 操作 | 命令 | 风险 |
|---|---|---|
| 回退提交(保留改动) | git reset --soft HEAD~1 | 低 |
| 回退提交(改动退回工作区) | git reset --mixed HEAD~1 | 中 |
| 回退提交(彻底丢弃改动) | git reset --hard HEAD~1 | 高 |
| 整理提交历史 | git rebase -i HEAD~3 | 中 |
| 挑选提交 | git cherry-pick <hash> | 低 |
| 合并分支 | git merge <branch> | 中 |
| 中止合并 | git merge --abort | 低 |
| 中止变基 | git rebase --abort | 低 |
总结
| 命令 | 核心用途 | 一句话记忆 |
|---|---|---|
reset | 回退到历史提交 | soft 轻、mixed 中、hard 重 |
rebase | 整理提交历史为直线 | 只在本地用,别碰公共分支 |
cherry-pick | 精准摘取单个提交 | 像摘樱桃,挑熟的那颗 |
merge | 合并分支 | 冲突不可怕,看懂再改 |
掌握这四个命令,日常 Git 协作基本够用了。关键不是记住命令参数,而是理解每一步操作背后到底动了什么——HEAD、暂存区、工作区,搞清楚这三者,Git 就不会再让你头疼。