前言
Git 是目前最流行的分布式版本控制系统,由 Linus Torvalds 于 2005 年创建。CVS、SVN 时代早已过去,Git 已经成为事实上的标准。
这篇文章整理了日常使用中最核心的 Git 概念和命令,不求面面俱到,但求实用、够用。
基础概念
三个区域
Git 的核心围绕三个区域:工作区(Working Directory)、暂存区(Staging Area / Index)和仓库(Repository)。
┌─────────────┐ git add ┌─────────────┐ git commit ┌─────────────┐│ 工作区 │ ──────────────→ │ 暂存区 │ ───────────────→ │ 仓库 ││ Working │ │ Staging │ │ Repository ││ Directory │ ←────────────── │ Area │ │ (.git) │└─────────────┘ git restore └─────────────┘ └─────────────┘ └── git reset --soft ──────────────────→ └── git reset --mixed ────────→| 区域 | 说明 | 对应命令 |
|---|---|---|
| 工作区 | 你实际编辑的文件目录 | 日常编辑 |
| 暂存区 | 下次提交的快照预览,在 .git/index 中 | git add |
| 仓库 | 保存所有提交历史的对象数据库 | git commit |
四种对象类型
| 对象 | 含义 | SHA-1 举例 |
|---|---|---|
| blob | 文件内容 | e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 |
| tree | 目录结构(指向 blob 和子 tree) | 同上格式 |
| commit | 一次快照(指向 tree + 父 commit + 作者信息) | 同上格式 |
| tag | 指向某个 commit 的命名标记 | 同上格式 |
你不需要手动处理 SHA-1 哈希值。日常使用中 Git 通过引用(分支名、标签名、HEAD)来间接管理这些对象。
起步配置
安装 Git 后的第一件事:
git config --global user.name "Your Name"git config --global user.email "your@email.com"| 配置作用域 | 命令 | 优先级 |
|---|---|---|
| 系统级 | git config --system | 最低 |
| 用户级 | git config --global | 中 |
| 项目级 | git config --local(默认) | 最高 |
常用配置:
git config --global init.defaultBranch main # 默认分支名从 master → maingit config --global pull.rebase true # pull 默认用 rebase 而非 mergegit config --global core.autocrlf input # 跨平台换行符处理git config --global core.editor vim # 默认编辑器git config --global credential.helper osxkeychain # macOS 凭据缓存查看配置:
git config --list # 列出所有配置git config --global --list # 只列出用户级git config user.name # 查单项基本操作
创建仓库
# 从零开始git init
# 克隆远程仓库git clone git@github.com:user/repo.gitgit clone https://github.com/user/repo.git日常提交流程
git status # 查看当前状态git add file.txt # 暂存单个文件git add . # 暂存所有变更git commit -m "feat: add login" # 提交
# 跳过暂存区(仅已跟踪的文件)git commit -a -m "直接提交所有修改"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 | 查看指定提交 |
# 实用组合git log --oneline --graph --all --decorate
# 查看某文件的修改历史git log --oneline -- README.md
# 谁动了哪几行git blame README.md查看差异
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 的可移动指针。
分支基础
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)
# 将 feature 合并到当前分支git checkout maingit merge feature| 合并类型 | 条件 | 结果 |
|---|---|---|
| Fast-forward | 目标分支在当前分支的直系祖先 | 直接移动指针,无新 commit |
| Three-way merge | 两个分支有分叉 | 生成一个新的 merge commit |
| Squash merge | --squash 参数 | 将所有提交压缩为一个 |
# 强制生成 merge commit(即使可以 fast-forward)git merge --no-ff feature
# 压缩合并git merge --squash featuregit commit -m "feat: add feature"--no-ff 在团队协作中很有用:它保留了”这是一个合并分支”的显式记录,方便后续追溯。但如果你追求线性历史,用 rebase 更合适。
变基(rebase)
Rebase 的本质是将一排提交”搬”到另一个基底上:
git checkout featuregit rebase main # 将 feature 的提交搬到 main 顶端| 对比 | merge | rebase |
|---|---|---|
| 历史 | 保留分支结构,产生分叉 | 线性历史,干净整洁 |
| 可追溯性 | 能看到”什么时候合并的” | 看不出原本的分支结构 |
| 安全 | 安全(无副作用) | |
| 冲突解决 | 一次解决,一个 merge commit | 每个提交都得解决冲突 |
# rebase 遇到冲突时git status # 查看冲突vim conflicted-file # 手动解决git add conflicted-filegit rebase --continue # 继续
git rebase --skip # 跳过当前提交git rebase --abort # 放弃 rebase,回到之前状态交互式变基(rebase -i)
git rebase -i HEAD~3 # 修改最近 3 个提交git rebase -i main # 从 main 分支分歧点开始重写交互界面中的可选操作:
| 命令 | 缩写 | 含义 |
|---|---|---|
pick | p | 保留该提交 |
reword | r | 保留内容,修改提交信息 |
edit | e | 保留提交,但停下来让我修改内容 |
squash | s | 合并到前一个提交 |
fixup | f | 类似 squash,但丢弃该提交的信息 |
drop | d | 删除该提交 |
# 常用场景:把多个 wip(work-in-progress)提交合并成一个pick abc1234 feat: add login pagesquash def5678 wip: login formsquash 789abcd wip: login validation不要对已经推送到远程的分支执行 rebase。如果你的同事已经基于这些提交做了工作,rebase 会让他们陷入困境。永远不要在公共分支上 rebase,这条规则要记牢。
后悔药(撤销与恢复)
Git 之所以强大,很大程度在于它给了你很多”吃后悔药”的机会。
工作区 / 暂存区的撤销
| 操作 | 命令 | 效果 |
|---|---|---|
| 撤销工作区修改 | git restore file | 丢弃工作区的改动 |
| 从暂存区撤回 | git restore --staged file | 文件留在工作区,但不再暂存 |
| 同时撤回暂存+工作区 | git checkout -- file(旧版) | restore 是新版推荐方式 |
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 “反做”旧提交 | 推送到远程后的撤销 |
# 只修改提交信息(不改内容)git commit --amend -m "新的提交信息"
# 添加漏掉的文件到上一次提交git add forgot-file.txtgit commit --amend --no-edit
# 撤销最近一次提交但保留改动git reset --soft HEAD~1
# 撤销最近三次提交中的第二次git revert HEAD~2已推送的提交:只能用 git revert。,强行推送会导致协作者的仓库混乱。git reset 会改写历史
工作区暂存(stash)
需要切换分支但手头工作还没做完时:
git stash # 暂存当前改动git stash list # 查看所有 stashgit stash pop # 恢复最近的 stash 并删除它git stash apply # 恢复但不删除git stash drop # 删除最近的 stashgit stash apply stash@{2} # 恢复指定的 stashgit stash -u # 也暂存未跟踪的文件| 场景 | 做法 |
|---|---|
| 切分支修紧急 bug | git stash → 切分支修 → 切回来 → git stash pop |
| 拉取远程更新 | git stash → git pull → git stash pop |
| 试不同的改动方案 | git stash → 试方案 A → git stash pop → 试方案 B |
远程协作
远程仓库管理
git remote -v # 查看远程仓库git remote add origin git@github.com:user/repo.gitgit 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) |
git pull # 拉取并合并(相当于 git fetch + git merge)git pull --rebase # 拉取并变基(推荐,历史更线性)git fetch # 只拉取,不合并推荐做法:git pull --rebase 替代默认的 git pull。这样你的本地提交会”排在”远程提交的后面,历史更干净。可以在配置中设为默认:
git config --global pull.rebase true分支推送策略
# 推送本地分支到远程同名分支git push origin feature
# 本地分支名和远程不同git push origin local-branch:remote-branch
# 删除远程分支git push origin --delete old-feature
# 本地删除后同步远程分支列表git remote prune origin标签(Tag)
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:挑选特定提交
# 把某个分支上的特定提交应用到当前分支git cherry-pick abc1234
# 挑选多个提交git cherry-pick abc1234 def5678
# 注意:会得到新的 commit hashbisect:二分查找 bug
# 开始二分查找git bisect startgit bisect bad # 当前版本有 buggit bisect good v1.0.0 # v1.0.0 是好的
# Git 会切换到一个中间提交,你测试后:git bisect good # 这个提交没问题# 或git bisect bad # 这个提交有问题
# 重复几次后 Git 会定位到第一个有问题的提交git bisect reset # 结束后回到原分支# 自动化git bisect start HEAD v1.0.0git bisect run npm test # 自动用测试脚本来判断git bisect resetreflog:最后的救命稻草
git reflog 记录了 HEAD 指针的所有移动记录,包括被 reset / rebase 丢弃的提交:
git reflog # 查看操作历史git reset --hard HEAD@{2} # 回到两步前的状态git checkout abc1234 # 直接 checkout 被丢弃的提交reflog 只在本地有效,且在 90 天后过期。如果你做了 git reset --hard 然后后悔了,赶紧查 reflog 救回来。
submodule:子模块
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/* | 紧急修复生产问题 | 修复后合并删除 |
或者更简洁的个人项目模型:
main # 永远可部署 └─ feat # 日常开发提交信息规范
推荐使用 Conventional Commits:
<type>: <简短描述>
<详细描述(可选)>
<引用 issue(可选)>| 类型 | 说明 |
|---|---|
feat | 新功能 |
fix | 修复 bug |
docs | 文档 |
style | 格式(不影响代码逻辑) |
refactor | 重构 |
test | 测试 |
chore | 构建/工具/依赖 |
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
# 忽略 Node 项目node_modules/dist/.env
# 忽略系统文件.DS_StoreThumbs.db
# 忽略 IDE 配置.idea/.vscode/*.swp如果文件已经被跟踪后再加入 .gitignore 不会生效,需要先取消跟踪:
git rm --cached <file> # 从 Git 中移除但不删文件修改最近的提交
# 提交信息写错了git commit --amend -m "新信息"
# 漏了一个文件git add forgotten-file.jsgit commit --amend --no-edit
# 注意:amend 会生成新的 commit hash,如果已经推送需要用 --force合并冲突处理
冲突标记:
<<<<<<< HEAD当前分支的内容=======合并进来的分支的内容>>>>>>> feature解决步骤:
- 找到冲突文件(
git status查看) - 手动编辑解决冲突
-
git add <file>标记已解决 -
git commit完成合并
# 或者用可视化工具git mergetool不小心 commit 了敏感信息
# 第一步:立刻删除git filter-branch --force --index-filter \ "git rm --cached --ignore-unmatch config/password.txt" \ --prune-empty -- --all
# 第二步:强制推送覆盖git push origin --force --all
# 第三步:通知团队所有成员重新 clonefilter-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-way | merge, rebase |
| 撤销 | 未提交用 restore,已提交用 reset/revert | restore, reset, revert |
| 远程协作 | fetch / pull / push | push, pull, fetch, remote |
| 后悔药 | stash 暂存,reflog 救急 | stash, reflog |
Git 的学习曲线确实有点陡,但日常最常用的命令其实不超过 10 个。记住所有命令是不可能的,把这张速查表收藏好,用到的时候回来翻翻就好。
评论
GitHub 登录后可评论。
评论区会在滚动到这里时自动加载。