80 lines
5.9 KiB
Markdown
80 lines
5.9 KiB
Markdown
---
|
||
id: devops.git.interview
|
||
title: Git 面试题
|
||
type: interview
|
||
domain: devops
|
||
stack: [git]
|
||
tags: [Git, 分支, 撤销, 面试]
|
||
level: basic
|
||
related: [devops.git.branch-commit, devops.git.merge-rebase-recovery]
|
||
week: [15, 16]
|
||
---
|
||
|
||
# Git 面试题
|
||
|
||
## 基础
|
||
|
||
### 【基础】`git merge` 和 `git rebase` 有什么区别?
|
||
|
||
**参考答案**:merge 把两条分支的历史合并,可能产生合并提交,保留真实的并行开发历史;rebase 把当前分支的提交在目标分支最新提交之后重新应用,历史变成一条直线,但会生成新的提交哈希。公共分支不要 rebase,个人功能分支可以用 rebase 同步主干、整理提交。
|
||
|
||
**追问**:什么时候不能用 rebase?→ 已经推送到共享分支、别人基于它开发的提交不要 rebase,否则会改写别人依赖的历史。
|
||
|
||
### 【基础】`git revert` 和 `git reset` 有什么区别?
|
||
|
||
**参考答案**:revert 生成一个新提交来抵消指定提交的改动,不改写历史,适合撤销已推送的提交;reset 移动分支指针到指定提交,`--soft` 保留改动在暂存区、`--mixed` 保留在工作区、`--hard` 丢弃改动,会改写历史,只适合未推送的本地提交。
|
||
|
||
**追问**:已经推送的提交想撤销用哪个?→ 用 `git revert` 生成一个反向提交,不改写历史;`reset` 后强推会影响其他人。
|
||
|
||
### 【基础】`git fetch` 和 `git pull` 的区别?
|
||
|
||
**参考答案**:fetch 只下载远程的最新提交,更新远程跟踪分支(如 `origin/main`),不改动本地分支;pull = fetch + merge(或配置为 rebase),会把远程改动合并到当前分支。习惯先 fetch 看清楚差异再决定合并方式。
|
||
|
||
**追问**:`git pull --rebase` 有什么好处?→ 本地提交会接在远程最新提交之后,避免产生多余的合并提交,历史更清晰。
|
||
|
||
### 【基础】你们用什么分支模型?
|
||
|
||
**参考答案**:按真实情况回答,并说明原因。例如 GitHub Flow:main 始终可发布,功能分支通过合并请求、CI 和评审后合并,合并后自动部署测试环境。有固定版本周期的(App、私有化交付)用 Git Flow 的 release / hotfix 分支。持续部署成熟的团队用主干开发 + 特性开关。
|
||
|
||
**追问**:分支模型怎么选?→ 持续部署的团队适合主干开发(短期功能分支 + 快速合并);有固定版本发布周期的适合 Git Flow 类的发布分支模型。
|
||
|
||
## 进阶
|
||
|
||
### 【进阶】误执行了 `git reset --hard`,提交还能找回吗?
|
||
|
||
**参考答案**:只要曾经提交过就能找回。`git reflog` 记录了 HEAD 的每次移动,找到 reset 之前的提交哈希,`git reset --hard 哈希` 或 `git branch 新分支 哈希` 恢复。删除的分支同理。但从未提交、也没有 stash 的工作区改动无法通过 reflog 找回。
|
||
|
||
**追问**:reflog 会一直保留吗?→ 不会,默认未引用的记录大约 30 天、可达的 90 天后被清理(可配置),而且 reflog 只在本地。
|
||
|
||
### 【进阶】`--force` 和 `--force-with-lease` 有什么区别?
|
||
|
||
**参考答案**:`--force` 无条件用本地覆盖远程分支;`--force-with-lease` 只有当远程分支仍然是你上次获取时的状态才覆盖,如果别人在这期间推送了新提交就会拒绝,避免误覆盖同事的工作。改写过历史的个人分支推送时应该用后者。
|
||
|
||
**追问**:强推前还应该做什么?→ 先 `git fetch` 并确认远程没有别人的新提交;重要分支应该设置保护,禁止强推。
|
||
|
||
### 【进阶】什么时候用 cherry-pick?有什么注意事项?
|
||
|
||
**参考答案**:需要把某个提交单独应用到另一个分支时,例如热修复同时带到 release 和 main、把某个修复回移到旧版本。注意 cherry-pick 会生成新的提交(不同哈希),如果之后两个分支合并,可能出现重复改动或冲突;大量 cherry-pick 说明分支策略可能有问题。
|
||
|
||
**追问**:cherry-pick 后两个分支有相同修改的不同提交,合并时会冲突吗?→ 内容相同通常能自动合并,但历史上是两个不同的提交;修改有差异时可能冲突。
|
||
|
||
## 场景题
|
||
|
||
### 【场景题】有人把密钥提交到了远程仓库,怎么处理?
|
||
|
||
**参考答案**:第一步立即作废并轮换密钥、检查是否被滥用;然后删除文件并加入 `.gitignore`;视情况用 `git filter-repo` 清理历史并强制推送,通知所有人重新克隆,联系平台清理缓存;最后加上提交前和 CI 中的密钥扫描。清理历史不能代替轮换。详见[热修复和密钥误提交场景](scenarios/hotfix-and-leaked-secret.md)。
|
||
|
||
**追问**:从历史中删除了密钥,还需要做什么?→ 必须吊销(轮换)这个密钥,因为它可能已经被克隆或抓取;然后检查是否有异常使用记录。
|
||
|
||
### 【场景题】线上版本有紧急 bug,但 main 上已经有很多未发布的功能,怎么修?
|
||
|
||
**参考答案**:从线上版本的标签或 release 分支拉热修复分支,修复并补测试,走 CI 后合入 release 分支发布补丁版本;再把修复 cherry-pick 或合并回 main,保证下个版本包含修复。不要从 main 拉分支修复,否则会把未测试的功能带上线。
|
||
|
||
**追问**:修复之后 main 上怎么办?→ 把修复合并回 main(或 cherry-pick),避免下一次发布时问题重新出现。
|
||
|
||
### 【场景题】合并时遇到大量冲突,怎么处理?
|
||
|
||
**参考答案**:先理解双方各自改了什么(`git log`、`git diff` 看两边的提交),逐个文件解决,不要一键全选某一方;解决后运行测试;冲突太多时可以和对方一起解决。根本上要避免长期不合并的分支:功能分支保持短小、经常同步主干。
|
||
|
||
**追问**:怎么减少冲突?→ 小步提交、频繁合并主干;避免同时大规模格式化;冲突较多的文件按模块拆分。
|