上篇写git基础操作的时候,被问到最多的不是怎么commit,而是合并冲突怎么办、文件改乱了怎么恢复、为什么push老要输密码。说实话,这才是git使用者的真实日常:命令谁都能敲,真正决定用得顺不顺手的是遇到问题后能不能快速定位原因。这篇就奔着这些高频问题来,我把平时帮同事排查过几十次的场景重新整理了一遍,适合已经会add、commit、push,但对合并、回退、免密、报错还心里没底的开发者。文章不按文档顺序讲命令,而是按“你真正会碰到的场景”来组织,每段都可以直接落地。
1. 分支合并与冲突处理:从 merge 到 rebase 的实战选择
1.1 动手前先定分支策略,这部分省不了
我见过不少小团队,仓库拉下来就一个main分支,所有人直接在上面提交。前两周还没事,等代码量上来,一次上线要回滚某个功能时,谁都不敢动了。没有分支策略的仓库,本质上是在用“信任”对抗“混乱”,短期看省事,长期看是给自己埋雷。
小团队落地不需要多复杂的分支模型,记住一条主线就行:main分支保持可发布状态,开发需求时从main拉出特性分支,写完经review后再合回main。这条规则能覆盖80%的场景。分支命名建议带上类型和主题,比如feature/order-export、fix/login-timeout、release/v1.2.0。别小看命名规范,它能让git branch列出的历史一眼可读,也让后续的自动清理脚本有规则可依。
动手写代码前,先确认自己在正确的分支上:
bash复制git checkout main
git pull origin main
git checkout -b feature/order-export
这套命令的含义是:先切到main,拉取远端最新代码,然后基于这个最新状态创建并切换新分支。很多人图省事直接git branch feature/xxx再git checkout feature/xxx,效果一样,但-b一步到位更少出错。记住一点:从旧代码上拉分支,后面合回main时大概率会撞上别人改过的文件,这不是git的问题,是你拉分支的时机不对。
1.2 merge 和 rebase 的区别:一次说清
merge和rebase是git里最容易被拿来对比的两个命令,也是被误解最多的。两者的目标都是“把另一条分支的提交整合进当前分支”,但处理历史的方式完全不同。
git merge会创建一个新的合并提交(merge commit),把两条分支的历史“焊接”在一起。优点是历史真实,能看到哪些提交来自哪个分支,回滚时也能整体回滚到合并前。缺点也很明显:merge commit多了之后,历史图看起来像一张蜘蛛网,尤其多人并行开发时,git log --graph基本没法看。
git rebase做的事情是“搬移提交”:把当前分支上独有的那几次提交摘下来,依次重放到目标分支的最新提交之后。这样一来,提交历史变成一条直线,干净清晰。代价是重放过程中会重写这些提交的哈希值,本质上等于“改写历史”。
所以在项目里的选型经验是:
- 公共分支之间整合代码,用
git merge,不修改公共历史,大家pull下来都会觉得踏实; - 自己维护的特性分支要同步主干的最新代码,用
git rebase,既能避免无意义的合并提交,又方便后续review; - 绝对不要对多人共用的公共分支执行
git rebase。一旦别人基于旧历史工作,你重写历史后,他下次pull会看到一堆莫名其妙的重复提交,甚至直接冲突到生无可恋。
具体操作上,两种命令都很简单:
bash复制# 在特性分支上同步main的最新代码
git fetch origin
git rebase origin/main
# 或者选择merge
git merge origin/main
如果rebase过程中发现冲突太多、处理不下去,不用硬扛,随时可以逃跑:
bash复制git rebase --abort
这个--abort命令是rebase的保命符:它会把一切恢复到rebase开始之前的状态。我处理过不少次同事把rebase做到一半想切回别的分支干活的场景,正确做法不是硬切,而是先--abort或者rebase --continue把当前流程结束再走。git的状态是全局的,中间状态没结束,切分支会失败或带出诡异现象。
1.3 冲突解决:不要无脑接受任何一边
先说透冲突的本质:git不是不知道“哪份代码正确”,而是它不打算替你猜。同一个文件的同一行,两边都有了不同版本的修改,git无法判断哪个是你的意图,只能把选择权交给你。
冲突发生时,git会在冲突文件里插入标记,长这样:
text复制<<<<<<< HEAD
这边是当前分支的内容
=======
这边是正在合并进来的内容
>>>>>>> feature/order-export
看到这个结构,正常处理流程是:
git status查看哪些文件冲突;- 打开冲突文件,逐段搜索
<<<<<<<; - 对每一段冲突,判断应该保留哪一边,或者两边都保留并做整合;
- 删除所有冲突标记,包括
<<<<<<<、=======、>>>>>>>三行; - 保存文件后执行
git add; - 如果是merge,直接
git commit;如果是rebase,执行git rebase --continue。
这里有个非常常见的误区:merge冲突解决完后,有人直接执行git commit,结果git提示“no changes”或者“nothing to commit”,当场就懵了。原因在于,git认为“冲突解决完成”的标记是文件被git add进暂存区,而不是文件内容修改完成。先git add,再git commit,顺序不能反。
再说说冲突内容的判断。我的习惯是打开冲突文件后,先不急着删标记,而是把两边的代码都看一遍,搞清楚它们的意图差异。常见的三种情况:
- 两边改了不同方法,只是位置相邻:两个都保留,去掉标记即可;
- 同一处逻辑两边都改了,但功能不同:需要找review的同学确认,保留哪个或合并两者;
- 一边是重构后代码,一边是旧代码:以重构方向为准,必要时查提交历史确认谁是“最新意图”。
处理完冲突别急着提交,先把测试跑一遍。我见过太多人冲突解决了、编译也过了,结果逻辑被拼错,上线后出问题。冲突解决是最需要冷静处理的场景,别省那几分钟测试时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 文件恢复与版本回退:restore、reset、reflog 的正确打开方式
2.1 三个区的状态先对号入座
git目录下每个文件都可以处于多种状态,深入理解之前,先记住一个简化的模型:文件会经过“工作区 → 暂存区 → 版本库”这三个位置。
工作区就是你本地看到的文件;暂存区是git add之后存放内容的中间区域,代表“下次提交要包含哪些文件”;版本库是git commit
