Git
Git 通过提交保存项目快照,通过分支引用组织开发历史;日常命令都可以放回工作区、暂存区和仓库这三个位置理解。
1. Git 保存对象和引用
一次提交会记录根目录快照、父提交、作者和提交说明。文件内容、目录结构与提交分别保存为不同对象,并按内容计算标识;相同内容可以复用同一个对象。
分支只是一个可移动的引用,指向该分支最新的提交。HEAD 通常指向当前分支,因此创建新提交时,Git 会生成提交对象,再把当前分支向前移动。切换分支则会改变 HEAD,并让工作区与目标提交匹配。
理解这两个事实后,分支不再是一份完整目录副本,提交也不等于“保存一组差异”。差异是比较两个快照得到的结果。
2. 三个区域描述修改所处的位置
Git 日常操作涉及三个主要区域:
- 工作区:磁盘上正在编辑的文件。
- 暂存区:下一次提交准备包含的快照,也叫 index。
- 本地仓库:已经创建的提交与其他对象。
git add 把选定内容写入暂存区,git commit 根据暂存区创建提交。一个文件可以同时存在“已暂存的修改”和“暂存后继续编辑的修改”,因此排查状态时先看 git status,必要时分别使用 git diff 与 git diff --staged。
git status
git diff
git diff --staged
git add src/App.java
git commit -m "Handle empty input"
3. 分支用于组织提交历史
创建分支只会新增一个引用,成本很低。功能开发通常从稳定分支创建短生命周期分支,完成后通过合并或变基整合。
git switch -c feature/payment-timeout
git switch main
git merge feature/payment-timeout
远程跟踪分支保存上次同步时看到的远程状态。git fetch 更新这些引用,不会直接修改当前工作区;git pull 通常等于 fetch 后再 merge 或 rebase,因此执行前要知道项目采用哪一种历史策略。
4. Merge 和 rebase 生成不同历史
merge 保留两条开发线,并在需要时创建合并提交。它不会改写已有提交,适合已经共享的历史。
rebase 会把当前分支的提交逐个重放到新的基点,得到内容相近但标识不同的新提交。它可以让历史保持线性,但会改写提交身份。已经被其他人基于其继续开发的公共提交,不应随意 rebase 后强制推送。
选择方式时遵循团队约定,并看历史是否已经共享。两者都可能产生冲突,区别在于冲突出现的过程和最后的提交图。
5. 冲突需要按最终意图解决
发生冲突时,Git 只能指出同一区域存在无法自动合并的改动,不能判断业务上应该保留哪一份。处理步骤是:
- 使用
git status找到冲突文件。 - 阅读共同基点与两侧修改,整理出最终内容。
- 运行测试或构建,确认组合后的行为。
git add标记已解决,再继续 merge 或 rebase。
不要把“接受 ours”或“接受 theirs”当成固定答案。rebase 过程中两个名称的视角还容易与直觉相反,直接检查最终文件更可靠。
6. 撤销命令先区分目标区域
不同撤销命令处理的对象不同:
git restore <file>:恢复工作区文件。git restore --staged <file>:把内容移出暂存区,保留工作区修改。git reset:移动分支或调整暂存区;--hard还会覆盖工作区,使用前必须确认目标。git revert <commit>:创建一个反向提交,不改写已有历史,适合撤销已经共享的提交。
判断命令前先回答两个问题:要撤销的是工作区、暂存区还是提交;相关提交是否已经推送并被他人使用。
7. 团队协作需要可审查的提交
一次提交应表达一个完整、可解释的改动,并保持能够构建或测试。提交说明写清行为变化,不记录机械操作。提交前检查 diff,避免把生成文件、密钥、调试日志或无关格式化混入修改。
需要整理本地未共享历史时,可以使用交互式 rebase 合并、拆分或改写提交。进入共享分支后,优先通过新提交修正问题,避免给协作者制造重复解决历史的成本。
8. 常见问题
8.1 git fetch 和 git pull 有什么区别
fetch 只从远程下载对象并更新远程跟踪分支,当前分支与工作区不变。pull 会在 fetch 后继续把远程变化整合到当前分支,具体使用 merge 还是 rebase 取决于参数和配置。
8.2 提交后发现漏了一个文件怎么办
如果提交尚未共享,可以暂存文件后使用 git commit --amend 更新最后一次提交;如果已经共享,通常创建新的修正提交更安全。决定依据是改写历史是否会影响其他协作者。
9. 面试题
9.1 git merge 和 git rebase 有什么区别,什么时候使用
出现公司:拼多多、百度
考察重点
- 提交图、合并提交和重放提交。
- 提交标识变化与共享历史的风险。
- 冲突处理和团队分支约定。
相关内容:第 3 节“分支用于组织提交历史”至第 7 节“团队协作需要可审查的提交”。
参考回答
merge 把两个分支的历史连接起来,必要时创建一个拥有两个父提交的合并提交,不会改写原有提交。rebase 把当前分支的提交重放到新基点,每个重放后的提交都会获得新的标识,最终历史通常更线性。
本地、尚未共享的功能分支可以 rebase 到最新主线,便于整理提交;已经共享并被他人依赖的历史优先 merge,或只通过新提交修正。选择还要服从团队规范。无论使用哪一种方式,冲突都要按最终业务意图解决并重新验证,不能只看提交图是否整齐。