
“你平时用 Git 吗说说 merge 和 rebase 的区别。”这大概是技术面试里出现频率最高的一道 Git 题。我参加过不少面试也坐在面试官那一边问过别人一个直观感受是Git 这个话题几乎每个岗位都会问但很少有人能答得系统。多数人停留在“会用 add、commit、push、pull”的层面一聊到 reset 的三种模式、rebase 的原理、detached HEAD 是怎么回事就开始含糊。这篇文章不打算按“面经题库”的方式罗列几十道题再给标准答案。那种碎片化背诵对面试帮助有限因为面试官真正想看的不是你知道多少命令而是你遇到仓库状态混乱时有没有清晰的恢复思路。所以我把面试中最常出现的 Git 问题重新组织了一下按“必问题、情境题、原理题、易错题”几个维度拆开讲每一道都给出我认为比较完整的回答路径也会穿插一些实际踩坑经验。如果你是准备面试的开发者这篇文章可以作为 Git 部分的系统复习提纲如果你已经在带团队、经常帮同事收拾 Git 烂摊子也可以看看其中对“部分撤销”“rebase 中断恢复”的理解是不是和你的实践经验一致。1. 面试官为什么总爱从 Git 问起它的考察重点不在命令很多候选人觉得 Git 面试题就是考命令背诵这是误解。Git 在面试里的定位其实很特殊它既是协作工具又是代码管理思维的试金石。我记得有一次面一个简历上写着“精通 Git”的候选人我问他“你的分支上已经提交了三次现在发现第一次提交里有一个不该提交的配置文件但后面两次提交都依赖这个文件存在你怎么处理”他愣了一下然后说“那我 reset 回去改完再重新提交”。这不算错但显然没有考虑过 reset 之后远端分支会怎样、其他同事已经拉过这个分支该怎么办。面试官问 Git通常想确认三件事第一你有没有真正在团队协作场景里用过 Git而不是自己一个人瞎 commit。单人开发时什么习惯都能容忍但多人协作时分支管理、冲突解决、提交历史的整洁度都会直接影响团队效率。第二你对仓库的理解是“命令的堆砌”还是“对象的操作”。Git 本质上是一个有向无环图上的节点操作理解这一点的人遇到意外情况能自己推理出恢复方法不理解的人只能靠百度搜索“git 撤销 commit”。这两类人面对同样的问题表现完全不同。第三你处理事故的冷静程度。面试里经常会出现“我不小心把分支删了 / 我把 rebase 搞到一半想放弃 / 我提交到了错误的分支”这类情境题。Git 的强大之处在于绝大多数操作都可恢复面试官想看你能不能逻辑清晰地一步步找回状态。所以这篇文章不会教你背命令而是带着你理解每个命令背后的设计逻辑。理解了逻辑面试官换着花样问你都能接住。2. 高频必问题暂存区、merge 与 rebase、reset 三种模式这一节集中讲三道出现频率最高的题每一道都给完整的回答骨架。2.1 Git 的三种区域模型工作区、暂存区、版本库“说说 Git 的工作区、暂存区、版本库有什么区别”这通常是 Git 面试的开始题简单但不代表容易答好。工作区就是你电脑上能看到的目录你在这里修改文件。暂存区也叫索引区是你执行git add之后文件进入的中间区域它记录的是“下一次提交将包含哪些内容”。版本库则是git commit之后生成的提交历史每个提交对应仓库的一个快照状态。很多人会忽略一个关键点暂存区里存的不是文件副本而是文件内容对应的对象引用。你git add一个文件时Git 会把这个文件的内容生成一个 blob 对象并把路径和对象 ID 的对应关系写入索引。所以“把文件加入暂存区”更准确的说法是“把当前文件内容登记到下一次提交的清单里”。有个高频追问是“修改了已暂存的文件后git add之前和之后的暂存区分别是什么状态”答案是第一次git add后暂存区保存的是那一刻的文件快照之后你再修改文件工作区内容和暂存区内容就不一致了只有再次git add新修改才会更新到暂存区。git status里“Changes to be committed”和“Changes not staged for commit”两个列表分别对应的就是暂存区和暂存区与工作区之间的差异。这道题要答出深度最好顺带提一句git diff的三个用法git diff比较工作区和暂存区git diff --cached比较暂存区和版本库git diff HEAD比较工作区和版本库。能把这三个 diff 的含义分清楚面试官就会确认你是真的懂暂存区模型而不是背概念。2.2 merge 与 rebase不只是“谁的历史更干净”的问题光说“merge 保留历史rebase 使历史线性”其实只答对了一半。面试官更在意的是你能不能把两条路的代价和适用场景讲清楚。merge 的逻辑是“分叉历史的汇合”。当你的分支和主干都有新的提交merge 会在两个分支的分叉点之后找到共同祖先把双方的变化合并到一起生成一个新的 merge commit。这个提交有两个父提交它能完整保留“这件事是并行发生的”这一事实。代价是历史会出现分叉和合并节点主干图的复杂度会上升。rebase 的逻辑是“把当前分支的提交一个个摘下来按顺序接到目标分支的最新位置”。比如你在 feature 分支上提交了三次此时主干多了两个新提交git rebase main做的事是找到 feature 和 main 的共同祖先在共同祖先之后的 feature 提交会被保存为补丁然后在 main 的最新位置上依次重新应用这些补丁。结果是 feature 的历史变成了在 main 最新提交之后接着排图的走向是单线的。面试里最值得展开的是安全性问题如果 feature 分支只有你自己在用rebase 完全没问题还能让合并进主干时历史非常清爽。如果 feature 分支已经多人共享且已经 push 到远程重写历史就会导致其他人本地分支的提交“凭空消失”因为他们的本地提交基于旧历史。黄金法则是对已经 push 并且多人协作的分支不要使用 rebase 重写历史。一个很好的补充回答是“如果我把 rebase 做到一半发现冲突太多想放弃怎么办”答案是git rebase --abort它会回到 rebase 之前的状态。但如果 rebase 已经完成并且你后悔了可以借助 reflog 找回原来的分支位置。能说出这条恢复链路就比单纯背概念高一个档次。2.3 reset 的三种模式soft、mixed、hard 到底动了什么git reset大概是面试中被误解最深的命令。它有三个参数--soft、--mixed默认、--hard区别在于回退时动了哪些区域。用最简单的话来说git reset --soft HEAD~1只移动 HEAD 和分支指针暂存区和工作区都不动。效果是“把最近一次提交撤销成已暂存状态”。如果你想重新编辑提交信息或者把最近的几个提交合并成一个这个模式很合适。git reset --mixed HEAD~1移动指针同时把暂存区重置为和回退后版本一致但工作区保持不动。这是默认模式效果是“撤销提交并且把暂存区清掉但你的修改还在工作区里”。所以git reset HEAD~1之后文件看起来是“已修改未暂存”的状态。git reset --hard HEAD~1指针、暂存区、工作区全部回到目标提交的状态。这是最危险的操作因为工作区里未提交的修改会直接消失。面试很容易在这里挖坑“我用git reset --hard HEAD~1把提交撤销了还能找回来吗”答案是能只要你执行 reset 前记住或能查到原来的提交哈希。Git 会通过 reflog 记录 HEAD 指针的每次移动。git reflog能看到所有历史操作记录找到你 reset 之前的提交哈希后用git reset --hard hash或者git cherry-pick hash就能把提交找回来。这也是“Git 里其实没有真正删除”这句话的最好注脚。另外注意区分git reset和git revertreset 是移动分支指针到历史某处会改写历史revert 是生成一个“反做”的新提交不改写历史。在多人共享分支上想撤销某个提交应该用 revert 而不是 reset因为 revert 不会导致其他人本地同步时出现历史错位。3. 情境题实战那些“操作失误”类问题的完整应对思路面试进入中段后面试官通常会抛几个“事故场景”考察你在真实项目中的应变能力。这一节整理了三道高频场景题重点是从排查路径的角度去回答而不是只给出最终命令。3.1 commit 信息写错了怎么办这几乎是最常见的小事故。有两种情况如果提交还没 push 到远程直接git commit --amend修改最近一次提交信息就行。amend 的本质是“用一个新的提交替换掉原来的提交”新提交的父节点和原提交相同只是内容和提交信息可能有变化。注意 amend 会改变提交哈希所以已 push 的提交不要用这种方式。如果提交已经 push另一个团队里常见的做法是本地 amend 修改信息然后git push --force-with-lease覆盖远程分支。--force-with-lease比--force安全它在强制推送前会检查远程分支是否还是你上次拉取时的状态如果远程已经有别人推送的新提交就会拒绝覆盖避免意外冲掉同事的代码。这个场景回答里最容易被面试官认可的是你主动提到“为什么用 force-with-lease 而不是 force”因为这体现了你对多人仓库操作风险的意识。3.2 把本该提交到 A 分支的提交提交到了 B 分支项目里的典型场景你正在 feature/login 分支上开发临时被喊去修一个线上 bug你忘了切回 dev 分支直接在当前分支上 commit 了修复代码。推荐的恢复路径记录当前分支名和提交哈希确保能找回原始提交。把当前分支切回到 bug 修复应该所在的分支git switch dev。在 dev 上把刚才那个提交拾取过来git cherry-pick 提交哈希。回到原来误提交的 feature/login 分支把那个提交从分支历史中移除git reset --hard HEAD~1前提是该提交尚未被其他人拉取。注意第 4 步要小心如果用reset --hard把提交从 feature/login 删掉但同时你已经 cherry-pick 了一份到 dev那这个提交相当于“转移”成功了。这个方案的关键在于 cherry-pick 创建的是内容相同但哈希不同的新提交两边互不影响。还有一种更贴近实际的情况feature/login 上的提交不止一个你只想把最后一个误提交的“线上修复”拿出来。那就不适合 reset 整个分支而是用git cherry-pick单独摘取再在 feature/login 上用git revert出来一个反操作提交或者用交互式 rebase 把那一笔从历史里改写掉。哪种方案好取决于这个分支是否已经多人协作。3.3 rebase 过程中出现大量冲突怎么安全处理rebase 的冲突和 merge 的冲突在解决思路上类似但有一个很重要的区别rebase 是一提交一提交地重放你可能需要连续解决多轮冲突。比如你的分支有 5 个提交rebase 后每个提交都可能和主干产生冲突Git 会停在第一个冲突处你解决完git add后执行git rebase --continue它再重放下一个提交可能又产生新的冲突。这个体验很磨人但核心原则是按顺序一处处解决别想着跳过去。实际项目里遇到大量冲突时我会先评估冲突的性质再做决定如果冲突只是同一批文件反复出现通常是公共配置类文件可以考虑把这个文件在重放过程中的修改合并逻辑梳理清楚。Git 的 rererereuse recorded resolution机制会记住你之前对同一冲突的处理结果后续自动应用。git config --global rerere.enabled true开启后长时间 rebase 的冲突解决效率会明显提升。如果冲突是结构性的大改比如整个模块重写了继续逐个解决可能非常低效不如放弃 rebase改用 merge让合并引擎一次性计算共同祖先和双方全部改动通常反而更容易处理。所以面试时如果答“rebase 冲突太多就直接 abort”只能算及格。更好的回答是先判断冲突分布和规模规模小就逐个解决并用 rerere 加速规模大就考虑git rebase --abort然后改成git merge同时说明为什么 merge 在这种场景下更合适。4. “撤销”相关问题的完整回答从 soft 到 cherry-pick 的灵活组合Git 面试题里“撤销”是范围最大、追问最深的一类。我把面试官常问的几个方向和推荐回答整理在这里你可以对照自己的掌握程度查漏补缺。4.1 只想撤销某个文件的部分修改而不是整个文件这是git checkout -- file和git restore file涉及的场景但面试问的往往更细“我只想恢复文件里某几行怎么办”推荐思路如果修改还没暂存用git diff查看具体改动然后手动把不要的行改回去。如果修改涉及多个文件想精确回退某个文件的全部修改但保留其他文件的修改用git restore file可以把该文件恢复到和暂存区或 HEAD一致的状态。如果只是想丢弃某个 hunk 的修改可以用交互式暂存git add -p的逆操作思路或者借助编辑器里的 Git 插件做逐块还原。这个问题的考察点是“你是否清楚 Git 里最小粒度的操作可以到行级”。实际开发中我经常遇到同事提交了一堆文件里面混着调试日志、无用注释正确处理方式就是用git add -p交互式选择要提交的 hunk而不是一股脑全 add 进去。4.2git revert撤销的是“改动”不是“时间”回答跳过的问题在于很多候选人不理解 revert 生成的是什么。Revert 生成一个方向相反的新提交它记录的是“把某次提交的改动反向应用回来”。所以原提交和新提交都会留在历史里不会被抹掉。面试官常追问“如果一个提交已经被 revert 了后来又想把原提交的改动重新应用回来怎么办”答案是再 revert 一次那个 revert。这么做看起来很绕但确实是对已推送分支最安全的恢复方式。另一个选择是git cherry-pick 原提交哈希但它会直接把原提交的改动应用一遍可能和 revert 后的状态冲突需要额外处理。4.3 用 reflog 找回“丢失”的提交git reflog是 Git 面试里最应该重点掌握但是被提到频率最低的命令。它记录的是 HEAD 的每一次移动包括 reset、rebase、checkout、commit 等操作。我自己的一个真实踩坑经历是之前做历史清理时用git reset --hard把分支指错了位置丢失了一个已经写了不少内容的功能分支。当时第一反应是慌后来冷静下来打开git reflog按照操作时间找到丢失提交的哈希用git branch feature-name hash重建分支所有提交全部回来了。面试里展示自己对 reflog 的理解时可以这样说“reflog 是有时效的默认 90 天内有效并且只在本地仓库维护不会同步到远程。所以远程分支上如果发生了 reset 或 force push别人本地的 reflog 也可能保留旧引用但这不是可靠的恢复手段正规做法是尽快从远端拉取或联系操作者确认。”能主动提到 reflog 的边界条件面试官会觉得你确实用它处理过事故而不是背过命令。5. 底层原理题提交对象、分支本质、HEAD 与 detached HEAD技术面试进行到深水区后Git 的底层原理题就会出现。这类题目考验的不是记忆而是你对 Git 对象模型的理解程度。如果理解了 Git 的数据结构很多上层命令的行为都能自己推出来。5.1 commit、tree、blob 之间的关系Git 的仓库本质上是一个内容寻址的文件系统。核心对象有三种blob文件内容不包含文件名、路径、权限这些元信息。tree目录结构记录了目录下有哪些文件和子目录以及每个文件对应的 blob 哈希。commit一次提交的快照指向一个 tree 对象同时记录父提交哈希、作者、提交者、提交信息等。一个提交不是“从上次提交之后改了多少”而是一个完整的目录快照。只不过 Git 在存储时会对未变化的文件做去重多个 commit 会共享相同的 tree 和 blob 对象所以仓库不会因为每次提交都存全量快照而无限膨胀。面试时如果被问“Git 为什么能这么高效地管理版本”从对象复用和内容寻址的角度回答就很扎实每次提交都是新 tree但底层 blob 可以从对象库里复用只有变化的内容才产生新对象。5.2 分支到底是什么这是 Git 入门最常见的一个认知误区很多人以为分支是“目录的副本”或者“代码的平行宇宙”。准确说分支只是一个指向某个提交的可移动指针。创建分支git branch feature本质上只是新增了一个指针指向当前 HEAD 所在的提交没有复制任何文件。当你切到新分支并提交时这个指针才会向前移动指向新的提交对象。理解“分支是指针”之后就能自然推导出删除分支只是删掉了指针提交对象如果仍被其他引用或 reflog 引用着就还能找回来。这也是“误删分支可恢复”的根本原因。5.3 HEAD 和 detached HEAD 状态HEAD 是“当前所在位置的指针”通常指向某个分支名而不是直接指向提交。当你git checkout feature时HEAD 指向的是 feature 这个 ref而 feature 再指向某个提交。detached HEAD 指的是 HEAD 不再指向分支而是直接指向某个具体的提交。进入这个状态最常用的命令是git checkout hash或git switch --detach hash。在这个状态下你新做的 commit 会形成一个游离的提交链如果没有用分支或标签引用它它看起来会“丢失”——这也是为什么经常有人误操作后找不到自己刚提交的东西。脱离状态下的正确做法是如果想保留新提交立刻创建一个分支指向它git switch -c new-branch。创建分支后新分支指针会接住当前的游离提交HEAD 也重新处于正常状态。明白了这一点面试官问“detached HEAD 下提交会不会丢”你就能准确回答对象不会立刻被垃圾回收但没有引用时很难通过普通历史命令找到只要创建分支或标签引用它它就和普通提交没区别。为了加深理解我用一个类比来帮助记忆提交对象像游戏里的存档记录分支像你给存档起的快捷方式名HEAD 是“当前读档位置”。detached HEAD 就是你没有给当前存档起名字游戏还在继续下次打开列表你可能找不到这个档位但存档文件还在硬盘上。6. 容易被追问的易错点快进合并、远程分支、force push面试官经常在听完一个完整回答后抽一个细节追问下去。这一节列的三个点是面试中出现频率很高的“下一问”。6.1 快进合并fast-forward和非快进合并的区别git merge并不总是产生 merge commit。当目标分支包含当前分支的所有提交、没有分叉时Git 会直接移动当前分支指针到目标分支的最新提交这种就是 fast-forward。它不产生新的提交历史保持线性。如果你希望即使没有分叉也保留合并记录可以用git merge --no-ff强制生成一个 merge commit。团队里常见的做法是合并 feature 分支到主干时用--no-ff这样主干上能看到清晰的功能合并点回溯时更容易定位某个功能是哪个合并引入的。面试里提到“合并策略可以有 ff、no-ff、squash 的选择”然后说明各自的适用场景就比单纯答“merge 会产生合并提交”准确得多。6.2 远程分支和本地分支的对应关系远程分支在本地表现为origin/main这种形式它保存的是“你最后一次与远程同步时远程分支的状态”不会实时更新。git fetch只更新远程跟踪分支不改变本地工作区git pull是 fetch 之后再做一次 merge或 rebase取决于配置。面试里如果被问“ fetch 和 pull 区别”标准回答之外能补一句“pull 本质是 fetch merge”通常会得到肯定。还有个高频追问“为什么有时候 push 会被拒绝”原因是远程分支有你本地没有的提交直接 push 会导致远程历史被覆盖。解决方案是先把远程变更合并pull 或 fetchmerge或变基pull --rebase再重新推送。能把这个拒绝机制说清楚面试官就会确信你在多人仓库里操作过。6.3 什么时候可以用 force push这是一个“没有唯一答案”的问题面试官想听的是你的风险判断。我的经验是只要分支是个人专用的、未共享的或者团队明确约定该分支允许重写就可以 force push——但要使用--force-with-lease而不是裸--force。共享主干分支、多人同时开发的长期分支原则上禁止 force push。因为别人已经基于旧历史做了新提交你一覆盖别人的提交就会在远程消失。出现“必须修改已推送提交”的需求时优先用 revert 保留历史确实需要改写也一定要在团队群同步时间窗口让相关同事先备份或先处理完自己的提交。这个问题回答得好说明你有“操作前先考虑影响面”的习惯。面试官也常用它来判断候选人能不能在团队中承担关键分支的管理职责。7. 快问快答清单Git 面试中常见的短问题及参考方向除了上面展开的深度题面试里还有一批短平快问题。我把它们的参考方向整理成表方便快速自查。问题核心要点常见误区git pull和git fetch的区别pull fetch merge或 rebase以为 pull 是“更新代码”而 fetch 是“看一眼”git stash的作用临时保存工作区和暂存区的修改之后可以 pop 回来以为 stash 只能存一次忽略它可以存多个git diff和git diff --cached前者对比工作区和暂存区后者对比暂存区和版本库混淆两个比较对象.gitignore不生效怎么办文件已跟踪时 ignore 无效需要先git rm --cached移除跟踪直接改 ignore 却忽略“已跟踪”的前提git log怎么查看某文件的历史git log -- file可以只看影响该文件的提交只想到git log -p看全部差异如何查看某个提交改了什么git show hash查看提交的完整 diff用git log找半天如何临时切到别的分支但不丢失当前修改git stash或git switch -c新分支携带修改强制 checkout 导致修改冲突如何撤销一个已 push 的 merge 提交git revert -m 1 merge提交哈希-m 指定保留哪个父提交直接 reset 后 force push导致团队历史混乱上面这些题不会单独作为大考察点但经常作为“追问”出现。比如候选人刚聊完 merge 和 rebase面试官顺势问一句“那 pull 的时候你更倾向 merge 还是 rebase”这是在试探你有没有明确的个人工程偏好。这时候不要打太极给出明确选择并说明理由比如“个人开发分支我会用 pull --rebase 保持线性共享集成分支我选择 merge 以保留完整集成交互信息”会比“都可以看情况”更有说服力。8. 给准备面试的人Git 复习路线的最后建议很多人在面试前会刷一遍 Git 命令大全但效果往往不好因为命令和实际场景是脱节的。我发现比较有效的复习方式是围绕事故恢复来构建知识网络而不是按命令字母顺序背。具体做法是自己在本地开一个测试仓库尝试完成以下操作序列每一步都先猜结果再执行验证创建三个文件的初始提交然后分别修改文件一、暂存文件二、删除文件三观察git status和git diff的输出说清楚每个区域的状态。基于 main 创建一个 feature 分支在 feature 上提交两次然后切回 main 新增一次提交对 feature 执行 rebase 和 merge 各一次对比历史图。在 feature 分支上连续提交三次尝试用git reset --soft、--mixed、--hard回退一次分别观察工作区和暂存区状态的变化。操作一次git checkout hash进入 detached HEAD在那里提交一次再用新建分支的方式接住游离提交。用git reflog找回一个“被删除”的分支并重建它。整个练习大概一个小时但效果远比背二十个命令强。因为你在操作中会直观体会到“指针移动”“对象复用”“引用即分支”这些底层概念面试官后续不管怎么换角度追问你都能回到原理上作答。最后分享一个我自己的心得面试里 Git 题目答得好不好不取决于你记得多少命令而取决于你能不能把“仓库当前状态”用一句话说清楚。每次操作前先问自己“我现在在哪个分支、工作区和暂存区分别和 HEAD 有什么关系”长期保持这种习惯后你会发现 Git 不再是需要查资料的工具而是一种清晰的思维方式。面试时你也能自然地表达出“我知道我的每一个操作会对仓库产生什么改变”这会比任何背诵出来的答案都更有说服力。