技术备忘 Git版本控制

前言

Git 是目前最流行的分布式版本控制系统,由 Linus Torvalds 于 2005 年创建。CVSSVN 时代早已过去,Git 已经成为事实上的标准。

这篇文章整理了日常使用中最核心的 Git 概念和命令,不求面面俱到,但求实用、够用。

基础概念

三个区域

Git 的核心围绕三个区域:工作区(Working Directory)、暂存区(Staging Area / Index)和仓库(Repository)。

bash
┌─────────────┐ git add ┌─────────────┐ git commit ┌─────────────┐
工作区 ──────────────→ 暂存区 ───────────────→ 仓库
Working Staging Repository
Directory ←────────────── Area (.git) │
└─────────────┘ git restore └─────────────┘ └─────────────┘
└── git reset --soft ──────────────────→
└── git reset --mixed ────────→
区域说明对应命令
工作区你实际编辑的文件目录日常编辑
暂存区下次提交的快照预览,在 .git/indexgit add
仓库保存所有提交历史的对象数据库git commit

四种对象类型

对象含义SHA-1 举例
blob文件内容e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
tree目录结构(指向 blob 和子 tree)同上格式
commit一次快照(指向 tree + 父 commit + 作者信息)同上格式
tag指向某个 commit 的命名标记同上格式
NOTE

你不需要手动处理 SHA-1 哈希值。日常使用中 Git 通过引用(分支名、标签名、HEAD)来间接管理这些对象。

起步配置

安装 Git 后的第一件事:

bash
git config --global user.name "Your Name"
git config --global user.email "your@email.com"
配置作用域命令优先级
系统级git config --system最低
用户级git config --global
项目级git config --local(默认)最高

常用配置:

bash
git config --global init.defaultBranch main # 默认分支名从 master → main
git config --global pull.rebase true # pull 默认用 rebase 而非 merge
git config --global core.autocrlf input # 跨平台换行符处理
git config --global core.editor vim # 默认编辑器
git config --global credential.helper osxkeychain # macOS 凭据缓存

查看配置:

bash
git config --list # 列出所有配置
git config --global --list # 只列出用户级
git config user.name # 查单项

基本操作

创建仓库

bash
# 从零开始
git init
# 克隆远程仓库
git clone git@github.com:user/repo.git
git clone https://github.com/user/repo.git

日常提交流程

bash
git status # 查看当前状态
git add file.txt # 暂存单个文件
git add . # 暂存所有变更
git commit -m "feat: add login" # 提交
# 跳过暂存区(仅已跟踪的文件)
git commit -a -m "直接提交所有修改"
NOTE

git commit -a 只对已被跟踪的文件有效。新文件仍然需要先用 git add

查看历史

命令效果
git log完整提交历史
git log --oneline一行一个提交
git log --graph --all --oneline分支图(最常用)
git log -p显示每次改动的 diff
git log --author="name"按作者过滤
git log --since="2023-01-01"按时间过滤
git log --grep="fix"按提交信息关键词过滤
git log --oneline --stat简略统计每次改动的文件
git show HEAD查看最近一次提交详情
git show abc1234查看指定提交
bash
# 实用组合
git log --oneline --graph --all --decorate
# 查看某文件的修改历史
git log --oneline -- README.md
# 谁动了哪几行
git blame README.md

查看差异

bash
git diff # 工作区 vs 暂存区
git diff --staged # 暂存区 vs 仓库(即将提交的内容)
git diff HEAD # 工作区 vs 仓库
git diff branch1..branch2 # 两个分支的差异
git diff commit1..commit2 # 两个提交的差异
git diff --stat # 只统计改动的文件

分支管理

分支是 Git 最强大的特性之一。Git 的分支本质上只是一个指向 commit 的可移动指针

分支基础

bash
git branch # 列出本地分支(* 表示当前分支)
git branch -r # 列出远程分支
git branch -a # 列出所有分支
git branch feature # 创建分支
git checkout feature # 切换分支
git checkout -b feature # 创建并切换(常用)
git switch feature # 新版切换(Git 2.23+)
git switch -c feature # 新版创建并切换
git branch -d feature # 删除分支(已合并)
git branch -D feature # 强制删除分支(未合并也删)

合并(merge)

bash
# 将 feature 合并到当前分支
git checkout main
git merge feature
合并类型条件结果
Fast-forward目标分支在当前分支的直系祖先直接移动指针,无新 commit
Three-way merge两个分支有分叉生成一个新的 merge commit
Squash merge--squash 参数将所有提交压缩为一个
bash
# 强制生成 merge commit(即使可以 fast-forward)
git merge --no-ff feature
# 压缩合并
git merge --squash feature
git commit -m "feat: add feature"
NOTE

--no-ff 在团队协作中很有用:它保留了”这是一个合并分支”的显式记录,方便后续追溯。但如果你追求线性历史,用 rebase 更合适。

变基(rebase)

Rebase 的本质是将一排提交”搬”到另一个基底上:

bash
git checkout feature
git rebase main # 将 feature 的提交搬到 main 顶端
对比mergerebase
历史保留分支结构,产生分叉线性历史,干净整洁
可追溯性能看到”什么时候合并的”看不出原本的分支结构
安全安全(无副作用)有风险不要对已推送的公共分支 rebase
冲突解决一次解决,一个 merge commit每个提交都得解决冲突
bash
# rebase 遇到冲突时
git status # 查看冲突
vim conflicted-file # 手动解决
git add conflicted-file
git rebase --continue # 继续
git rebase --skip # 跳过当前提交
git rebase --abort # 放弃 rebase,回到之前状态

交互式变基(rebase -i)

bash
git rebase -i HEAD~3 # 修改最近 3 个提交
git rebase -i main # 从 main 分支分歧点开始重写

交互界面中的可选操作:

命令缩写含义
pickp保留该提交
rewordr保留内容,修改提交信息
edite保留提交,但停下来让我修改内容
squashs合并到前一个提交
fixupf类似 squash,但丢弃该提交的信息
dropd删除该提交
bash
# 常用场景:把多个 wip(work-in-progress)提交合并成一个
pick abc1234 feat: add login page
squash def5678 wip: login form
squash 789abcd wip: login validation
NOTE

不要对已经推送到远程的分支执行 rebase。如果你的同事已经基于这些提交做了工作,rebase 会让他们陷入困境。永远不要在公共分支上 rebase,这条规则要记牢。

后悔药(撤销与恢复)

Git 之所以强大,很大程度在于它给了你很多”吃后悔药”的机会。

工作区 / 暂存区的撤销

操作命令效果
撤销工作区修改git restore file丢弃工作区的改动
从暂存区撤回git restore --staged file文件留在工作区,但不再暂存
同时撤回暂存+工作区git checkout -- file(旧版)restore 是新版推荐方式
bash
git restore README.md # 撤销对 README.md 未暂存的修改
git restore --staged index.html # 把 index.html 从暂存区移出
git restore --staged --worktree . # 撤销所有未提交的改动(⚠ 谨慎)

已提交的撤销

命令效果适用场景
git reset --soft HEAD~1撤销 commit,改动回到暂存区提交信息写错了,想重写
git reset --mixed HEAD~1撤销 commit,改动回到工作区提交了不该提交的内容
git reset --hard HEAD~1彻底丢弃 commit 和改动玩脱了想完全删除最近的提交
git revert HEAD生成一个新 commit “反做”旧提交推送到远程后的撤销
bash
# 只修改提交信息(不改内容)
git commit --amend -m "新的提交信息"
# 添加漏掉的文件到上一次提交
git add forgot-file.txt
git commit --amend --no-edit
# 撤销最近一次提交但保留改动
git reset --soft HEAD~1
# 撤销最近三次提交中的第二次
git revert HEAD~2
NOTE

已推送的提交:只能用 git revertgit reset 会改写历史,强行推送会导致协作者的仓库混乱。

工作区暂存(stash)

需要切换分支但手头工作还没做完时:

bash
git stash # 暂存当前改动
git stash list # 查看所有 stash
git stash pop # 恢复最近的 stash 并删除它
git stash apply # 恢复但不删除
git stash drop # 删除最近的 stash
git stash apply stash@{2} # 恢复指定的 stash
git stash -u # 也暂存未跟踪的文件
场景做法
切分支修紧急 buggit stash → 切分支修 → 切回来 → git stash pop
拉取远程更新git stashgit pullgit stash pop
试不同的改动方案git stash → 试方案 A → git stash pop → 试方案 B

远程协作

远程仓库管理

bash
git remote -v # 查看远程仓库
git remote add origin git@github.com:user/repo.git
git remote remove origin
# 修改远程地址(换了仓库地址时用)
git remote set-url origin git@github.com:new-user/new-repo.git

推送与拉取

命令含义
git push origin main推送本地 main 到远程 main
git push origin feature:feature推送本地 feature 到远程 feature
git push origin --delete feature删除远程分支
git push --tags推送所有标签
git push -u origin main推送并建立上游关联(后续可只用 git push
bash
git pull # 拉取并合并(相当于 git fetch + git merge)
git pull --rebase # 拉取并变基(推荐,历史更线性)
git fetch # 只拉取,不合并
NOTE

推荐做法:git pull --rebase 替代默认的 git pull。这样你的本地提交会”排在”远程提交的后面,历史更干净。可以在配置中设为默认:

bash
git config --global pull.rebase true

分支推送策略

bash
# 推送本地分支到远程同名分支
git push origin feature
# 本地分支名和远程不同
git push origin local-branch:remote-branch
# 删除远程分支
git push origin --delete old-feature
# 本地删除后同步远程分支列表
git remote prune origin

标签(Tag)

bash
git tag # 列出标签
git tag v1.0.0 # 创建轻量标签
git tag -a v1.0.0 -m "Release v1.0.0" # 创建附注标签
git tag -d v1.0.0 # 删除本地标签
git push origin v1.0.0 # 推送单个标签
git push origin --tags # 推送所有标签
git push origin --delete v1.0.0 # 删除远程标签
标签类型特点
轻量标签只是一个指针,不带额外信息
附注标签存储在对象数据库中,含创建者、日期、签名信息

对于发布版本,应该使用附注标签 -a

高级操作

cherry-pick:挑选特定提交

bash
# 把某个分支上的特定提交应用到当前分支
git cherry-pick abc1234
# 挑选多个提交
git cherry-pick abc1234 def5678
# 注意:会得到新的 commit hash

bisect:二分查找 bug

bash
# 开始二分查找
git bisect start
git bisect bad # 当前版本有 bug
git bisect good v1.0.0 # v1.0.0 是好的
# Git 会切换到一个中间提交,你测试后:
git bisect good # 这个提交没问题
# 或
git bisect bad # 这个提交有问题
# 重复几次后 Git 会定位到第一个有问题的提交
git bisect reset # 结束后回到原分支
bash
# 自动化
git bisect start HEAD v1.0.0
git bisect run npm test # 自动用测试脚本来判断
git bisect reset

reflog:最后的救命稻草

git reflog 记录了 HEAD 指针的所有移动记录,包括被 reset / rebase 丢弃的提交:

bash
git reflog # 查看操作历史
git reset --hard HEAD@{2} # 回到两步前的状态
git checkout abc1234 # 直接 checkout 被丢弃的提交
NOTE

reflog 只在本地有效,且在 90 天后过期。如果你做了 git reset --hard 然后后悔了,赶紧查 reflog 救回来。

submodule:子模块

bash
git submodule add git@github.com:user/lib.git lib/
git submodule update --init --recursive
# 克隆含子模块的仓库
git clone --recursive git@github.com:user/project.git

工作流

推荐的分支模型

main ──────●──────────●──────────────●─────────
\ / /
feature-1 ●──●──● /
/
feature-2 ●──●──────────────────
分支用途生命周期
main生产就绪的代码永久
feature/*新功能开发功能完成后合并删除
bugfix/*修 bug修复后合并删除
hotfix/*紧急修复生产问题修复后合并删除

或者更简洁的个人项目模型:

bash
main # 永远可部署
└─ feat # 日常开发

提交信息规范

推荐使用 Conventional Commits:

<type>: <简短描述>
<详细描述(可选)>
<引用 issue(可选)>
类型说明
feat新功能
fix修复 bug
docs文档
style格式(不影响代码逻辑)
refactor重构
test测试
chore构建/工具/依赖
bash
feat: add user login page
Implement OAuth2 login flow with GitHub and Google.
Closes #42

常用命令速查表

创建与获取

命令说明
git init初始化新仓库
git clone <url>克隆远程仓库

快照操作

命令说明
git status查看状态
git add <file>添加文件到暂存区
git commit -m "msg"提交暂存区
git commit -a -m "msg"暂存+提交(已跟踪文件)
git diff查看工作区与暂存区差异
git diff --staged查看暂存区与仓库差异
git rm <file>从仓库和文件系统删除
git mv <old> <new>移动/重命名文件
git restore <file>撤销工作区修改
git restore --staged <file>撤出暂存区

分支与合并

命令说明
git branch列出分支
git branch <name>创建分支
git checkout -b <name>创建并切换
git switch -c <name>创建并切换(新版)
git merge <branch>合并分支到当前
git merge --abort放弃合并
git rebase <branch>变基
git rebase --abort放弃变基
git rebase -i HEAD~n交互式变基

检查与比较

命令说明
git log查看历史
git log --oneline --graph --all分支图
git show <commit>查看某次提交详情
git blame <file>每行谁改的
git bisect start二分查找

远程操作

命令说明
git remote -v查看远程仓库
git fetch拉取远程更新(不合并)
git pull拉取并合并
git pull --rebase拉取并变基
git push推送
git push -u origin <branch>推送并设置上游

撤销与恢复

命令说明
git reset --soft <ref>撤销 commit,保留变更在暂存区
git reset --mixed <ref>撤销 commit,保留变更在工作区
git reset --hard <ref>丢弃变更
git revert <commit>生成反向 commit 来撤销
git commit --amend修改最近的提交信息
git stash暂存未完成的工作
git stash pop恢复暂存的工作
git reflog查看 HEAD 移动历史

常见问题

设置.gitignore

bash
# 忽略 Node 项目
node_modules/
dist/
.env
# 忽略系统文件
.DS_Store
Thumbs.db
# 忽略 IDE 配置
.idea/
.vscode/
*.swp

如果文件已经被跟踪后再加入 .gitignore 不会生效,需要先取消跟踪:

bash
git rm --cached <file> # 从 Git 中移除但不删文件

修改最近的提交

bash
# 提交信息写错了
git commit --amend -m "新信息"
# 漏了一个文件
git add forgotten-file.js
git commit --amend --no-edit
# 注意:amend 会生成新的 commit hash,如果已经推送需要用 --force

合并冲突处理

冲突标记:

<<<<<<< HEAD
当前分支的内容
=======
合并进来的分支的内容
>>>>>>> feature

解决步骤:

  • 找到冲突文件(git status 查看)
  • 手动编辑解决冲突
  • git add <file> 标记已解决
  • git commit 完成合并
bash
# 或者用可视化工具
git mergetool

不小心 commit 了敏感信息

bash
# 第一步:立刻删除
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch config/password.txt" \
--prune-empty -- --all
# 第二步:强制推送覆盖
git push origin --force --all
# 第三步:通知团队所有成员重新 clone
NOTE

filter-branch重写整个历史。更好的选择是使用 git filter-repo(一个独立的工具)。无论如何,密码泄露后第一时间去对应平台轮换密钥,Git 操作只是清理历史。

学习路线图

  • 理解三个区域(工作区、暂存区、仓库)
  • 掌握日常 add / commit / push
  • 熟练使用分支创建和切换
  • 理解 merge 与 rebase 的区别
  • 学会处理合并冲突
  • 掌握 reset / revert / restore 的撤销姿势
  • 学会用 stash 暂存未完成工作
  • 熟悉 reflog 的恢复方法
  • 理解远程协作的工作流
  • 能通过 bisect 定位 bug

总结

知识点要点核心命令
区域模型工作区 → 暂存区 → 仓库add, commit
分支轻量指针,创建开销极小branch, checkout, switch
合并fast-forward vs three-waymerge, rebase
撤销未提交用 restore,已提交用 reset/revertrestore, reset, revert
远程协作fetch / pull / pushpush, pull, fetch, remote
后悔药stash 暂存,reflog 救急stash, reflog

Git 的学习曲线确实有点陡,但日常最常用的命令其实不超过 10 个。记住所有命令是不可能的,把这张速查表收藏好,用到的时候回来翻翻就好。


参考来源