05-Git常用高阶操作:reset/rebase/cherry-pick/merge冲突解决

05-Git常用高阶操作:reset/rebase/cherry-pick/merge冲突解决

前言

做开发三年,Git 命令只会addcommitpush三件套?这在团队协作里可不够用。当你需要撤销提交、整理分支历史、或者把某个功能单独摘到另一个分支时,resetrebasecherry-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.java

4.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 冲突解决的三条原则

  1. 看懂再改:理解两段代码各自的意图,别盲目保留一端
  2. 改完测试:解决冲突后必须跑一遍编译和测试,确保逻辑没有断裂
  3. 小步合并:频繁同步主干分支,减少长期分叉带来的大面积冲突

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 就不会再让你头疼。