Git 实操高频

共 17 题
📑 题目列表 17 题
#
★★★

1. git merge 与 git rebase 的选择决策中团队协作分支与个人特性分支分别适用哪种?黄金法则是什么?

请说明 git merge 与 git rebase 的选择决策:团队协作分支与个人特性分支分别适用哪种?黄金法则是什么?

  • merge 与 rebase 的差异
  • 协作分支与特性分支的适用
  • 黄金法则

团队协作分支(共享、已推送)应使用 merge,以保留真实历史拓扑、避免重写他人依赖的提交;个人特性分支(未共享)可用 rebase,把开发过程中的零碎提交整理成线性历史,再合入主干。黄金法则是:不要 rebase 已共享的分支,因为 rebase 会重写提交 SHA,导致他人历史冲突、无法正常 pull。决策的原则是"是否共享"——共享用 merge,私有可用 rebase。

选择本质是"历史真实性 vs 历史整洁性"的权衡。merge 保留开发过程的真实分叉,rebase 让历史线性可读。但 rebase 的代价是重写历史,破坏已共享者。工程上常见做法:个人分支上 rebase 整理,共享主干用 merge 或 squash merge,严格遵守黄金法则。

#
★★★

2. Interactive Rebase 工作流中如何用 git rebase -i 进行 squash、reword、reorder、split 操作整理提交历史?autosquash 与 fixup 的配合使用?

请说明 Interactive Rebase 工作流:如何用 git rebase -i 进行 squash、reword、reorder、split 操作整理提交历史?autosquash 与 fixup 如何配合?

  • interactive rebase 的操作
  • 各操作的具体用法
  • autosquash 与 fixup 配合

git rebase -i <base> 打开 todo 列表,列出待整理的提交,每个提交前可标注操作:squash 与前一提交合并(合并 message)、fixup 合并但丢弃 message、reword 修改 message、reorder 调整顺序(拖动行顺序)、drop 删除、edit 暂停以便分拆(split)或修改。split 的实现是:在 edit 处用 git reset HEAD^ 取消暂存,再用 git add -p 分批暂存并多次 git commit 拆成多个提交。autosquash(git rebase -i --autosquash)会自动把 fixup!/squash! 前缀的提交归位到对应原提交后并合并,配合 git commit --fixup=<sha> 使用。

interactive rebase 是"提交历史保洁"的核心工具。squash/fixup 归并零碎提交,reword 规范化 message,reorder 调整逻辑顺序,split 把大提交拆小。autosquash 让"整理"半自动化,避免手动调整。工程上应在功能分支上整理后再合入,操作前用 reflog 兜底。

#
★★★

3. git bisect 定位回归缺陷的完整流程中如何设定 good/bad 边界、自动化 bisect run 脚本、处理不可构建的中间提交(skip)?

请说明 git bisect 定位回归缺陷的完整流程:如何设定 good/bad 边界、自动化 bisect run 脚本、处理不可构建的中间提交(skip)?

  • bisect 的 good/bad 设定
  • bisect run 自动化
  • 处理不可构建提交(skip)

git bisect 用二分查找定位引入缺陷的提交。流程:git bisect start 开始,git bisect bad 标记当前缺陷提交,git bisect good <sha> 标记一个已知良好的旧提交,git 自动二分检出中间提交,测试后分别标记 git bisect good/git bisect bad,直到精确定位。自动化可用 git bisect run <script>,脚本判定提交好坏(以非零退出码表示坏、零表示好)。若中间提交不可构建/无法测试,用 git bisect skip 跳过该提交,git 会跳过此点继续二分。

bisect 的价值是把"回归定位"从人工排查变成对数(log n)次构建。设定准确的 good/bad 边界与可判定的脚本是前提。skip 处理"不可构建"提交很关键,否则 bisect 会误判。工程上应保证每个提交可构建(原子提交),并写好可自动判定的测试脚本,使 bisect run 高效可靠。

#
★★★

4. git reflog 的「时间旅行」恢复中误删分支、错误 reset --hard、rebase 中断后如何用 reflog 找回提交并重建分支,恢复的边界是什么?

请说明 git reflog 的「时间旅行」恢复:误删分支、错误 reset --hard、rebase 中断后如何用 reflog 找回提交并重建分支,恢复的边界是什么?

  • reflog 的原理
  • 各场景的恢复步骤
  • 恢复边界

reflog 记录 HEAD 与分支引用的历史移动,可用于"时间旅行"恢复。误删分支时,用 git reflog 找到分支最后一次指向的提交,用 git branch <name> <sha> 重建;错误 git reset --hard 后,用 reflog 找到 reset 前的提交并 git reset --hard <sha> 恢复;rebase 中断或误操作后,用 reflog 找到 rebase 前的分支位置恢复。恢复边界是:提交必须仍存在(未被 GC 清除),且 reflog 记录未过期(默认约 90 天)。一旦提交被 GC,或 reflog 过期,则无法恢复。

reflog 是 git 撤销的"保险"。关键洞察是 git 的操作(reset、rebase、branch -d)大多不立即删除对象,只是移动引用,reflog 保留旧引用。工程上应养成"危险操作前记下原位置"的习惯,误操作后第一时间用 reflog 恢复,避免提交被 GC。

#
★★

5. git cherry-pick 与 git revert 的典型使用场景中热修复回合入主干与撤销已发布提交?

请说明 git cherry-pick 与 git revert 的典型使用场景:热修复回合入主干与撤销已发布提交?

  • cherry-pick 的场景
  • revert 的场景
  • 两者的区别

git cherry-pick <sha> 精确地把单个提交的改动应用到当前分支,典型场景是把 hotfix 分支的修复提交"回合入"主干,或把某个提交移植到其他分支。git revert <sha> 生成一个"反向提交"来撤销某个已发布提交的改动,典型场景是撤销已推送到共享分支/已发布的提交——因为它新产生一个提交而非重写历史,不会破坏已共享的历史。两者区别:cherry-pick 是"复制改动",revert 是"撤销改动"。

cherry-pick 适合"把某个修复带过来",revert 适合"安全地撤销已发布的东西"。revert 不重写历史,因此是撤销共享提交的安全方式;cherry-pick 则用于跨分支移植。工程上应记住:撤销已发布提交用 revert(而非 reset/rebase),移植修复用 cherry-pick。

#
★★

6. 误提交敏感信息(密钥)后的正确处置流程中为什么仅 revert 不够,需要 filter-repo + 轮换密钥?

请说明误提交敏感信息(密钥)后的正确处置流程:为什么仅 revert 不够,需要 filter-repo + 轮换密钥?

  • 误提交密钥的风险
  • 为什么 revert 不够
  • filter-repo 与密钥轮换

误提交密钥后,仅 revert 不够,因为 revert 只是"撤销后续提交",密钥仍留在 git 历史中(reflog、旧提交、clone 出去的用户副本),可被任何人从历史中提取。正确流程:1) 立即轮换/撤销密钥(在服务端把密钥作废,这是最关键的一步,因为历史无法彻底抹除);2) 用 git filter-repo(或 git filter-branch)重写历史,把密钥从历史中移除;3) 强推清理后的历史并通知协作者;4) 检查密钥是否已被泄露(如日志、公开仓库扫描)。核心是"revert 删不掉历史,密钥必须轮换"。

密钥泄露的本质是"历史中永久存在",因此唯一可靠的补救是轮换密钥。filter-repo 能把密钥从历史中移除,但无法移除已 clone 出去的副本,且重写历史会破坏共享分支。工程上应把"先轮换密钥"作为第一优先级,再清理历史,并考虑用 secret scanning 工具预防。

#
★★

7. cherry-pick vs rebase 的选择中何时用 cherry-pick 精确摘取单个提交?cherry-pick 导致的重复提交和冲突如何处理?

请说明 cherry-pick vs rebase 的选择:何时用 cherry-pick 精确摘取单个提交?cherry-pick 导致的重复提交和冲突如何处理?

  • cherry-pick 与 rebase 的差异
  • cherry-pick 的适用场景
  • 重复提交与冲突处理

cherry-pick 用于"精确摘取单个/多个提交"到当前分支,不改变源分支,适合跨分支移植特定修复;rebase 用于"把一串提交整体重放到新基线",更适合整理个人分支。选择时:只想带某个提交到别处用 cherry-pick,想把整个分支平移/整理用 rebase。cherry-pick 可能产生重复提交——同一改动在多个分支各有一份(不同 SHA),需注意后续合并可能冲突;冲突处理与常规合并相同:解决冲突后 git cherry-pick --continue

cherry-pick 与 rebase 都"复制提交",但粒度与目的不同:cherry-pick 针对单个提交、rebase 针对整个分支。重复提交是 cherry-pick 的固有副作用(同一改动重复存在于多个分支),合并时可能"空/重复"冲突,需用 git cherry-pick --abort--continue 处理。工程上应明确移植边界,避免重复提交过多。

#
★★

8. git worktree 并行开发中如何在不 clone 的情况下同时检出多个分支进行开发/测试/hotfix?worktree 的生命周期管理和常见陷阱?

请说明 git worktree 并行开发:如何在不 clone 的情况下同时检出多个分支进行开发/测试/hotfix?worktree 的生命周期管理和常见陷阱?

  • worktree 的原理
  • 并行开发用法
  • 生命周期与陷阱

git worktree add <path> <branch> 可以在同一个仓库下为多个分支创建独立的 working tree,无需多次 clone,即可并行开发、测试、处理 hotfix。每个 worktree 共用同一仓库对象库,但含独立的工作区与索引。生命周期管理:git worktree list 查看、git worktree remove <path> 移除(需先清理该 worktree 的改动)、git worktree prune 清理已删除目录的残留。常见陷阱:同一分支不能同时被两个 worktree 检出(会报错)、worktree 中的分支删除前需先移除 worktree、临时目录删除后需 prune 清理元数据。

worktree 的价值是"一个仓库、多工作区",避免多次 clone 的存储与同步成本,适合并行任务与紧急 hotfix。但要管理好生命周期(add/remove/prune),避免残留元数据与分支占用。工程上应明确 worktree 的用途与清理规则,避免混乱。

#
★★

9. .gitignore 的边界中已跟踪文件(tracked file)为何不受 .gitignore 影响,如何强制忽略与恢复跟踪?

请说明 .gitignore 的边界:已跟踪文件(tracked file)为何不受 .gitignore 影响,如何强制忽略与恢复跟踪?

  • .gitignore 的作用对象
  • 已跟踪文件为何不受影响
  • 强制忽略与恢复跟踪

.gitignore 只作用于未跟踪文件(untracked),对已跟踪文件(tracked,已加入索引)无效——因为 git 已跟踪其内容,忽略规则不会排除已跟踪文件。若想强制忽略已跟踪文件,需先 git rm --cached <file>(从索引移除但保留磁盘文件),再加入 .gitignore,之后修改不再被跟踪。恢复跟踪:git rm --cachedgit add <file> 重新加入,或删除 .gitignore 中对应规则。

理解"tracked vs untracked"是关键。很多误以为 .gitignore 能忽略所有文件,实则只对新文件生效。强制忽略已跟踪文件需先将其移出索引。工程上应避免把易变文件(如环境变量、密钥、构建产物)提交进仓库,一旦提交便需 --cached 处理。恢复跟踪则反向操作。

#
★★

10. Git 的工作区、暂存区与版本库(working tree / index / object store)三层模型中 git add / reset 在各层之间的移动语义?

请说明 Git 的工作区、暂存区与版本库(working tree / index / object store)三层模型:git add / reset 在各层之间的移动语义?

  • 三层模型
  • git add 的语义
  • git reset 的语义

Git 有工作区(working tree)暂存区(index/staging area)、**版本库(object store)**三层。git add 把工作区的内容写入暂存区(并生成 blob 对象);git commit 把暂存区内容固化到版本库(生成 commit)。git reset 有多种模式,用于在三层间移动:git reset --soft <commit> 只移动 HEAD,不改暂存区与工作区;git reset(默认 --mixed)移动 HEAD 并把暂存区回退到该 commit,但保留工作区;git reset --hard 同时重置 HEAD、暂存区、工作区,丢弃未提交改动。git reset -- <file> 只把该文件从暂存区移回工作区(相当于"取消暂存")。

三层模型是理解 git 操作的基础。git add 是"工作区→暂存区",git reset 是"版本库/暂存区→工作区"的回退。--soft/--mixed/--hard 三档分别决定重置范围。工程上常用 git reset --hard 丢弃改动、git reset 取消暂存,需注意 --hard 会丢失工作区改动(可用 reflog 找回)。

#
★★

11. git log --graph 与分支可视化中--graph、--oneline、--all 组合如何快速理解仓库拓扑与合并历史?

请说明 git log 的分支可视化:--graph、--oneline、--all 组合如何快速理解仓库拓扑与合并历史?

  • 各参数的作用
  • 组合使用
  • 理解拓扑

git log --graph --oneline --all 组合可快速可视化仓库拓扑:--graph 用 ASCII 图形绘制分支分叉与合并连线,--oneline 每提交一行(短哈希 + 消息),--all 显示所有分支(含远端)的提交。三者组合能以紧凑的图形展示提交历史、分支分叉、合并节点,帮助快速理解"何时分叉、何时合并、各分支关系"。可加 --decorate 显示分支/tag 标签,加 --simplify-by-decoration 精简。

git log 的图形化是理解仓库拓扑的利器。--graph 让合并历史"可见",--oneline 让信息紧凑,--all 覆盖全部分支。工程上排查分支关系、评审合并历史、回答问题"这个功能合到哪了"时,此组合非常高效。可结合 --after/--before 按时间过滤。

#
★★

12. git submodule 与 git subtree 的取舍中外部依赖锁定、更新流程与团队协作的差异?

请说明 git submodule 与 git subtree 的取舍:外部依赖锁定、更新流程与团队协作的差异?

  • submodule 与 subtree 的机制
  • 依赖锁定与更新
  • 团队协作差异

git submodule 在仓库中嵌入指向另一个仓库具体 commit 的指针(记录在 .gitmodules 与 gitlink),克隆父仓库时需 git submodule update --init 拉取,更新需在子仓库内单独 pull 并提交父仓库的指针更新。git subtree 则把子仓库的历史合并进主仓库(作为普通目录),无需额外指针,更新用 git subtree pull 合并。取舍:submodule 精确锁定子仓库版本、保持仓库独立,但操作复杂、协作需额外 init/update、易踩坑;subtree 把依赖并入主仓库、clone 即完整、无额外命令,但历史膨胀、目录被"合并"进主历史、更新产生合并。

选择取决于"依赖隔离"与"使用便利"。submodule 适合"锁定精确版本、保持子仓库独立"(如内部共享库),但团队协作成本高;subtree 适合"希望 clone 即用、依赖不强隔离"的场景,但会引入子仓库历史。工程上应权衡锁定粒度、更新频率与团队习惯。

#
★★

13. 大规模仓库的 Git 性能方案中浅克隆(shallow clone)、部分克隆(partial clone)、稀疏检出(sparse checkout)在 monorepo 中的适用边界与团队协作影响?

请说明大规模仓库的 Git 性能方案:浅克隆(shallow clone)、部分克隆(partial clone)、稀疏检出(sparse checkout)在 monorepo 中的适用边界与团队协作影响?

  • 三种方案的原理
  • 适用边界
  • 团队协作影响

大规模 monorepo 的性能优化方案:浅克隆git clone --depth 1)只取最近 N 个提交,减少历史体积,但缺少历史(无法 bisect 完整历史、fetch 未来提交需加深);部分克隆git clone --filter=blob:none)只取元数据,按需下载 blob,减少仓库体积,但后续操作可能因按需下载增加网络往返;稀疏检出git sparse-checkout set)只检出指定子目录,减少工作区体积,但元数据仍完整。三者可组合。适用边界:浅克隆适合 CI 快速获取最新状态(不需历史);部分克隆适合"仓库大但只需部分内容";稀疏检出适合"只需工作区部分目录"。团队协作影响:浅克隆部分功能受限(如某些操作需完整历史),部分克隆与稀疏检出对协作影响较小,但需 CI 一致性配置。

三种方案分别针对"历史体积、仓库体积、工作区体积"。工程上应根据 CI 与开发需求组合:CI 常结合浅克隆 + 稀疏检出快速构建,开发可用部分克隆 + 稀疏检出。要注意浅克隆对需要完整历史的流程(如 bisect 全历史)不适用,部分克隆/稀疏检出需团队统一配置以避免不一致。

#

14. git stash、reflog、bisect 三个"救急"命令的典型场景?

请说明 git stash、reflog、bisect 三个"救急"命令的典型场景?

  • stash 的场景
  • reflog 的场景
  • bisect 的场景

git stash 用于临时保存工作区/暂存区的未提交改动,以便切换分支或处理其他事,之后 git stash pop 恢复,典型场景是"改到一半要紧急切换分支/处理 hotfix"。git reflog 用于"时间旅行"恢复,找回误删分支、错误 reset、rebase 中断的提交,是撤销事故的兜底。git bisect 用于二分定位引入回归的提交,是"代码突然坏了"时快速定位出问题提交的利器。

三个命令是 git 的"救急三件套":stash 救"临时把手头事放一放",reflog 救"误操作/丢东西",bisect 救"不知道哪次改动引入的 bug"。它们分别解决"暂存工作、恢复历史、定位回归"三类高频危机。工程上应熟练掌握这三个命令以应对突发状况。

#

15. Git 提交签名(GPG/SSH)的配置与验证中如何配置 commit signing?GitHub/GitLab 的 verified badge 机制?SSH signing 相比 GPG 的便利性?

请说明 Git 提交签名(GPG/SSH)的配置与验证:如何配置 commit signing?GitHub/GitLab 的 verified badge 机制?SSH signing 相比 GPG 的便利性?

  • commit signing 的配置
  • verified badge 机制
  • SSH signing 与 GPG 对比

提交签名(commit signing)用于验证提交确实来自声明者。GPG 方式:生成 GPG 密钥对,git config --global user.signingkey <key>git config --global commit.gpgsign true,提交时用私钥签名;GitHub/GitLab 通过将公钥关联到账号,对签名提交显示 "Verified"(已验证)badge。SSH signing 方式:把 SSH 公钥配置为 signing key,git config --global gpg.format sshuser.signingkey ~/.ssh/id_ed25519.pub,提交时用 SSH 私钥签名。SSH signing 相比 GPG 的便利性在于:多数开发者已有 SSH 密钥(无需新生成 GPG 密钥对),且配置更简单、与现有 SSH 基础设施复用。

verified badge 让"提交是否可信"可验证,提升供应链安全。GPG 历史悠久但密钥管理繁琐;SSH signing 复用现有 SSH 密钥,配置简便,近年来被 GitHub 等支持。工程上应让团队(尤其维护者)开启签名,提升提交可信度,并根据团队基础设施选择 GPG 或 SSH。

#

16. git config 的 core.autocrlf / core.eol 与 .gitattributes 的协同中跨平台换行符(CRLF/LF)规范化的优先级?

请说明 git config 的 core.autocrlf / core.eol 与 .gitattributes 的协同:跨平台换行符(CRLF/LF)规范化的优先级?

  • core.autocrlf 与 core.eol
  • .gitattributes 的优先级
  • 跨平台换行符规范化

core.autocrlf(Windows 常用 true/false)与 core.eol(lf/crlf/native)通过 git config 控制换行符转换,但它们是本地配置,随机器而异。.gitattributes 中的 text/eol 属性是仓库级规则,优先级高于本地 config,能保证所有协作者一致。规范化优先级:.gitattributestext=eol 规则 > core.autocrlf/core.eol 本地配置。推荐做法:用 .gitattributes 明确按文件类型指定换行符(如 *.js text eol=lf),并提交入库,让跨平台协作统一换行符,避免"假 diff"。

跨平台换行符问题的根源是本地 config 不一致。.gitattributes 作为仓库级、可提交的规范,能让所有协作者统一,是更可靠的方案。工程上应优先用 .gitattributes 声明换行符规则,而不依赖各机器上的 core.autocrlf 设置,从而彻底避免 CRLF/LF 混乱。

#

17. GitLab CI 的 extends/include 复用机制,与 Git 分支/标签规则结合定义多环境流水线?

请说明 GitLab CI 的 extends/include 复用机制:与 Git 分支/标签规则结合定义多环境流水线?

  • extends 与 include 的机制
  • 与分支/标签规则结合
  • 多环境流水线

GitLab CI(.gitlab-ci.yml)提供两种复用机制:include把外部文件(local/project/remote/template)的配置合并进当前流水线,适合共享公共 job 与全局配置;extends 在 job 之间继承已有 job 的配置(extends: .base-job),可覆盖字段,适合在脚手架上定制。结合分支/标签规则(rules: if: $CI_COMMIT_BRANCHonly/except 指向分支或 tag)可定义多环境流水线:如 main 分支触发生产部署、release/* 触发预发布、tag 触发发布、feature 分支只跑测试。这样同一套 YAML 通过 include 复用公共部分、用 extends 定制 job、用 rules 分流到不同环境。

include 实现"文件级复用"(公共模板),extends 实现"job 级复用"(配置继承),rules 实现"环境分流"。三者结合让多环境流水线 DRY 且清晰。工程上应把公共 job 通过 include 抽成模板,用 extends 定制各环境差异,用 rules 按分支/tag 触发对应环境,避免在每个环境重复粘贴配置。