重新学习前端之Git

Git

一、Git 基础概念

1. Git 是什么?

定义 Git 是一个分布式版本控制系统(Distributed Version Control System),由 Linus Torvalds 于 2005 年创建,用于管理 Linux 内核开发。

原理

  • Git 以快照(Snapshot)的方式记录项目的每一次变更,而不是以差异(Delta)的方式
  • 每个提交(Commit)都包含一个指向项目完整快照的指针
  • 使用 SHA-1 哈希算法为每个对象生成唯一标识
  • 所有数据在本地都有完整副本,支持离线操作

核心架构

scss 复制代码
工作区 (Working Directory) → 暂存区 (Staging Area/Index) → 本地仓库 (Local Repository) → 远程仓库 (Remote Repository)

示例

bash 复制代码
# 初始化仓库
git init

# 查看当前状态
git status

# 添加到暂存区
git add .

# 提交到本地仓库
git commit -m "Initial commit"

# 推送到远程仓库
git push origin main

常见误区

  • 误区:Git 就是 GitHub → 正解:Git 是工具,GitHub 是基于 Git 的代码托管平台
  • 误区:Git 保存的是文件差异 → 正解:Git 保存的是文件快照
  • 误区:必须联网才能使用 Git → 正解:Git 是分布式系统,大部分操作可离线完成

2. HEAD、工作树和索引

定义

概念 说明
工作树 (Working Tree) 当前在磁盘上可见的文件,开发者实际编辑的文件
索引 (Index/Stage) 暂存区,记录即将被提交的变更,是一个中间状态
HEAD 指向当前所在分支或提交的指针,表示当前所在位置

原理

sql 复制代码
工作树  ←→  git add  ←→  索引  ←→  git commit  ←→  本地仓库
   ↑                                                            ↓
   ←←←←←←←←←←←←←←←←←←← git checkout/reset ←←←←←←←←←←←←←←←←←←←←

HEAD 的三种状态

bash 复制代码
# 1. HEAD 指向某个分支(正常状态)
HEAD -> main

# 2. HEAD 指向某个具体提交(分离 HEAD 状态)
HEAD detached at abc1234

# 3. HEAD 指向 ORIG_HEAD、MERGE_HEAD 等特殊引用
HEAD -> MERGE_HEAD

示例

bash 复制代码
# 查看 HEAD 指向
cat .git/HEAD

# 查看索引内容
git ls-files --stage

# 查看工作区与索引的差异
git diff

# 查看索引与最后一次提交的差异
git diff --cached

常见误区

  • 误区:索引就是仓库 → 正解:索引是暂存区,是工作区和仓库之间的中间状态
  • 误区:HEAD 总是指向最新提交 → 正解:HEAD 指向当前分支,分支才指向最新提交

3. Fork、Clone、Branch 区别

定义

概念 说明 层级
Fork 在服务器端复制别人的仓库到自己的账户下 服务器级别
Clone 将远程仓库完整下载到本地 远程 → 本地
Branch 同一仓库内的并行开发线 仓库内部

对比

对比维度 Fork Clone Branch
操作位置 远程服务器 远程 → 本地 本地仓库
独立性 完全独立的新仓库 原始仓库的副本 共享同一仓库历史
关系保持 与原仓库无自动关联 自动关联 origin 共享同一历史
使用场景 开源项目贡献 获取代码到本地 功能开发
权限要求 不需要原仓库权限 需要读取权限 需要仓库写入权限

选择策略

  • 参与开源项目 → Fork → Clone → Branch
  • 自己项目开发 → Clone → Branch
  • 同一项目不同功能 → Branch

示例

bash 复制代码
# Fork: 在 GitHub 网页上操作,点击 Fork 按钮

# Clone: 复制 Fork 后的仓库到本地
git clone https://github.com/your-username/project.git

# Branch: 创建新分支进行开发
git checkout -b feature/new-feature

二、Git 基础命令

4. git init

定义 git init 用于在当前目录初始化一个新的 Git 仓库,创建 .git 隐藏目录。

原理 执行后会在当前目录创建 .git 目录,包含:

  • HEAD - 指向当前分支
  • config - 仓库配置
  • objects/ - 存储所有 Git 对象
  • refs/ - 存储分支和标签引用
  • hooks/ - Git 钩子脚本

示例

bash 复制代码
# 初始化当前目录
git init

# 初始化并指定分支名
git init -b main

# 初始化裸仓库(无工作区)
git init --bare

常见误区

  • 误区:在已有仓库中再次 init 会破坏数据 → 正解:已存在仓库时 init 是安全的,不会覆盖已有数据
  • 误区:git init 会自动创建初始提交 → 正解:git init 只创建空仓库,需要手动添加文件并提交

5. git clone

定义 git clone 用于将远程仓库完整复制到本地,包括所有历史、分支和标签。

原理

  • 克隆时会建立与远程仓库的关联(默认命名为 origin)
  • 自动 checkout 默认分支(通常是 main/master)
  • 下载所有对象和引用

示例

bash 复制代码
# 基本克隆
git clone https://github.com/user/repo.git

# 克隆到指定目录
git clone https://github.com/user/repo.git my-project

# 只克隆最新一次提交(浅克隆)
git clone --depth 1 https://github.com/user/repo.git

# 克隆特定分支
git clone -b develop https://github.com/user/repo.git

# 使用 SSH 克隆
git clone git@github.com:user/repo.git

常见误区

  • 误区:git clone 只下载当前分支 → 正解:会下载所有分支的引用,但只 checkout 默认分支
  • 误区:浅克隆可以后续转为完整克隆 → 正解:可以使用 git fetch --unshallow 转换

6. git add

定义 git add 将文件的变更从工作区添加到暂存区(Index),为下一次提交做准备。

原理

  • Git 跟踪的是文件快照,git add 会将当前文件内容存入对象数据库
  • 暂存区是一个索引文件,记录哪些变更将被包含在下次提交中

示例

bash 复制代码
# 添加所有变更
git add .

# 添加指定文件
git add file.txt

# 添加指定目录
git add src/

# 交互式添加
git add -p

# 添加被修改和删除的文件(不包括新文件)
git add -u

# 添加新文件和修改的文件(不包括删除)
git add -A

常见误区

  • 误区:git add . 只添加新文件 → 正解:git add . 添加所有变更(新增、修改、删除)
  • 误区:add 后就无法修改文件 → 正解:可以再次 add 更新暂存区内容

7. git commit

定义 git commit 将暂存区的变更提交到本地仓库,创建一个新的提交对象。

原理

  • 提交对象包含:作者信息、提交者信息、提交信息、父提交指针、指向树对象的指针
  • 每次提交生成唯一的 SHA-1 哈希值
  • 提交是不可变的,修改提交会创建新的提交

示例

bash 复制代码
# 基本提交
git commit -m "feat: add user login feature"

# 跳过暂存区直接提交已跟踪的文件
git commit -am "fix: correct typo"

# 修改最后一次提交
git commit --amend -m "updated message"

# 空提交(创建新提交但不包含变更)
git commit --allow-empty -m "trigger CI build"

# 提交签名
git commit -S -m "signed commit"

提交信息规范(Conventional Commits)

xml 复制代码
<type>(<scope>): <subject>

<body>

<footer>

常见 type:

  • feat - 新功能
  • fix - 修复 bug
  • docs - 文档更新
  • style - 代码格式
  • refactor - 重构
  • test - 测试
  • chore - 构建/工具

常见误区

  • 误区:commit 后会推送到远程 → 正解:commit 只提交到本地仓库,需要 push 才会到远程
  • 误区:可以随时修改历史提交 → 正解:已推送的提交不应修改,会影响协作者

8. git push

定义 git push 将本地分支的提交推送到远程仓库。

原理

  • 推送前会检查远程分支的提交历史是否包含本地提交的祖先
  • 如果远程有新提交,需要先 pull 合并后才能 push

示例

bash 复制代码
# 推送到默认远程和分支
git push

# 推送到指定远程和分支
git push origin main

# 首次推送并建立追踪关系
git push -u origin feature-branch

# 强制推送(危险!)
git push --force-with-lease

# 推送所有分支
git push --all

# 推送标签
git push --tags

# 删除远程分支
git push origin --delete branch-name

常见误区

  • 误区:git push --force 是安全的 → 正解:--force 会覆盖远程历史,应使用 --force-with-lease
  • 误区:push 会自动推送所有分支 → 正解:默认只推送当前分支

9. git pull 与 git fetch

定义

命令 说明
git fetch 从远程仓库下载最新的提交和引用,但不合并到当前工作区
git pull 从远程仓库下载并自动合并到当前分支(= fetch + merge)

对比

对比维度 git fetch git pull
下载远程更新
自动合并
安全性 高(先审查再合并) 低(直接合并)
工作流程 两步操作 一步操作
推荐使用 ✅ 推荐 熟悉项目后使用

原理

bash 复制代码
# git pull 等价于:
git fetch origin
git merge origin/main

# 或者(使用 rebase 模式):
git fetch origin
git rebase origin/main

示例

bash 复制代码
# 获取远程更新但不合并
git fetch origin

# 查看所有远程分支
git branch -r

# 拉取并合并(默认)
git pull origin main

# 拉取并变基
git pull --rebase origin main

# 仅获取特定分支
git fetch origin main

选择策略

  • 新手或重要分支 → git fetch + 手动 git merge
  • 日常开发且无冲突风险 → git pull
  • 希望保持线性历史 → git pull --rebase

常见误区

  • 误区:fetch 会修改工作区 → 正解:fetch 只更新远程跟踪分支,不影响工作区
  • 误区:pull 总是安全的 → 正解:pull 会自动合并,可能产生冲突或混乱的历史

10. git status

定义 git status 显示工作区和暂存区的状态,包括已修改、已暂存和未跟踪的文件。

示例

bash 复制代码
# 查看状态
git status

# 简洁输出
git status -s

输出解读

ruby 复制代码
M  file.txt    # 已修改且已暂存
 M file.txt    # 已修改但未暂存
A  file.txt    # 新文件已暂存
D  file.txt    # 已删除且已暂存
?? file.txt    # 未跟踪的文件

11. git log

定义 git log 显示提交历史记录。

示例

bash 复制代码
# 基本日志
git log

# 单行简洁输出
git log --oneline

# 图形化显示分支合并
git log --oneline --graph --all

# 显示最近 n 条
git log -n 5

# 显示每次提交的变更统计
git log --stat

# 显示具体变更内容
git log -p

# 按作者过滤
git log --author="username"

# 按时间过滤
git log --since="2024-01-01" --until="2024-12-31"

# 搜索提交信息
git log --grep="feat"

# 查看某个文件的提交历史
git log --follow -- file.txt

12. git diff

定义 git diff 显示不同版本之间的差异。

示例

bash 复制代码
# 工作区 vs 暂存区
git diff

# 暂存区 vs 最近提交
git diff --cached

# 工作区 vs 最近提交
git diff HEAD

# 两个提交之间的差异
git diff commit1 commit2

# 两个分支之间的差异
git diff main..develop

# 查看具体文件的差异
git diff -- file.txt

13. git rm 和 git mv

定义

命令 说明
git rm 从工作区和暂存区删除文件,并记录删除操作
git mv 重命名或移动文件

示例

bash 复制代码
# 删除文件
git rm file.txt

# 仅从暂存区删除(保留工作区文件)
git rm --cached file.txt

# 递归删除目录
git rm -r dir/

# 重命名文件
git mv old.txt new.txt

14. Git 和 SVN 的区别

对比

对比维度 Git SVN
架构 分布式 集中式
版本号 基于哈希(SHA-1) 顺序数字(r1, r2, r3)
分支 轻量级,本地操作 重量级,目录复制
离线工作 完整支持 仅有限操作
速度 快(本地操作) 较慢(依赖网络)
存储方式 按快照存储 按差异存储
学习曲线 较陡峭 较平缓
适用场景 大型项目、开源协作 小型团队、集中管理

选择策略

  • 新项目 → 优先选择 Git
  • 需要管理大型二进制文件 → Git + LFS 或 SVN
  • 传统企业环境 → 根据团队习惯选择

三、暂存与现场保存

15. git stash

定义 git stash 将当前工作区的修改临时保存起来,恢复工作区到干净状态,方便切换分支。

原理

  • stash 实际上是创建了两个提交(一个保存工作区,一个保存暂存区)
  • stash 列表是一个栈结构,后进先出

使用场景

  1. 正在开发功能,需要紧急修复 bug
  2. 需要拉取远程更新但不想提交当前工作
  3. 需要切换分支但当前工作未完成

示例

bash 复制代码
# 保存当前修改
git stash

# 保存并添加描述
git stash save "WIP: user login feature"

# 或使用新版命令
git stash push -m "WIP: user login feature"

# 查看 stash 列表
git stash list

# 恢复最近一次 stash 并删除
git stash pop

# 恢复但不删除
git stash apply

# 恢复指定的 stash
git stash apply stash@{2}

# 删除指定 stash
git stash drop stash@{0}

# 清空所有 stash
git stash clear

# 从 stash 创建新分支
git stash branch new-branch stash@{0}

# 包含未跟踪的文件
git stash -u

常见误区

  • 误区:stash 是永久的 → 正解:stash 只是临时存储,可能被清理
  • 误区:stash 可以在分支间共享 → 正解:stash 是本地操作,不随 push 推送

四、分支管理

16. Git 分支概述

定义 Git 分支是指向提交的轻量级可移动指针。分支本质上是包含 40 个字符(SHA-1 哈希)的文件。

原理

  • 创建分支只是创建一个指向当前提交的新指针,成本极低
  • 切换分支会更新 HEAD 指向,并更新工作区文件
  • 每个分支独立演进,互不干扰

示例

bash 复制代码
# 创建分支
git branch feature-login

# 创建并切换
git checkout -b feature-login
# 或
git switch -c feature-login

# 查看所有分支
git branch
git branch -a  # 包括远程分支

# 切换分支
git checkout main
git switch main

# 删除分支
git branch -d feature-login     # 安全删除(已合并)
git branch -D feature-login     # 强制删除(未合并)

# 重命名分支
git branch -m old-name new-name

17. git checkout 与 git switch

定义

命令 说明
git checkout 多功能命令:切换分支、恢复文件、分离 HEAD
git switch Git 2.23+ 引入,专门用于切换分支(更安全)

对比

对比维度 git checkout git switch
功能范围 广泛(分支+文件) 专注(仅分支)
恢复文件 git checkout -- file 不支持
切换分支 git checkout branch git switch branch
创建分支 git checkout -b branch git switch -c branch
安全性 较低(可能误操作) 较高(用途明确)

选择策略

  • Git 2.23+ → 推荐使用 git switch 切换分支,git restore 恢复文件
  • 旧版本 → 使用 git checkout

18. 分支类型与命名规范

主分支(Main Branches)

分支 说明 命名
main/master 生产环境代码,始终可部署 mainmaster
develop 开发集成分支,包含最新开发成果 developdev

辅助分支(Supporting Branches)

分支类型 说明 命名规范 来源 合并到
功能分支 开发新功能 feature/xxx develop develop
发布分支 准备新版本发布 release/v1.x.x develop main + develop
热修复分支 紧急修复生产问题 hotfix/xxx main main + develop

分支命名规范

bash 复制代码
# 功能分支
feature/user-authentication
feature/123-add-login-page       # 关联 issue 编号

# 修复分支
fix/incorrect-login-validation
bugfix/456-fix-crash

# 发布分支
release/v1.2.0
release/2024-spring

# 热修复分支
hotfix/critical-security-patch
hotfix/v1.2.1

# 实验分支
experiment/new-ui-framework

最佳实践

  • 分支名使用小写字母,单词间用连字符分隔
  • 关联 issue/ticket 编号便于追溯
  • 及时删除已合并的分支
bash 复制代码
# 删除已合并的本地分支
git branch --merged | grep -v "\*" | xargs git branch -d

# 删除已合并的远程分支
git remote prune origin

五、合并与变基

19. git merge

定义 git merge 将两个或多个分支的历史合并在一起,创建一个新的合并提交。

合并类型

类型 说明 是否创建新提交
快进合并 (Fast-forward) 目标分支是当前分支的直接祖先
普通合并 (Recursive/Octopus) 两个分支有分叉,需要合并
禁止快进 (No-ff) 强制创建合并提交

示例

bash 复制代码
# 基本合并
git merge feature-branch

# 禁止快进合并(保留分支历史)
git merge --no-ff feature-branch

# 快进合并(如果可以)
git merge --ff feature-branch

# 不自动提交
git merge --no-commit feature-branch

# 使用 squash(压缩所有提交为一个)
git merge --squash feature-branch

原理图解

css 复制代码
# Fast-forward 合并
A---B---C (main)
         \
          D---E (feature)
          
# 合并后(fast-forward)
A---B---C---D---E (main, feature)

# No-ff 合并
A---B---C (main)
         \
          D---E (feature)
          
# 合并后
A---B---C-------F (main)
         \     /
          D---E (feature)

20. git rebase

定义 git rebase 将一个分支的提交"重新应用"到另一个分支的最新提交之上,创建线性历史。

原理

  • rebase 会复制提交并在新基础上重新创建
  • 原始提交不会被删除,但不再被分支引用
  • 适用于清理本地未推送的提交历史

示例

bash 复制代码
# 将当前分支变基到 main
git checkout feature
git rebase main

# 交互式变基
git rebase -i HEAD~3

# 变基到特定提交
git rebase --onto new-base old-base feature

# 继续变基(解决冲突后)
git rebase --continue

# 跳过当前提交
git rebase --skip

# 中止变基
git rebase --abort

交互式变基操作

bash 复制代码
git rebase -i HEAD~4

可执行的操作:

sql 复制代码
pick   - 保留该提交
reword - 保留提交但修改提交信息
edit   - 暂停以修改提交内容
squash - 将该提交合并到上一个提交
fixup  - 同 squash,但丢弃提交信息
drop   - 删除该提交

原理图解

css 复制代码
# 变基前
          A---B---C (feature)
         /
D---E---F---G (main)

# 变基后
                  A'--B'--C' (feature)
                 /
D---E---F---G (main)

21. merge 与 rebase 的区别

定义

操作 说明
merge 创建新的合并提交,保留完整的历史分支结构
rebase 重写提交历史,将提交移到新基础上,保持线性历史

对比

对比维度 merge rebase
历史记录 保留完整分支历史 重写历史,保持线性
提交数量 增加一个合并提交 不增加额外提交
可追溯性 高(知道何时合并) 较低(丢失分支信息)
安全性 高(不修改历史) 低(修改已存在提交)
冲突处理 一次解决所有冲突 可能需要多次解决
适用场景 公共分支合并 本地分支整理
协作友好 ✅ 适合共享分支 ❌ 不适合已推送分支

选择策略

场景 推荐 原因
合并到 main/develop merge --no-ff 保留功能开发的完整上下文
同步 main 到功能分支 rebase 保持功能分支历史清晰
整理本地提交 rebase -i 压缩、重组、修改提交
公共分支 merge 不修改他人可见的历史

最佳实践

bash 复制代码
# 黄金法则:永远不要对已推送到公共仓库的提交执行 rebase
# ✅ 好的做法
git checkout feature
git rebase main      # 在本地整理后再推送
git push origin feature

# ❌ 危险的做法
git rebase main      # 如果 feature 已被他人拉取
git push --force     # 会覆盖他人的历史

22. git cherry-pick

定义 git cherry-pick 从其他分支选择特定的提交应用到当前分支。

示例

bash 复制代码
# 应用单个提交
git cherry-pick abc1234

# 应用多个提交
git cherry-pick abc1234 def5678

# 应用一个范围的提交(不包含 start)
git cherry-pick start^..end

# 应用但不自动提交
git cherry-pick -n abc1234

# 保留原提交者信息
git cherry-pick -x abc1234

使用场景

  • 将热修复应用到多个版本分支
  • 从实验分支挑选有用的提交
  • 错误的分支上提交了,需要转移到正确分支

23. 选择性合并

定义 只合并其他分支的部分变更,而非整个分支。

方法

bash 复制代码
# 方法1:cherry-pick(推荐)
git cherry-pick commit-hash

# 方法2:checkout 单个文件
git checkout source-branch -- path/to/file

# 方法3:交互式变基后合并
git rebase -i source-branch
# 删除不需要的提交后 merge

# 方法4:使用 patch
git diff source-branch -- path/to/file > changes.patch
git apply changes.patch

六、冲突解决

24. 合并冲突

定义 当两个分支修改了同一文件的同一区域,Git 无法自动合并时产生的情况。

冲突标记

markdown 复制代码
<<<<<<< HEAD
当前分支的代码内容
=======
被合并分支的代码内容
>>>>>>> feature-branch

冲突解决步骤

bash 复制代码
# 1. 查看冲突文件
git status

# 2. 手动编辑冲突文件,解决冲突标记
# 删除冲突标记,保留需要的代码

# 3. 标记冲突已解决
git add resolved-file.txt

# 4. 完成合并
git commit

# 如果是 rebase 过程中的冲突
git add resolved-file.txt
git rebase --continue

25. 冲突解决策略

策略对比

策略 说明 适用场景
手动解决 编辑冲突标记,选择保留的代码 复杂冲突
使用 ours 完全保留当前分支的代码 确定当前分支正确
使用 theirs 完全保留被合并分支的代码 确定被合并分支正确
使用外部工具 使用 meld、kdiff3 等可视化工具 大规模冲突

示例

bash 复制代码
# 使用 ours 策略(保留当前分支)
git merge -s ours feature-branch

# 解决单个文件时使用 ours
git checkout --ours conflicted-file.txt
git add conflicted-file.txt

# 解决单个文件时使用 theirs
git checkout --theirs conflicted-file.txt
git add conflicted-file.txt

# 使用合并驱动(在 .gitattributes 中配置)
# *.txt merge=union  # 自动合并文本文件

冲突解决工具配置

bash 复制代码
# 使用 VS Code 作为合并工具
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# 使用 meld
git config --global merge.tool meld
git config --global mergetool.meld.cmd 'meld $LOCAL $MERGED $REMOTE --output $MERGED'

# 启动合并工具
git mergetool

26. 冲突解决最佳实践

  1. 频繁同步:经常 pull/rebase 主分支,减少冲突概率
  2. 小步提交:每次提交只做一件事,便于冲突定位
  3. 明确分工:团队成员负责不同文件或模块
  4. 及时沟通:多人修改同一文件前先沟通
  5. 使用 .gitattributes:为特定文件类型配置合并策略
bash 复制代码
# 示例 .gitattributes
# 二进制文件不进行合并
*.png binary
*.jpg binary

# 配置文件使用 ours 策略
*.xml merge=ours
*.json merge=ours

七、Git 工作流

27. Git 工作流概述

定义 Git 工作流是团队协作中使用 Git 的规范和流程,确保代码管理的有序性。

主要工作流类型

工作流 适用场景 复杂度
Git Flow 有固定发布周期的项目
GitHub Flow 持续部署的 Web 应用
GitLab Flow 需要环境分支管理
Forking Flow 开源项目

28. Git Flow

定义 Git Flow 是由 Vincent Driessen 提出的分支模型,适合有固定发布周期的项目。

分支结构

bash 复制代码
main (master)
  └── release/v1.x
        └── develop
              ├── feature/login
              ├── feature/dashboard
              └── hotfix/security-fix

工作流程

bash 复制代码
# 1. 初始化 Git Flow
git flow init

# 2. 开发新功能
git flow feature start user-auth
# 开发完成后
git flow feature finish user-auth

# 3. 准备发布
git flow release start 1.0.0
# 修复发布问题
git flow release finish 1.0.0

# 4. 热修复
git flow hotfix start critical-fix
git flow hotfix finish critical-fix

各分支说明

分支 生命周期 说明
main 永久 生产环境代码,只有发布和热修复会合并进来
develop 永久 开发集成分支,包含最新的开发成果
feature/* 临时 功能开发,从 develop 创建,合并回 develop
release/* 临时 发布准备,从 develop 创建,合并到 main 和 develop
hotfix/* 临时 紧急修复,从 main 创建,合并到 main 和 develop

优点

  • 清晰的分支结构和职责
  • 适合有版本发布计划的项目
  • 支持并行开发和发布

缺点

  • 分支结构复杂,学习成本高
  • 不适合持续部署
  • 发布流程较长

29. GitHub Flow

定义 GitHub Flow 是简化的工作流,适合持续部署的项目,只有 main 分支和功能分支。

工作流程

bash 复制代码
# 1. 从 main 创建功能分支
git checkout main
git pull origin main
git checkout -b feature/add-search

# 2. 开发并提交
git add .
git commit -m "feat: add search functionality"

# 3. 推送到远程
git push origin feature/add-search

# 4. 创建 Pull Request
# 在 GitHub 上创建 PR,进行 Code Review

# 5. 合并后部署
# PR 合并后自动部署到生产环境

规则

  1. main 分支的任何代码都是可部署的
  2. 新功能必须在独立分支上开发
  3. 通过 Pull Request 进行代码审查
  4. 审查通过后合并到 main
  5. 合并后立即部署

优点

  • 简单易懂
  • 适合持续集成/持续部署
  • 促进代码审查

缺点

  • 不适合多版本维护
  • 没有明确的发布流程
  • 不适合复杂的项目管理

30. GitLab Flow

定义 GitLab Flow 是 Git Flow 和 GitHub Flow 的折中方案,增加了环境分支。

分支结构

css 复制代码
main
  └── pre-production
        └── production

工作流程

  1. 在 main 上进行开发
  2. 将 main 合并到 pre-production 进行测试
  3. 将 pre-production 合并到 production 进行发布

变体:带环境分支

css 复制代码
feature branches → main → staging → production

31. Forking 工作流

定义 每个开发者 Fork 仓库到自己的账户,在本地开发后发起 Pull Request。

适用场景

  • 开源项目协作
  • 不需要给所有贡献者写权限
  • 需要严格的代码审查

工作流程

bash 复制代码
# 1. Fork 仓库(在 GitHub 上操作)

# 2. Clone Fork 的仓库
git clone https://github.com/your-username/repo.git

# 3. 添加上游仓库
git remote add upstream https://github.com/original-owner/repo.git

# 4. 创建功能分支
git checkout -b feature/my-feature

# 5. 开发并推送
git push origin feature/my-feature

# 6. 创建 Pull Request

# 7. 同步上游更新
git fetch upstream
git checkout main
git merge upstream/main

32. Pull Request 与 Merge Request

定义

概念 平台 说明
Pull Request (PR) GitHub 请求将分支的变更拉取到目标分支
Merge Request (MR) GitLab 请求将分支的变更合并到目标分支

PR/MR 流程

  1. 开发者创建功能分支并推送
  2. 在平台创建 PR/MR
  3. 自动触发 CI 检查
  4. 团队成员进行 Code Review
  5. 解决反馈问题
  6. 审核通过后合并

Code Review 最佳实践

  • 小批量提交审查(每次变更不超过 400 行)
  • 使用模板确保审查一致性
  • 设置至少一名审查者
  • 自动化检查(lint、测试)先行
  • 及时反馈,友好沟通

八、版本回退与重置

33. git reset

定义 git reset 将当前分支指针移动到指定提交,可选择性地修改暂存区和工作区。

三种模式

模式 分支指针 暂存区 工作区 说明
--soft 移动 不变 不变 变更保留在暂存区
--mixed 移动 重置 不变 变更保留在工作区(默认)
--hard 移动 重置 重置 所有变更丢弃

示例

bash 复制代码
# 软重置 - 回退提交,保留暂存
git reset --soft HEAD~1

# 混合重置 - 回退提交,变更回到工作区
git reset --mixed HEAD~1
git reset HEAD~1  # 默认模式

# 硬重置 - 完全回退,丢弃所有变更
git reset --hard HEAD~1
git reset --hard abc1234

# 重置特定文件到暂存区之前
git reset HEAD -- file.txt

# 只重置暂存区,不移动指针
git reset -- file.txt

图解

css 复制代码
# 当前状态
A---B---C---D (main, HEAD)

# git reset --soft HEAD~2
A---B---C---D
        ↑
       main, HEAD
       (C和D的变更在暂存区)

# git reset --mixed HEAD~2
A---B---C---D
        ↑
       main, HEAD
       (C和D的变更在工作区)

# git reset --hard HEAD~2
A---B (main, HEAD)
(C和D的变更完全丢弃)

34. git revert

定义 git revert 创建一个新的提交,该提交的内容是指定提交的反向操作,用于安全地撤销已推送的提交。

与 reset 的区别

对比维度 git reset git revert
历史记录 删除提交历史 保留完整历史
新提交 不创建新提交 创建新提交
安全性 低(修改历史) 高(追加历史)
适用场景 本地未推送的提交 已推送的公共提交
协作影响 影响他人 不影响他人

示例

bash 复制代码
# 撤销最后一次提交
git revert HEAD

# 撤销指定提交
git revert abc1234

# 撤销多个提交
git revert HEAD~3..HEAD

# 撤销但不自动提交
git revert -n abc1234

# 编辑提交信息
git revert -e abc1234

选择策略

  • 提交未推送 → git reset
  • 提交已推送 → git revert
  • 公共分支 → 只能用 git revert

35. git reflog

定义 reflog(Reference Log)记录 HEAD 和分支引用的所有变更历史,包括 reset、checkout 等操作。

原理

  • reflog 是本地操作,不会推送到远程
  • 默认保存 90 天
  • 可用于恢复误删的提交

示例

bash 复制代码
# 查看 reflog
git reflog

# 输出示例
# abc1234 HEAD@{0}: commit: add feature
# def5678 HEAD@{1}: reset: moving to HEAD~1
# ghi9012 HEAD@{2}: commit: fix bug

# 恢复到指定状态
git reset --hard HEAD@{2}
# 或使用提交哈希
git reset --hard def5678

# 查看特定分支的 reflog
git reflog show main

# 清理过期记录
git reflog expire --expire=now --all

恢复误删的提交

bash 复制代码
# 1. 查看 reflog 找到误删的提交
git reflog

# 2. 找到对应的提交哈希
# abc1234 HEAD@{3}: commit: important feature

# 3. 恢复
git cherry-pick abc1234
# 或
git reset --hard abc1234

36. 撤销操作汇总

场景 命令 说明
撤销最后一次提交(保留变更) git reset --soft HEAD~1 变更回到暂存区
撤销最后一次提交(保留到工作区) git reset HEAD~1 变更回到工作区
完全撤销最后一次提交 git reset --hard HEAD~1 丢弃所有变更
撤销已推送的提交 git revert HEAD 创建反向提交
撤销暂存 git reset HEAD file.txt 从暂存区移除
撤销文件修改 git checkout -- file.txt 恢复到最后一次提交
撤销所有未提交修改 git reset --hard 回到最后一次提交状态
恢复已删除的提交 git reflog + git reset 通过 reflog 找回

九、标签管理

37. Git 标签概述

定义 标签(Tag)用于标记重要的提交,通常用于发布版本。

标签类型

类型 说明 包含信息
轻量标签 (Lightweight) 指向提交的简单引用 仅标签名
附注标签 (Annotated) 完整的 Git 对象 标签名、作者、日期、消息、GPG签名

示例

bash 复制代码
# 创建轻量标签
git tag v1.0.0

# 创建附注标签(推荐)
git tag -a v1.0.0 -m "Release version 1.0.0"

# 创建带 GPG 签名的标签
git tag -s v1.0.0 -m "Signed release"

# 查看所有标签
git tag
git tag -l "v1.*"

# 查看标签详情
git show v1.0.0

# 在指定提交上打标签
git tag -a v0.9.0 -m "Beta release" abc1234

38. 标签推送与删除

bash 复制代码
# 推送单个标签
git push origin v1.0.0

# 推送所有标签
git push --tags

# 删除本地标签
git tag -d v1.0.0

# 删除远程标签
git push origin --delete v1.0.0
# 或
git push origin :refs/tags/v1.0.0

# 清理本地不存在的远程标签
git fetch --prune --prune-tags

最佳实践

  • 发布版本时使用附注标签
  • 遵循语义化版本规范(SemVer)
  • 及时推送标签到远程

十、Git Hook

39. Git Hook 概述

定义 Git Hook 是在特定 Git 事件发生时自动执行的脚本,存放在 .git/hooks/ 目录。

常用 Hook 类型

Hook 触发时机 用途
pre-commit 执行 git commit 之前 代码检查、格式化
commit-msg 提交信息创建后 提交信息格式校验
pre-push 执行 git push 之前 运行测试、阻止推送
post-commit 提交完成后 发送通知
pre-receive 服务器接收推送前 服务器端校验
post-receive 服务器接收推送后 触发部署

40. pre-commit Hook

示例:创建 pre-commit 脚本

bash 复制代码
#!/bin/sh
# .git/hooks/pre-commit

# 运行代码检查
npm run lint

# 如果检查失败,阻止提交
if [ $? -ne 0 ]; then
    echo "Lint check failed. Commit aborted."
    exit 1
fi

# 运行测试
npm test

if [ $? -ne 0 ]; then
    echo "Tests failed. Commit aborted."
    exit 1
fi

41. Husky 与 lint-staged

定义

工具 说明
Husky 简化 Git Hook 管理的工具,让 Hook 配置版本化
lint-staged 只对暂存的文件运行 lint,提高效率

配置示例

json 复制代码
// package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "pre-push": "npm test"
    }
  },
  "lint-staged": {
    "*.{js,ts}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{css,scss}": [
      "stylelint --fix"
    ]
  }
}

Husky v9+ 配置

bash 复制代码
# 安装
npm install husky --save-dev

# 初始化
npx husky init

# 添加 hook
echo "npm run lint" >> .husky/pre-commit

优势

  • Hook 配置随代码库版本控制
  • 只检查暂存文件,速度快
  • 自动修复可修复的问题

十一、Git 配置与优化

42. git config

配置层级

层级 范围 文件位置
--system 系统所有用户 /etc/gitconfig
--global 当前用户所有仓库 ~/.gitconfig
--local 当前仓库 .git/config

常用配置

bash 复制代码
# 设置用户信息
git config --global user.name "Your Name"
git config --global user.email "your@email.com"

# 设置默认编辑器
git config --global core.editor "code --wait"

# 设置默认分支名
git config --global init.defaultBranch main

# 设置 Pull 默认行为
git config --global pull.rebase true

# 设置颜色输出
git config --global color.ui auto

# 设置自动补全
git config --global core.autocrlf input  # Mac/Linux
git config --global core.autocrlf true   # Windows

# 查看配置
git config --list
git config --global --list

# 设置别名
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status
git config --global alias.ci commit
git config --global alias.lg "log --oneline --graph --all"

43. .gitignore

定义 .gitignore 文件指定哪些文件或目录应该被 Git 忽略。

规则示例

gitignore 复制代码
# 忽略所有 .log 文件
*.log

# 不忽略 important.log
!important.log

# 忽略整个目录
node_modules/
dist/
.env

# 忽略特定路径
logs/*.log
docs/notes/

# 忽略所有 .txt 文件,但保留 docs/ 下的
*.txt
!/docs/

# 忽略空目录(需要占位文件)
empty-dir/.gitkeep

# 忽略 IDE 配置
.vscode/
.idea/
*.swp

注意事项

  • .gitignore 只对未跟踪的文件生效
  • 已被跟踪的文件需要先从缓存移除
  • 可以使用全局 .gitignore
bash 复制代码
# 设置全局 .gitignore
git config --global core.excludesFile ~/.gitignore_global

# 对已跟踪文件生效忽略
git rm --cached file.txt

44. .gitattributes

定义 .gitattributes 文件为特定文件设置 Git 行为,如换行符处理、合并策略等。

示例

gitattributes 复制代码
# 统一换行符处理
* text=auto
*.sh text eol=lf
*.bat text eol=crlf

# 二进制文件
*.png binary
*.jpg binary
*.pdf binary

# 特定合并策略
*.xml merge=ours
package.json merge=ours

# 自定义 diff 驱动
*.pdf diff=pdf

# 不导出到 archive
.gitattributes export-ignore
tests/ export-ignore

45. SSH 密钥配置

bash 复制代码
# 生成 SSH 密钥
ssh-keygen -t ed25519 -C "your@email.com"

# 或(兼容旧系统)
ssh-keygen -t rsa -b 4096 -C "your@email.com"

# 启动 SSH Agent
eval "$(ssh-agent -s)"

# 添加密钥到 Agent
ssh-add ~/.ssh/id_ed25519

# 测试连接
ssh -T git@github.com

46. Git 别名

bash 复制代码
# 基本别名
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status
git config --global alias.ci commit
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'

# 图形化日志
git config --global alias.lg "log --oneline --graph --all --decorate"

# 查看变更统计
git config --global alias.ls "log --oneline --stat"

# 查看文件变更历史
git config --global alias.hist "log --oneline --graph --decorate"

# 撤销最后一次提交
git config --global alias.undo 'reset --soft HEAD~1'

47. Git 性能优化

大仓库优化

bash 复制代码
# 浅克隆
git clone --depth 1 <repo-url>

# 启用多线程
git config --global pack.threads 4

# 调整压缩级别
git config --global core.compression 0

# 预加载索引
git config --global core.preloadindex true

# 启用文件系统缓存
git config --global core.fscache true

定期维护

bash 复制代码
# 垃圾回收
git gc --prune=now

# 优化对象存储
git repack -a -d --depth=250 --window=250

# 清理远程跟踪分支
git remote prune origin

十二、Git LFS 与子模块

48. Git LFS (Large File Storage)

定义 Git LFS 是 Git 的扩展,用于管理大文件(如二进制文件、媒体文件),将大文件存储在远程服务器上,只在仓库中保存指针。

适用场景

  • 设计资源(PSD、AI 文件)
  • 视频、音频文件
  • 编译产物
  • 数据集

示例

bash 复制代码
# 安装 Git LFS
git lfs install

# 跟踪特定文件类型
git lfs track "*.psd"
git lfs track "*.mp4"

# 查看已跟踪的文件类型
git lfs track

# 提交 .gitattributes(LFS 配置存在于此)
git add .gitattributes

# 查看所有 LFS 文件
git lfs ls-files

# 推送 LFS 文件
git push origin main

# 拉取 LFS 文件
git lfs pull

49. Git 子模块 (git submodule)

定义 子模块允许将一个 Git 仓库作为另一个 Git 仓库的子目录,保持独立的提交历史。

示例

bash 复制代码
# 添加子模块
git submodule add https://github.com/user/library.git lib/library

# 初始化子模块
git submodule init

# 更新子模块
git submodule update

# 克隆包含子模块的仓库
git clone --recursive https://github.com/user/project.git
# 或分步
git clone https://github.com/user/project.git
cd project
git submodule update --init --recursive

# 更新所有子模块
git submodule update --remote

# 查看子模块状态
git submodule status

# 删除子模块
git submodule deinit lib/library
git rm lib/library

注意事项

  • 子模块记录的是特定提交,不是最新代码
  • 需要手动更新子模块
  • 替代方案:Git Subtree、包管理器

十三、Git 代理配置

50. Git 代理

bash 复制代码
# 设置 HTTP 代理
git config --global http.proxy http://127.0.0.1:1080
git config --global https.proxy http://127.0.0.1:1080

# 设置 SOCKS5 代理
git config --global http.proxy socks5://127.0.0.1:1080
git config --global https.proxy socks5://127.0.0.1:1080

# 仅为 GitHub 设置代理
git config --global http.https://github.com.proxy http://127.0.0.1:1080

# 取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy

十四、Git 常见问题

51. 常见操作问题

分离 HEAD 状态

bash 复制代码
# 查看当前状态
git status
# HEAD detached at abc1234

# 如果想保留修改,创建新分支
git checkout -b new-branch

# 如果想丢弃修改,回到分支
git checkout main

推送被拒绝

bash 复制代码
# 原因:远程有新提交
# 解决方案1:先拉取再推送
git pull --rebase origin main
git push

# 解决方案2:强制推送(慎用)
git push --force-with-lease

错误的提交信息

bash 复制代码
# 修改最后一次提交信息
git commit --amend -m "新的提交信息"

# 修改更早的提交信息
git rebase -i HEAD~3
# 将 pick 改为 reword

误删分支

bash 复制代码
# 通过 reflog 找回
git reflog
git branch recovered-branch abc1234

52. 版本管理理解

Git 的四种对象

对象类型 说明 存储内容
Blob 二进制大对象 文件内容
Tree 树对象 目录结构和文件名
Commit 提交对象 提交元数据和指向 tree 的指针
Tag 标签对象 指向 commit 的引用

提交对象结构

sql 复制代码
commit abc1234567890
tree def5678901234
parent 1234567890abc
author Developer <dev@email.com> 1234567890 +0800
committer Developer <dev@email.com> 1234567890 +0800

Commit message here

引用机制

css 复制代码
main ──→ commit C ──→ commit B ──→ commit A
HEAD ──→ main

十五、面试高频问答速查

基础概念

Q: Git 是什么? A: Git 是分布式版本控制系统,以快照方式记录项目变更,支持离线操作和完整的本地历史。

Q: Git 和 SVN 的区别? A: Git 是分布式的,SVN 是集中式的;Git 分支轻量,SVN 分支重量;Git 支持离线操作,SVN 依赖网络。

Q: HEAD、工作树、索引的关系? A: 工作树是可见文件,索引是暂存区,HEAD 指向当前分支。变更流程:工作树 → add → 索引 → commit → 仓库。

分支操作

Q: merge 和 rebase 的区别? A: merge 创建合并提交保留完整历史;rebase 重写历史保持线性。公共分支用 merge,本地整理用 rebase。

Q: 什么是快进合并? A: 当目标分支是当前分支的直接祖先时,Git 只是移动指针而不创建新提交,称为快进合并。

Q: Fork、Clone、Branch 区别? A: Fork 是服务器端复制仓库;Clone 是下载到本地;Branch 是仓库内的并行开发线。

版本回退

Q: reset 和 revert 的区别? A: reset 移动分支指针,修改历史,适合本地提交;revert 创建新提交反向操作,保留历史,适合公共提交。

Q: 如何恢复误删的提交? A: 使用 git reflog 查看操作历史,找到对应哈希后用 git resetgit cherry-pick 恢复。

工作流

Q: Git Flow 有哪些分支? A: main(生产)、develop(开发)、feature/(功能)、release/(发布)、hotfix/*(热修复)。

Q: Pull Request 流程是什么? A: 创建功能分支 → 开发推送 → 创建 PR → Code Review → CI 检查 → 合并到主分支。

高级操作

Q: stash 的使用场景? A: 临时保存工作区变更以便切换分支处理紧急任务,包括切换分支、拉取更新、代码审查等场景。

Q: 什么是 Git LFS? A: Git 大文件存储扩展,将大文件存在远程服务器,仓库中只保存指针,适用于二进制文件和媒体文件。

Q: Git Hook 有什么用途? A: 在 Git 事件发生时自动执行脚本,如 pre-commit 做代码检查,commit-msg 校验提交格式,pre-push 运行测试。

相关推荐
walking9571 小时前
重新学习前端之CSS
前端·vue.js·面试
walking9571 小时前
重新学习前端之小程序
前端
魔术师Grace1 小时前
AI让我退化成原始人了
前端·程序员
铁皮饭盒1 小时前
今天你会学到这些关键词
前端·后端
李剑一1 小时前
耗时 2 小时!我复刻了全网超火的通透 3D 水晶球动效,Vue3+Three.js 做出高级视觉特效
前端·three.js
oil欧哟1 小时前
🤔 很长时间没写文章了,分享一下最近的一些思考
前端·后端
Hello--_--World2 小时前
Vue指令:v-if vs v-show、v-if 与 v-for 的优先级冲突、自定义指令
前端·javascript·vue.js
神の愛2 小时前
ReactHooks
前端·javascript·react.js
蝎子莱莱爱打怪2 小时前
用好CC,事半功倍!Claude Code 命令大全,黄金命令推荐、多模型配置、实践指南、Hooks 和踩坑记录大全
前端·人工智能·后端