Skip to content

Git 系统学习文档 ​

资料核对日期:2026-06-30
适合人群:前端、Node.js、全栈、前端架构师候选人
学习目标:从下载安装到项目协作,从基础命令到冲突处理、历史修复、团队规范和真实故障排查,形成可直接用于实际项目的 Git 能力。


1. Git 是什么 ​

Git 是一个分布式版本控制系统,用来记录代码历史、多人协作、分支开发、回滚错误、追踪变更来源。

它解决的核心问题:

  • 代码每天都在变,必须知道“谁在什么时候改了什么,为什么改”。
  • 多人同时开发,必须把不同人的修改合并到同一个项目里。
  • 线上出问题时,必须能定位引入问题的提交,并安全回退。
  • 需求、Bug、实验功能需要并行推进,必须用分支隔离风险。

Git 和平台不是一回事:

名称作用常见使用
Git本地版本控制工具git add、git commit、git merge
GitHub代码托管与协作平台Pull Request、Actions、Issues
GitLab企业常用代码平台Merge Request、CI/CD、权限管理
Gitee国内常用代码托管平台国内团队协作、镜像仓库

你可以没有 GitHub,但只要做现代开发,几乎离不开 Git。


2. 学习路线 ​

建议按下面顺序学习:

  1. 安装 Git,配置身份、编辑器、默认分支、换行符。
  2. 理解工作区、暂存区、本地仓库、远程仓库。
  3. 熟练掌握 status、add、commit、log、diff。
  4. 学会分支:switch、branch、merge、rebase。
  5. 学会远程协作:clone、remote、fetch、pull、push。
  6. 掌握冲突解决、回滚、恢复误删、撤销提交。
  7. 学会团队规范:提交规范、分支策略、PR/MR、代码审查。
  8. 进阶掌握 stash、tag、cherry-pick、reflog、bisect、worktree。
  9. 能排查真实项目中的 Git 报错。

3. 下载与安装 ​

3.1 Windows ​

官方下载入口:

推荐方式一:官网下载 Git for Windows 安装包。

安装时重点选项:

  • Editor:如果你用 VS Code,可以选择 VS Code。
  • Default branch name:建议选择 main。
  • PATH:建议选择让 Git 可以在命令行中使用。
  • HTTPS transport backend:一般选择 OpenSSL。
  • Line endings:Windows 常见选择是 checkout 为 CRLF、commit 为 LF,也可以通过后续配置控制。
  • Terminal emulator:一般默认即可。

推荐方式二:使用 winget。

bash
winget install --id Git.Git -e

安装后检查:

bash
git --version

3.2 macOS ​

方式一:触发 Xcode Command Line Tools。

bash
git --version

如果系统未安装,会提示安装命令行工具。

方式二:使用 Homebrew。

bash
brew install git
git --version

3.3 Linux ​

Ubuntu / Debian:

bash
sudo apt update
sudo apt install git
git --version

CentOS / RHEL / Fedora:

bash
sudo dnf install git
git --version

4. 首次配置 ​

安装 Git 后,先配置提交身份。这个身份会写入 commit 历史。

bash
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

查看配置:

bash
git config --global --list

设置默认分支名:

bash
git config --global init.defaultBranch main

设置编辑器,以 VS Code 为例:

bash
git config --global core.editor "code --wait"

设置凭据缓存,减少反复输入账号密码:

bash
git config --global credential.helper manager

配置换行符。

Windows 常用:

bash
git config --global core.autocrlf true

macOS / Linux 常用:

bash
git config --global core.autocrlf input

说明:

  • CRLF 是 Windows 常见换行。
  • LF 是 Linux、macOS、服务端、CI 环境常见换行。
  • 如果团队里有人用 Windows,有人用 macOS,建议通过 .gitattributes 明确管理换行。

5. SSH、HTTPS 和访问令牌 ​

5.1 HTTPS ​

HTTPS 地址类似:

bash
https://github.com/org/repo.git

优点:

  • 配置简单。
  • 公司网络环境里通常更容易访问。

注意:

  • GitHub 等平台通常不再支持用账号密码直接推送代码。
  • 需要使用 Personal Access Token,也就是个人访问令牌。

适合场景:

  • 新手快速 clone。
  • CI/CD 中使用令牌访问私有仓库。
  • 公司网络限制 SSH 端口时。

5.2 SSH ​

SSH 地址类似:

bash
git@github.com:org/repo.git

生成密钥:

bash
ssh-keygen -t ed25519 -C "you@example.com"

启动 ssh-agent 并添加私钥:

bash
ssh-add ~/.ssh/id_ed25519

查看公钥:

bash
cat ~/.ssh/id_ed25519.pub

把公钥添加到 GitHub / GitLab / Gitee 的 SSH keys 页面。

测试 GitHub SSH:

bash
ssh -T git@github.com

常见错误:

text
Permission denied (publickey).

排查顺序:

  1. 确认本机是否有密钥:ls ~/.ssh。
  2. 确认公钥是否添加到代码平台。
  3. 确认 remote 地址是不是 SSH 地址:git remote -v。
  4. 确认私钥是否加入 ssh-agent:ssh-add -l。
  5. 如果有多个平台或多个账号,配置 ~/.ssh/config。

多账号示例:

sshconfig
Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work

Host github-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal

对应 remote:

bash
git remote set-url origin git@github-work:company/project.git

6. Git 的四个区域 ​

Git 学不好,通常是因为没理解区域。

区域英文含义
工作区Working Tree你正在编辑的真实文件
暂存区Index / Staging Area准备进入下一次提交的变更
本地仓库Local Repository.git 中保存的 commit 历史
远程仓库Remote RepositoryGitHub、GitLab、Gitee 上的仓库

典型流转:

text
编辑文件 -> git add -> 暂存区 -> git commit -> 本地仓库 -> git push -> 远程仓库

撤销也要看区域:

想做什么命令
撤销工作区文件修改git restore <file>
取消暂存,但保留文件修改git restore --staged <file>
撤销上一次本地提交,保留变更到暂存区git reset --soft HEAD~1
撤销上一次本地提交,保留变更到工作区git reset --mixed HEAD~1
用一个新提交反向撤销历史提交git revert <commit>

6.1 .git 目录里到底存了什么 ​

Git 学不深,往往是因为没理解对象模型。.git 不是“一堆备份文件”,而是一个对象数据库(Object Database):

对象类型存什么类比
blob文件内容,不含文件名一张快照里的某个文件
tree一个目录的快照:文件名、权限、指向的 blob文件夹
commit指向顶层 tree、作者、提交者、时间、信息、父提交一次快照 + 元数据
tag(annotated)指向某个 commit,带标签名、说明、打标签者版本书签

可以用命令直接验证:

bash
git cat-file -t HEAD        # 查看 HEAD 指向的对象类型:commit
git cat-file -p HEAD        # 打印 commit 对象内容,能看到 tree 和 parent
git cat-file -p HEAD^{tree} # 打印顶层 tree,能看到每个文件对应的 blob

关键结论:

  • 每个对象都按内容哈希命名(SHA-1,现在也支持 SHA-256 仓库)。内容相同只存一份,这就是 Git 体积小的原因之一。
  • commit 不存“差异”,而是存指向 tree 快照的指针;差异是展示时计算出来的。
  • 分支只是指向某个 commit 的可移动指针,所以创建分支几乎零成本。
  • HEAD 是指向当前分支的指针,detached HEAD 就是 HEAD 直接指向 commit 而不是分支。

⚠️ 理解了“分支 = 指针”“commit = 快照”,reset、rebase、cherry-pick、reflog 就都不再是魔法:它们只是在移动指针或复制快照。

6.2 三个核心引用:HEAD、index 与工作区 ​

“四个区域”是使用者视角,底层其实是三个引用在互相比较:

text
HEAD(最近一次提交的快照) <-> index(暂存区快照) <-> 工作区(磁盘文件)
命令实际在比较/操作什么
git diff工作区 vs index
git diff --stagedindex vs HEAD
git diff HEAD工作区 vs HEAD(两者合并的差异)
git add把工作区文件内容写入 index
git commit把 index 写成一个新的 commit 对象,并移动分支指针
git checkout/restore用 index 或 HEAD 的内容覆盖工作区

这也解释了常见现象:git add 之后再改文件,git commit 提交的是暂存那一刻的内容,而不是提交那一刻的文件。


7. 创建仓库 ​

7.1 从零创建 ​

bash
mkdir demo
cd demo
git init

添加文件并提交:

bash
echo "# demo" > README.md
git add README.md
git commit -m "docs: add readme"

绑定远程仓库:

bash
git remote add origin git@github.com:your-name/demo.git
git push -u origin main

-u 的作用是建立本地分支和远程分支的 upstream 关系。之后可以直接:

bash
git push
git pull

7.2 克隆已有项目 ​

bash
git clone git@github.com:org/project.git
cd project
git remote -v

克隆指定分支:

bash
git clone -b develop git@github.com:org/project.git

浅克隆,适合 CI 或只需要最新历史:

bash
git clone --depth 1 git@github.com:org/project.git

注意:浅克隆历史不完整,某些依赖完整历史的命令会受限,例如复杂的 bisect、版本生成、历史比较。


8. 基础命令 ​

8.1 查看状态 ​

bash
git status
git status -sb

git status -sb 输出更简洁,适合日常查看。

8.2 查看差异 ​

查看工作区和暂存区之间的差异:

bash
git diff

查看暂存区和上次提交之间的差异:

bash
git diff --staged

查看某个提交:

bash
git show <commit>

8.3 暂存与提交 ​

暂存全部变更:

bash
git add .

暂存指定文件:

bash
git add src/main.ts

交互式暂存,适合一个文件里有多处修改但只想提交其中一部分:

bash
git add -p

提交:

bash
git commit -m "feat: add login page"

修改最近一次提交信息:

bash
git commit --amend

注意:如果最近一次提交已经推送到共享分支,修改历史会影响别人。需要推送时用 --force-with-lease,并确认团队允许。

8.4 查看历史 ​

bash
git log
git log --oneline
git log --oneline --graph --decorate --all

查看某个文件历史:

bash
git log -- src/main.ts

查看每一行最后是谁改的:

bash
git blame src/main.ts

git blame 不应该用于甩锅,而应该用于理解上下文:为什么这里要这样写,应该找谁确认。

8.5 跟踪重命名:git log --follow 和 -M ​

Git 不在元数据里记录“重命名”,而是在比较时通过内容相似度检测出来。所以:

bash
# 查看某个文件的历史,跨越它被重命名之前
git log --follow -- src/utils/request.ts

# 查看某次提交或两个提交间的差异时检测重命名/复制
git log -M --summary
git diff -M v1.0.0 v2.0.0

注意:

  • --follow 只对单文件历史有效,且依赖相似度检测,重命名同时大改内容时可能断开。
  • git blame 也有类似的 -C 选项,可以追踪从其他文件复制来的代码行。

8.6 git log 的高级过滤 ​

排查问题时,git log 的过滤能力比翻历史高效得多:

bash
# 谁改过这个函数(按代码内容搜索,需要 -p 展开 diff)
git log -S "useToken" --oneline -- src/auth

# 谁修改过匹配正则的内容(区别于 -S 的“出现次数变化”)
git log -G "VITE_CDN" --oneline

# 只看某个作者、某段时间、某个路径
git log --author="Ada" --since="2 weeks ago" -- src/router

# 看合并提交 / 排除合并提交
git log --merges --oneline
git log --no-merges --oneline

# 查看两个分支的差异提交:在 feature 上但不在 main 上
git log main..feature/login --oneline

# 反向:在 main 上但不在 feature 上
git log feature/login..main --oneline

-S 和 -G 的差别:

  • -S 找“某个字符串出现次数发生变化”的提交,适合定位“这个 API 是什么时候引入/删除的”。
  • -G 找“diff 内容匹配正则”的提交,适合定位“哪次提交动过这行代码”。

9. 分支 ​

分支是 Git 协作的核心。

查看分支:

bash
git branch
git branch -a

创建并切换分支:

bash
git switch -c feature/login

切换已有分支:

bash
git switch main

删除本地分支:

bash
git branch -d feature/login

强制删除本地分支:

bash
git branch -D feature/login

删除远程分支:

bash
git push origin --delete feature/login

9.1 merge ​

merge 会把一个分支的变更合并进当前分支。

bash
git switch main
git merge feature/login

适合:

  • 保留真实分支合并历史。
  • 团队希望看到每个功能分支的合并节点。
  • PR/MR 合并时使用 merge commit。

缺点:

  • 历史可能出现较多合并提交。

9.2 rebase ​

rebase 会把当前分支的提交“挪到”另一个分支的最新提交之后。

bash
git switch feature/login
git fetch origin
git rebase origin/main

适合:

  • 让功能分支历史更线性。
  • 提 PR/MR 前同步主分支。
  • 整理本地提交历史。

重要原则:

  • 可以 rebase 自己还没共享出去的分支。
  • 不要随便 rebase 多人共同开发、已经被别人基于其继续开发的公共分支。

9.3 merge 和 rebase 怎么选 ​

场景推荐
本地功能分支同步主分支rebase origin/main
合并 PR/MR 到主分支按团队策略选择 merge、squash、rebase merge
多人共用长期分支谨慎 rebase,优先 merge
想保留完整分支合并历史merge
想让提交历史更直线rebase

9.4 merge 的内部机制:三种方式 ​

git merge 并不总是同一种行为。理解它有助于判断冲突来源:

bash
git merge feature/login
场景行为说明
Fast-forward当前分支没有新提交,直接把指针移到 feature/login不产生 merge commit,历史看起来像直线
三方合并(3-way)两边都有新提交,找到共同祖先(merge base),做三方比较产生一个有两个父提交的 merge commit
Octopus同时合并多个分支较少用,要求无冲突
bash
# 不允许 fast-forward,强制保留合并节点(团队常用,保留“这里发生过合并”)
git merge --no-ff feature/login

# 只合并,如果无法 fast-forward 就失败(适合脚本和 CI 检查)
git merge --ff-only feature/login

# 合并但不自动提交,先看一眼结果
git merge --no-commit feature/login

查找两个分支的共同祖先(判断冲突范围时很有用):

bash
git merge-base main feature/login

⚠️ 三方合并的冲突标记里,HEAD 一侧是当前分支内容,另一侧是被合并分支内容;git merge 还可以配合 git checkout --ours/--theirs 快速选择一边,但理解两边意图后再手动合并通常更安全。

9.5 交互式 rebase:整理提交历史 ​

rebase 不只是“同步主分支”,--interactive 是整理本地提交的核心工具:

bash
# 整理最近 5 个提交
git rebase -i HEAD~5

打开的编辑器会列出每个提交及其动作:

text
pick a1b2c3d feat: add login form
pick d4e5f6a fix: typo
pick 7g8h9i0 feat: add login validation

常用动作:

动作含义
pick保留该提交
reword保留提交但修改提交信息
squash合并到上一个提交,保留信息并编辑
fixup合并到上一个提交,丢弃本条信息
drop删除该提交
edit停在该提交,允许修改内容后 --continue

典型用法:把多个零碎的 wip、fix typo 提交 squash/fixup 成一个干净的提交,再推送。

注意:

  • 交互式 rebase 会改写历史,只对自己还没共享(或团队允许改写)的分支使用。
  • 执行前先建备份分支:git branch backup。
  • 中途出错随时可以 git rebase --abort 回到起点。

10. 远程协作 ​

查看远程仓库:

bash
git remote -v

添加远程:

bash
git remote add origin git@github.com:org/project.git

修改远程地址:

bash
git remote set-url origin git@github.com:org/new-project.git

拉取远程信息,但不自动合并:

bash
git fetch origin

拉取并合并当前分支对应的远程分支:

bash
git pull

git pull 本质上通常等于:

bash
git fetch
git merge

如果配置了 rebase,则可能等于:

bash
git fetch
git rebase

建议团队明确 pull 策略:

bash
git config --global pull.rebase false

或:

bash
git config --global pull.rebase true

只允许 fast-forward:

bash
git config --global pull.ff only

推送当前分支并建立 upstream:

bash
git push -u origin feature/login

10.1 upstream 跟踪关系与分支清理 ​

-u(即 --set-upstream)建立的是本地分支与远程分支的跟踪关系。有了它:

  • git push、git pull 不用再写参数。
  • git status -sb 会显示领先/落后远程多少个提交(例如 [ahead 2, behind 1])。
  • git branch -vv 可以查看所有本地分支的跟踪情况:
bash
git branch -vv
# feature/login a1b2c3d [origin/feature/login: ahead 1] feat: add login

维护远程分支列表的几个习惯:

bash
# fetch 时顺便删除远程已删除分支对应的本地跟踪引用
git fetch --prune

# 删除已经合并进当前分支的本地分支(清理用)
git branch --merged main

# 推送所有本地分支(谨慎使用,通常逐个推送更可控)
git push --all origin

⚠️ git fetch 只更新 origin/* 这类远程跟踪分支(存在 .git/refs/remotes/ 下),不会碰你的本地分支;本地分支要更新,必须再走 merge、rebase 或 pull。这是很多人“fetch 了为什么代码没变”的原因。


11. 常见团队工作流 ​

11.1 Feature Branch ​

最常见,适合前端、Node、业务系统。

流程:

text
main / develop -> 创建 feature 分支 -> 开发 -> push -> PR/MR -> Code Review -> 合并

命令示例:

bash
git switch main
git pull
git switch -c feature/user-permission

# 开发
git add .
git commit -m "feat(permission): add route guard"
git push -u origin feature/user-permission

优点:

  • 每个需求隔离。
  • 方便 Code Review。
  • 方便 CI 针对 PR/MR 跑测试。

11.2 Git Flow ​

常见分支:

分支作用
main线上稳定版本
develop集成开发版本
feature/*功能开发
release/*发布准备
hotfix/*线上紧急修复

适合:

  • 版本发布节奏较清晰的产品。
  • 客户端、小程序、需要发版审批的项目。

缺点:

  • 分支多,流程较重。
  • 对快速迭代 Web 项目可能显得复杂。

11.3 Trunk-Based Development ​

主干开发强调短分支、小步提交、频繁集成。

适合:

  • CI/CD 完备。
  • 自动化测试较可靠。
  • 团队有 feature flag 能力。

典型要求:

  • 分支生命周期很短。
  • 每次变更都比较小。
  • 主分支必须随时可发布。

11.4 前端项目建议流程 ​

中小型前端团队:

text
main -> feature/* -> PR -> CI -> review -> squash merge -> main

多环境企业项目:

text
main 线上
release 预发
develop 测试
feature/* 需求
hotfix/* 紧急修复

Monorepo 项目:

  • 分支策略要更严格,因为一个仓库里包含多个应用或包。
  • PR 描述必须说明影响范围。
  • CI 最好基于 changed files 只跑受影响项目。
  • 锁文件冲突要谨慎处理。

11.5 PR 的三种合并方式 ​

GitHub / GitLab 平台合并 PR 时通常有三种策略,本质对应不同的 Git 操作:

方式对应 Git 操作结果适合
Merge commitgit merge --no-ff保留全部提交 + 一个合并节点需要完整保留开发过程
Squash merge把 PR 所有提交压成一个再提交主分支每个 PR 只有一个干净提交小团队、频繁提交、主分支要求干净
Rebase merge把 PR 提交逐个变基到主分支再 fast-forward线性历史,无合并节点追求线性且接受改写

Squash merge 的内部机制:

text
feature 分支:A - B - C
squash 后:   一个包含 A+B+C 全部变更的新提交,只有一个父提交

注意:

  • Squash 之后的新提交和原来的 A、B、C 不是同一个 commit(哈希不同),所以功能分支不能直接再 git merge 进主分支,否则会出现重复变更。
  • 用 squash 策略时,功能分支合并后应该删除,后续修改开新分支。
  • 如果团队在 squash 后又想在功能分支上继续开发,建议先在本地把历史整理干净,或使用 merge commit 策略。

12. 提交信息规范 ​

推荐使用 Conventional Commits 风格。

格式:

text
<type>(scope): <subject>

常见 type:

type含义
feat新功能
fix修复 Bug
docs文档
style格式,不影响逻辑
refactor重构
perf性能优化
test测试
build构建系统
ciCI 配置
chore杂项
revert回滚

示例:

bash
git commit -m "feat(auth): add login token refresh"
git commit -m "fix(upload): resume chunk upload after network retry"
git commit -m "build(vite): enable legacy plugin"

团队收益:

  • PR/MR 更容易看。
  • 可以自动生成 changelog。
  • 可以根据提交类型控制版本号。
  • git log --oneline 更清晰。

13. .gitignore 和 .gitattributes ​

13.1 .gitignore ​

.gitignore 用来忽略不应该进入仓库的文件。

前端项目常见配置:

gitignore
node_modules/
dist/
build/
.turbo/
.vite/
.next/
.nuxt/
coverage/
*.log
.env
.env.local
.DS_Store

Node / Nest 项目常见配置:

gitignore
node_modules/
dist/
coverage/
.env
.env.*
!.env.example
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*

注意:

  • .env 通常不应该提交。
  • 可以提交 .env.example,说明需要哪些环境变量。
  • 如果文件已经被 Git 追踪,后面加入 .gitignore 不会自动取消追踪。

取消已追踪文件:

bash
git rm --cached .env
git commit -m "chore: stop tracking env file"

13.2 .gitattributes ​

.gitattributes 用于统一换行、文件属性、合并策略。

常见配置:

gitattributes
* text=auto
*.js text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.vue text eol=lf
*.json text eol=lf
*.md text eol=lf
*.png binary
*.jpg binary
*.pdf binary

锁文件可以保持文本合并:

gitattributes
package-lock.json text eol=lf
pnpm-lock.yaml text eol=lf
yarn.lock text eol=lf

14. 标签与版本发布 ​

标签常用于发布版本。

创建轻量标签:

bash
git tag v1.0.0

创建带说明的标签:

bash
git tag -a v1.0.0 -m "release v1.0.0"

推送标签:

bash
git push origin v1.0.0

推送全部标签:

bash
git push origin --tags

删除本地标签:

bash
git tag -d v1.0.0

删除远程标签:

bash
git push origin --delete v1.0.0

项目使用场景:

  • Web 项目发布到生产环境后打 tag。
  • 小程序每次审核版本打 tag。
  • npm 包发布版本和 Git tag 对齐。
  • 后端服务镜像 tag 和 Git tag 对齐。

15. 撤销、回滚与恢复 ​

15.1 restore ​

撤销工作区文件修改:

bash
git restore src/main.ts

取消暂存:

bash
git restore --staged src/main.ts

15.2 reset ​

撤销最近一次提交,保留变更在暂存区:

bash
git reset --soft HEAD~1

撤销最近一次提交,保留变更在工作区:

bash
git reset --mixed HEAD~1

彻底回到某个提交:

bash
git reset --hard <commit>

危险点:

  • --hard 会丢弃工作区和暂存区变更。
  • 对已经推送到共享分支的历史使用 reset 要非常谨慎。

15.3 revert ​

用新提交撤销旧提交:

bash
git revert <commit>

适合:

  • 线上回滚。
  • 已经推送到共享分支的提交。
  • 需要保留历史,不想改写历史。

15.4 reflog ​

reflog 记录本地 HEAD 和分支指针移动历史。误删分支、误 reset 后,常用它救命。

bash
git reflog

找回某个提交:

bash
git reset --hard <reflog中的commit>

找回误删分支:

bash
git reflog
git switch -c recovered-branch <commit>

注意:

  • reflog 主要是本地记录,不等同于远程永久备份。
  • 时间久了可能被清理。

15.5 revert 合并提交与选择决策表 ​

普通 git revert <commit> 处理单父提交很直接,但 revert 一个 merge commit 时要指定保留哪一侧:

bash
# -m 1 表示保留第一个父提交(通常是主干)那一侧,撤销被合并分支带来的变更
git revert -m 1 <merge-commit>

理解要点:

  • merge commit 有两个父提交,-m 1 是“以第一个父提交为基准计算反向差异”。
  • revert 一个 merge 后,如果以后想把该功能分支重新合并,需要先“revert 那个 revert”,否则 Git 认为那些变更已经合并过,不会再次带入。

三种撤销手段的选择决策表:

场景推荐原因
改动只在工作区,还没 git addgit restore <file>不触碰历史
已暂存,想取消暂存git restore --staged <file>保留修改
本地未推送的提交想重做git reset --soft/--mixed HEAD~1历史未共享,安全
已推送到共享分支的提交要撤销git revert <commit>不改写历史,对协作者无影响
刚合并的功能整体有问题git revert -m 1 <merge-commit>保留历史且可追溯
误操作导致分支/提交“消失”git reflog 找回对象还在,只是没被引用

⚠️ reset --hard 和 git clean -fd 是少数会真正“丢数据”的操作。执行前如果不确定,先 git stash push -u 或建备份分支,成本极低。


16. stash 临时保存 ​

当你正在开发,但需要临时切换分支处理 Bug,可以用 stash。

保存当前修改:

bash
git stash push -m "wip: login page"

查看 stash:

bash
git stash list

恢复最近一次 stash,并删除该 stash:

bash
git stash pop

恢复但保留 stash:

bash
git stash apply stash@{0}

删除 stash:

bash
git stash drop stash@{0}

保存未追踪文件:

bash
git stash push -u -m "wip: include new files"

适合场景:

  • 切分支前临时保存当前工作。
  • 拉取远程代码前避免本地修改被覆盖。
  • 需要把未完成工作转移到另一个分支。

17. cherry-pick、bisect、worktree ​

17.1 cherry-pick ​

把某个提交复制到当前分支。

bash
git cherry-pick <commit>

适合:

  • hotfix 已经提交到 main,需要同步到 release。
  • 某个功能分支里只有一两个提交需要提前合入。

遇到冲突:

bash
git status
# 解决冲突
git add .
git cherry-pick --continue

放弃:

bash
git cherry-pick --abort

17.2 bisect ​

二分查找哪个提交引入了 Bug。

bash
git bisect start
git bisect bad
git bisect good <known-good-commit>

Git 会切到中间提交,你运行测试或手动验证。

如果当前提交有问题:

bash
git bisect bad

如果当前提交没问题:

bash
git bisect good

结束:

bash
git bisect reset

适合:

  • 项目很大,无法肉眼定位问题。
  • 线上突然出现回归,需要找出引入问题的 commit。

17.3 worktree ​

同一个仓库同时检出多个工作目录。

bash
git worktree add ../project-hotfix hotfix/login-error

适合:

  • 当前分支有大量未完成工作,但要立刻修复线上问题。
  • 同时对比两个分支的构建结果。
  • Monorepo 中需要并行处理多个任务。

查看:

bash
git worktree list

删除:

bash
git worktree remove ../project-hotfix

18. 冲突处理 ​

冲突并不代表出错,它只代表 Git 无法自动判断哪一边应该保留。

常见冲突来源:

  • 两个人修改了同一个文件同一段代码。
  • 一个人修改文件,另一个人删除文件。
  • 一个分支重命名文件,另一个分支修改旧文件。
  • package-lock.json、pnpm-lock.yaml、yarn.lock 同时变化。
  • 大型重构和业务需求并行开发。

18.1 merge 冲突解决流程 ​

合并:

bash
git switch main
git merge feature/login

如果出现冲突:

bash
git status

文件里会出现冲突标记:

text
<<<<<<< HEAD
当前分支内容
=======
被合并分支内容
>>>>>>> feature/login

处理步骤:

  1. 打开冲突文件。
  2. 理解两边代码的业务意图。
  3. 删除 <<<<<<<、=======、>>>>>>> 标记。
  4. 保留正确代码,必要时手动合并两边逻辑。
  5. 运行测试、构建、lint。
  6. 标记已解决。
bash
git add src/auth.ts
git commit

放弃本次 merge:

bash
git merge --abort

18.2 rebase 冲突解决流程 ​

bash
git switch feature/login
git fetch origin
git rebase origin/main

出现冲突后:

bash
git status
# 修改冲突文件
git add .
git rebase --continue

跳过当前提交:

bash
git rebase --skip

放弃 rebase:

bash
git rebase --abort

注意:

  • rebase 可能对多个提交逐个处理冲突。
  • 每次解决后都要 git rebase --continue。
  • 如果你不确定,先 git branch backup-before-rebase 建备份分支。

18.3 锁文件冲突 ​

前端项目高频冲突文件:

  • package-lock.json
  • pnpm-lock.yaml
  • yarn.lock

不要简单地随便选一边,因为锁文件决定依赖版本。

推荐处理:

bash
# 先解决 package.json
# 再重新安装依赖,重新生成锁文件
pnpm install

或:

bash
npm install

然后:

bash
git add package.json pnpm-lock.yaml
git commit

检查:

bash
pnpm test
pnpm build

18.4 二进制文件冲突 ​

图片、设计稿、PDF、压缩包等二进制文件不能像代码一样合并。

处理方式:

  • 和协作者确认保留哪个版本。
  • 使用 git checkout --ours <file> 保留当前分支版本。
  • 使用 git checkout --theirs <file> 保留被合并分支版本。

示例:

bash
git checkout --ours public/logo.png
git add public/logo.png

或:

bash
git checkout --theirs public/logo.png
git add public/logo.png

19. 真实开发高频问题与解决方案 ​

这一节按实际开发中常见报错和事故整理。

19.1 non-fast-forward ​

常见报错:

text
! [rejected] main -> main (non-fast-forward)

原因:

  • 远程分支有你本地没有的提交。
  • 直接 push 会覆盖远程历史,所以 Git 拒绝。

解决:

bash
git fetch origin
git rebase origin/main
git push

如果团队不用 rebase:

bash
git pull
git push

不要第一反应就 git push -f。强推会改写远程历史,可能覆盖同事代码。

19.2 divergent branches ​

常见报错:

text
You have divergent branches and need to specify how to reconcile them.

原因:

  • 本地和远程都有新提交。
  • Git 需要你明确 pull 时使用 merge、rebase,还是只允许 fast-forward。

配置方式:

bash
git config --global pull.rebase false

或:

bash
git config --global pull.rebase true

或:

bash
git config --global pull.ff only

临时指定:

bash
git pull --rebase
git pull --no-rebase
git pull --ff-only

19.3 local changes would be overwritten ​

常见报错:

text
Your local changes to the following files would be overwritten by checkout

原因:

  • 你本地有未提交修改。
  • 切分支、pull、merge 会覆盖这些修改。

解决方式一:提交。

bash
git add .
git commit -m "wip: save current work"

解决方式二:stash。

bash
git stash push -m "wip before switching branch"
git switch target-branch
git stash pop

解决方式三:确认不要这些修改后丢弃。

bash
git restore .

19.4 untracked files would be overwritten ​

常见报错:

text
The following untracked working tree files would be overwritten by checkout

原因:

  • 你本地有未被 Git 追踪的新文件。
  • 目标分支里也有同路径文件。

解决:

bash
git status

如果要保留:

bash
git add .
git commit -m "wip: keep untracked files"

或:

bash
git stash push -u -m "save untracked files"

如果确认不要:

bash
git clean -fd

注意:git clean -fd 会删除未追踪文件和目录,执行前必须确认。

19.5 detached HEAD ​

常见状态:

text
HEAD detached at <commit>

原因:

  • 你检出了某个 commit 或 tag,而不是分支。

如果只是查看历史,直接切回分支:

bash
git switch main

如果已经在 detached HEAD 上做了提交,要保留它:

bash
git switch -c fix/from-detached-head

19.6 提交到了错误分支 ​

场景:

  • 本来应该在 feature/a 开发,却提交到了 main。

如果还没推送:

bash
git branch feature/a
git reset --soft HEAD~1
git switch feature/a
git add .
git commit -m "feat: correct branch commit"

如果已经推送到共享分支:

  • 不要自行强推。
  • 和团队确认后用 git revert 或开 PR 修正。

19.7 提交信息写错 ​

最近一次提交未推送:

bash
git commit --amend -m "fix(auth): correct token refresh"

已推送到个人分支:

bash
git commit --amend -m "fix(auth): correct token refresh"
git push --force-with-lease

已推送到公共分支:

  • 通常不改历史。
  • 可以追加一个修正提交。

19.8 错误提交了敏感信息 ​

例如:

  • .env
  • 数据库密码
  • API key
  • 私钥

立即处理:

  1. 先废弃泄露的密钥,在平台上重新生成。
  2. 从当前版本中删除文件。
bash
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit -m "chore: remove env from tracking"

如果敏感信息已经进入历史,需要重写历史。可以使用 git filter-repo 或平台推荐工具。完成后还要强制推送,并通知所有协作者重新同步。

重点:删除当前文件不等于删除历史里的秘密,所以密钥必须轮换。

19.9 fatal: not a git repository ​

常见报错:

text
fatal: not a git repository

原因:

  • 当前目录不是 Git 仓库。
  • .git 目录被删除。
  • 你在错误目录运行命令。

排查:

bash
pwd
ls -a
git rev-parse --show-toplevel

解决:

  • 切换到正确项目目录。
  • 如果是新项目,执行 git init。
  • 如果 .git 被误删,通常需要重新 clone,或从备份恢复。

19.10 repository not found ​

常见原因:

  • 仓库地址写错。
  • 没有权限。
  • 私有仓库没有授权。
  • SSH key 或 PAT 配置错误。

排查:

bash
git remote -v
ssh -T git@github.com

HTTPS 场景:

  • 检查 Personal Access Token 是否过期。
  • 检查 token 权限是否包含仓库访问范围。

19.11 Permission denied publickey ​

原因:

  • 没有 SSH key。
  • 公钥没加到平台。
  • ssh-agent 没加载私钥。
  • 多账号时用了错误私钥。

排查:

bash
ssh -T git@github.com
ssh-add -l

如果没有加载:

bash
ssh-add ~/.ssh/id_ed25519

19.12 refusing to merge unrelated histories ​

常见报错:

text
fatal: refusing to merge unrelated histories

原因:

  • 两个仓库没有共同提交历史。
  • 常见于本地 git init 后,又强行关联一个远程已有仓库。

如果确认要合并:

bash
git pull origin main --allow-unrelated-histories

更推荐:

  • 新项目先 clone 远程仓库,再把代码复制进去。
  • 避免本地和远程分别初始化。

19.13 pathspec did not match ​

常见报错:

text
error: pathspec 'xxx' did not match any file(s) known to git

原因:

  • 分支名、tag 名、文件路径写错。
  • 本地还没有 fetch 到远程分支。

解决:

bash
git branch -a
git fetch origin
git switch -c feature/foo origin/feature/foo

19.14 误删分支 ​

如果只是删了本地分支:

bash
git reflog
git switch -c recovered <commit>

如果远程分支还在:

bash
git fetch origin
git switch -c feature/foo origin/feature/foo

如果远程分支也删了:

  • 先找本地 reflog。
  • 再查同事本地仓库是否还有该分支。
  • 查 GitHub / GitLab 的 PR/MR 页面是否能恢复。

19.15 锁文件频繁冲突 ​

原因:

  • 多个需求同时安装或升级依赖。
  • npm、pnpm、yarn 混用。
  • Node 版本不一致导致锁文件格式变化。

解决策略:

  1. 团队统一包管理器,例如只用 pnpm。
  2. 提交 packageManager 字段。
  3. 启用 Corepack。
  4. 统一 Node 版本,例如 .nvmrc 或 .node-version。
  5. 冲突时先解决 package.json,再重新 install。

示例 package.json:

json
{
  "packageManager": "pnpm@9.0.0",
  "engines": {
    "node": ">=20"
  }
}

19.16 大文件 push 被拒绝 ​

常见原因:

  • 提交了视频、PSD、压缩包、模型文件。
  • 平台限制单文件大小。

解决:

  1. 普通构建产物不要提交,加入 .gitignore。
  2. 必须版本化的大文件使用 Git LFS。
bash
git lfs install
git lfs track "*.psd"
git add .gitattributes
git add design.psd
git commit -m "chore: track psd with lfs"

如果大文件已经进入历史,需要从历史中清理,再重新推送。

19.17 子模块没有初始化 ​

常见现象:

  • clone 后子目录为空。
  • 构建时报依赖源码不存在。

解决:

bash
git submodule update --init --recursive

克隆时同时拉子模块:

bash
git clone --recurse-submodules git@github.com:org/project.git

更新子模块:

bash
git submodule update --remote --merge

19.18 代理、证书、网络问题 ​

常见现象:

  • clone 超时。
  • SSL certificate problem。
  • 公司网络不能访问 SSH 端口。

排查:

bash
git remote -v
git config --global --get http.proxy
git config --global --get https.proxy

设置代理示例:

bash
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890

取消代理:

bash
git config --global --unset http.proxy
git config --global --unset https.proxy

企业内网证书问题应优先安装公司根证书,不建议长期关闭 SSL 校验。


20. 项目中的 Git 使用示例 ​

20.1 Vue 2 / Vue 3 项目 ​

典型文件:

text
src/
public/
package.json
pnpm-lock.yaml
vite.config.ts 或 vue.config.js
.env.development
.env.production

建议:

  • .env.local 不提交。
  • .env.example 提交。
  • dist/ 不提交。
  • pnpm-lock.yaml 或 package-lock.json 必须提交,保证依赖一致。

开发流程:

bash
git switch main
git pull --rebase
git switch -c feature/vue-user-list
pnpm install
pnpm dev

git add src package.json pnpm-lock.yaml
git commit -m "feat(user): add user list page"
git push -u origin feature/vue-user-list

提交前检查:

bash
pnpm lint
pnpm test
pnpm build

20.2 React 项目 ​

React 项目常见冲突点:

  • 组件文件同名修改。
  • 路由表同时修改。
  • 状态管理 store 同时修改。
  • 锁文件冲突。

建议:

  • 功能分支尽量小。
  • 公共组件改动单独 PR。
  • 大范围格式化不要和业务需求混在一个提交里。

示例:

bash
git switch -c feature/react-dashboard-filter
git add src/pages src/components
git commit -m "feat(dashboard): add filter panel"

20.3 Nuxt 项目 ​

Nuxt 常见生成目录:

gitignore
.nuxt/
.output/
dist/
node_modules/
.env

建议:

  • 不提交 .nuxt、.output。
  • 环境变量通过 .env.example 描述。
  • SSR 项目发布时用 tag 对齐部署版本。

发布:

bash
git tag -a v1.3.0 -m "release nuxt app v1.3.0"
git push origin v1.3.0

20.4 小程序项目 ​

小程序项目常见特点:

  • 版本需要提交审核。
  • project.config.json 可能包含成员本地配置。
  • 构建产物、上传密钥不应该随意提交。

建议:

  • 保留必要的项目配置。
  • 本地私有配置拆成 local 文件并忽略。
  • 每次提审前打 tag。

示例:

bash
git switch -c release/mp-v2.4.0
git commit -m "release(mp): prepare v2.4.0"
git tag -a mp-v2.4.0 -m "wechat mini program v2.4.0"
git push origin release/mp-v2.4.0
git push origin mp-v2.4.0

20.5 Node / Express / Nest 项目 ​

常见忽略:

gitignore
node_modules/
dist/
coverage/
.env
logs/
*.log

建议:

  • 数据库迁移文件要提交。
  • .env 不提交,.env.example 提交。
  • dist/ 通常不提交,由 CI 构建。
  • 生产发布用 tag 或 commit SHA 追踪镜像。

Nest 项目示例:

bash
git switch -c feature/nest-auth-guard
git add src/modules/auth src/common/guards
git commit -m "feat(auth): add jwt guard"
git push -u origin feature/nest-auth-guard

回滚线上问题:

bash
git revert <bad-commit>
git push

然后让 CI/CD 重新部署。

20.6 Monorepo 项目 ​

Monorepo 目录可能类似:

text
apps/admin
apps/web
packages/ui
packages/utils
package.json
pnpm-lock.yaml
pnpm-workspace.yaml

建议:

  • PR 描述影响范围,例如 apps/admin、packages/ui。
  • 修改公共包时说明影响的应用。
  • CI 按变更范围运行任务。
  • 使用 changeset 管理包版本。

示例:

bash
git switch -c feat/ui-button-loading
git add packages/ui apps/admin
git commit -m "feat(ui): add button loading state"

21. Git Hooks 与团队规范 ​

Git Hooks 是 Git 在特定时机执行脚本的机制。

常见 hooks:

Hook时机用途
pre-commit提交前lint、format、单测
commit-msg提交信息生成后校验提交信息规范
pre-pushpush 前测试、类型检查

前端项目常见组合:

  • Husky:管理 Git hooks。
  • lint-staged:只检查本次暂存的文件。
  • commitlint:检查 commit message。

示例:

bash
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional
npx husky init

package.json 示例:

json
{
  "lint-staged": {
    "*.{js,ts,tsx,vue}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,css,scss}": [
      "prettier --write"
    ]
  }
}

pre-commit 示例:

bash
npx lint-staged

commit-msg 示例:

bash
npx commitlint --edit "$1"

注意:

  • Hooks 是本地兜底,不应该替代 CI。
  • 团队强约束应放在 CI、分支保护、代码审查里。
  • 大型项目 pre-commit 不要跑太慢,否则开发者会绕过它。

22. 分支保护与 Code Review ​

成熟团队通常会保护主分支。

常见规则:

  • 禁止直接 push 到 main。
  • 必须通过 PR/MR。
  • 至少一名或两名 reviewer approve。
  • CI 通过后才能合并。
  • 必须解决所有 conversation。
  • 禁止 force push 到主分支。

PR/MR 描述建议:

markdown
## 背景
为什么做这次修改。

## 变更
- 增加用户权限路由守卫
- 调整菜单渲染逻辑

## 影响范围
- apps/admin
- packages/auth

## 验证
- pnpm lint
- pnpm test
- pnpm build

## 风险
- 老用户权限数据可能需要兼容

Code Review 看什么:

  • 业务逻辑是否正确。
  • 是否影响现有功能。
  • 是否有测试。
  • 是否有安全风险。
  • 是否有性能问题。
  • 是否有隐藏的破坏性变更。

23. CI/CD 中的 Git ​

CI/CD 通常基于 Git 事件触发:

  • push 到分支。
  • 提交 PR/MR。
  • 创建 tag。
  • 合并到主分支。

常见流水线:

text
push feature -> lint/test/build -> PR review
merge main -> build image -> deploy staging
tag v1.0.0 -> deploy production

前端项目:

  • PR 阶段跑 lint、typecheck、unit test、build。
  • 合并主分支后部署测试环境。
  • tag 后部署生产。

Node / Nest 项目:

  • PR 阶段跑 lint、test、e2e。
  • 合并主分支构建 Docker 镜像。
  • 镜像 tag 使用 commit SHA 或 Git tag。

24. 安全与最佳实践 ​

24.1 不要提交敏感信息 ​

不要提交:

  • .env
  • 私钥
  • token
  • 数据库密码
  • 生产配置
  • 云服务凭证

应该提交:

  • .env.example
  • 配置字段说明
  • 本地开发启动文档

24.2 小提交、清晰提交 ​

好的提交:

  • 一个提交只解决一个问题。
  • 提交信息说明目的。
  • 可以单独 revert。

不好的提交:

  • update
  • fix bug
  • 一个提交里同时格式化全项目、改业务、升级依赖。

24.3 不要在公共分支乱改历史 ​

高危命令:

bash
git reset --hard
git push --force
git rebase

不是不能用,而是要知道影响范围。

如果必须强推个人分支:

bash
git push --force-with-lease

--force-with-lease 会在远程分支被别人更新时拒绝覆盖,比 --force 安全。

24.4 合并前先自测 ​

前端常见:

bash
pnpm lint
pnpm test
pnpm build

Node / Nest 常见:

bash
pnpm lint
pnpm test
pnpm test:e2e
pnpm build

25. 常用命令速查 ​

bash
# 初始化与克隆
git init
git clone <url>

# 状态与差异
git status
git diff
git diff --staged

# 暂存与提交
git add .
git add -p
git commit -m "feat: message"
git commit --amend

# 历史
git log --oneline --graph --decorate --all
git show <commit>
git blame <file>

# 分支
git branch
git switch <branch>
git switch -c <branch>
git branch -d <branch>

# 远程
git remote -v
git fetch origin
git pull
git pull --rebase
git push -u origin <branch>

# 合并与变基
git merge <branch>
git rebase <branch>
git merge --abort
git rebase --continue
git rebase --abort

# 撤销与恢复
git restore <file>
git restore --staged <file>
git reset --soft HEAD~1
git reset --mixed HEAD~1
git revert <commit>
git reflog

# 临时保存
git stash push -m "message"
git stash list
git stash pop

# 标签
git tag
git tag -a v1.0.0 -m "release v1.0.0"
git push origin v1.0.0

26. 练习路线 ​

阶段一:个人基础 ​

练习目标:

  • 创建仓库。
  • 提交三次。
  • 查看历史。
  • 撤销工作区修改。
  • 修改最近一次提交信息。

练习命令:

bash
git init
git status
git add .
git commit -m "docs: init"
git log --oneline
git restore <file>
git commit --amend

阶段二:分支协作 ​

练习目标:

  • 创建两个 feature 分支。
  • 模拟同一文件修改。
  • 合并并解决冲突。

练习命令:

bash
git switch -c feature/a
git switch main
git switch -c feature/b
git merge feature/a

阶段三:远程协作 ​

练习目标:

  • 创建远程仓库。
  • push 分支。
  • 创建 PR/MR。
  • 模拟 non-fast-forward。
  • 解决后重新 push。

阶段四:故障恢复 ​

练习目标:

  • 误删分支后用 reflog 恢复。
  • 错误提交后用 reset 和 revert 分别处理。
  • 用 stash 保存未完成工作。
  • 用 bisect 定位 Bug。

27. 面试与架构师视角 ​

前端架构师不只会用 Git 命令,还要能设计团队协作方式。

需要掌握的问题:

  1. 为什么不能直接 push 到主分支?
  2. merge 和 rebase 的区别是什么?
  3. squash merge 适合什么场景?
  4. 线上 Bug 应该用 reset 还是 revert?
  5. 如何处理锁文件冲突?
  6. 如何避免提交敏感信息?
  7. 如何设计 Monorepo 的分支和发布策略?
  8. 如何通过 Git tag、commit SHA 对齐发布版本?
  9. 如何用 hooks 和 CI 保证提交质量?
  10. 如何处理误删分支、误 reset、误强推?

架构师级答案应该包含:

  • 命令本身。
  • 为什么这样做。
  • 对团队协作和历史记录的影响。
  • 是否会改写历史。
  • 是否影响 CI/CD。
  • 是否有回滚方案。

28. 官方资料 ​

建议优先阅读官方资料:

基于 VitePress 构建的个人学习文档知识库