Git 从入门到精通:命令、参数、工作流与实战练习

Git 从入门到精通:命令、参数、工作流与实战练习

适用人群:第一次接触 Git 的学习者,以及希望系统补齐分支、合并、变基、撤销、恢复与团队协作知识的开发者。
示例默认使用 Git 2.x,命令行环境为 macOS、Linux 或 Git Bash。
文中的 <名称> 表示需要替换的内容,[可选项] 表示可以省略。


目录

  1. Git 到底是什么
  2. 安装与首次配置
  3. 必须理解的四个区域
  4. 创建和获取仓库
  5. 日常基本操作
  6. 查看历史与差异
  7. 分支详解
  8. 合并、冲突与变基
  9. 远程仓库协作
  10. 撤销、回退与恢复
  11. 标签与版本发布
  12. 暂存工作:stash
  13. 选择性移植:cherry-pick
  14. 查错与定位
  15. 子模块、工作树与大文件
  16. 配置、别名、钩子与忽略规则
  17. 团队工作流
  18. 综合练习案例
  19. 常见问题与危险命令
  20. 命令速查表与进阶路线

1. Git 到底是什么

Git 是一个分布式版本控制系统。它记录的核心不是“某个文件改了几行”,而是项目在一系列时间点上的快照。

1.1 Git、GitHub、GitLab 的区别

  • Git:本地版本控制工具,不联网也能提交、建分支和查看历史。
  • GitHub / GitLab / Gitee:托管 Git 仓库并提供代码评审、议题、CI/CD 等协作功能的平台。
  • 远程仓库:服务器上的 Git 仓库副本;它不是 Git 的必需品,但团队协作通常需要它。

1.2 Git 的核心对象

  • blob:文件内容。
  • tree:目录结构及其包含的对象。
  • commit:一次项目快照,含作者、时间、说明和父提交。
  • tag:指向某个提交的固定标记。
  • branch:指向某个提交、会随新提交向前移动的引用。
  • HEAD:你当前所在位置,通常指向当前分支。

一个普通提交历史可理解为:

A <- B <- C   main
          ^
         HEAD

提交 C 记住自己的父提交 B;分支 main 指向 CHEAD 指向 main


2. 安装与首次配置

2.1 检查版本

git --version
git --help
git help <命令>
git <命令> -h
  • git help <命令>:打开完整手册,如 git help commit
  • git <命令> -h:显示简短参数帮助。

2.2 配置身份

git config --global user.name "Zhang San"
git config --global user.email "zhangsan@example.com"

常见配置层级:

层级 参数 作用范围
系统 --system 当前电脑全部用户
用户 --global 当前用户全部仓库
仓库 --local 当前仓库,默认层级
临时 -c key=value 仅本次命令

后一级会覆盖前一级。查看配置及来源:

git config --list --show-origin
git config --global --get user.name
git config --local user.email "work@example.com"
git config --global --unset <键>
git config --global --edit

推荐配置:

git config --global init.defaultBranch main
git config --global core.editor "code --wait"
git config --global pull.rebase false
git config --global fetch.prune true
git config --global color.ui auto

pull.rebase 是团队策略。设为 false 表示拉取时默认合并;团队采用线性历史时可设为 true。不确定时不要盲目全局修改。

练习 1:建立个人配置

git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
git config --list --show-origin

目标:能指出每条配置来自哪个文件,并理解仓库级配置为什么能覆盖全局配置。


3. 必须理解的四个区域

工作区 --git add--> 暂存区 --git commit--> 本地仓库 --git push--> 远程仓库
   ^                    |
   |---- restore -------|
  1. 工作区(working tree):实际看到和编辑的文件。
  2. 暂存区(index/staging area):下一次提交准备包含的快照。
  3. 本地仓库(repository).git 中保存的提交和引用。
  4. 远程仓库(remote repository):GitHub 等服务器上的仓库。

一个文件还可能处于以下状态:

  • untracked:未跟踪,新文件尚未加入 Git。
  • tracked:已跟踪。
  • modified:工作区内容相对暂存区有变化。
  • staged:变化已经放入暂存区。
  • committed:变化已经进入本地提交。

git status 是迷路时的第一条命令。


4. 创建和获取仓库

4.1 git init:初始化仓库

mkdir demo && cd demo
git init
git init --initial-branch=main
git init --bare server.git

重要参数:

  • -b, --initial-branch=<名称>:指定初始分支。
  • --bare:创建裸仓库,没有工作区,常用于服务器中转仓库。
  • --shared[=<权限>]:允许用户组共享仓库。
  • --separate-git-dir=<路径>:让工作区和 Git 元数据分开存放。

重复运行 git init 一般不会丢失历史,只会重新初始化必要文件。

4.2 git clone:克隆仓库

git clone https://example.com/team/app.git
git clone https://example.com/team/app.git my-app
git clone --branch develop --single-branch <URL>
git clone --depth 1 <URL>
git clone --filter=blob:none <URL>

重要参数:

  • -b, --branch <名称>:克隆后检出指定分支或标签。
  • --single-branch:只获取一个分支的历史。
  • --depth <n>:浅克隆,只取最近 n 层历史。
  • --filter=blob:none:部分克隆,按需下载文件内容,适合超大仓库。
  • --recurse-submodules:同时初始化并拉取子模块。
  • --origin <名称>:将默认远程名从 origin 改成指定名称。
  • --bare:克隆成裸仓库。
  • --mirror:镜像全部引用,通常用于备份迁移,比 --bare 更完整。

浅克隆节省时间和空间,但历史分析、变基和某些 CI 工具可能受限。可用 git fetch --unshallow 补全历史。


5. 日常基本操作

5.1 git status:查看状态

git status
git status --short
git status --branch --short
git status --ignored
  • -s, --short:紧凑输出;两列状态分别代表暂存区和工作区。
  • -b, --branch:显示分支及领先/落后情况。
  • --ignored:同时显示被忽略文件。
  • --porcelain:机器可稳定解析的格式。

短格式示例:

 M app.js   # 工作区修改,未暂存
M  api.js   # 已暂存
MM both.js  # 已暂存后又继续修改
?? note.md  # 未跟踪

5.2 git add:放入暂存区

git add README.md
git add src/
git add .
git add -A
git add -u
git add -p
git add -N new-file.txt

重要参数:

  • .:加入当前目录及子目录的变化。
  • -A, --all:加入整个仓库的新增、修改和删除。
  • -u, --update:只加入已跟踪文件的修改和删除,不加入新文件。
  • -p, --patch:按代码块交互式暂存,适合拆分提交。
  • -i, --interactive:交互式选择。
  • -N, --intent-to-add:标记“准备添加”,便于在 diff 中看到完整新文件。
  • -f, --force:强行加入被 .gitignore 忽略的文件,慎用。

git add 不是“添加文件名”,而是把当前内容快照复制到暂存区。暂存后继续编辑,需再次 add 才会纳入提交。

5.3 git commit:创建提交

git commit -m "feat: add login page"
git commit -am "fix: validate email"
git commit --amend
git commit --amend --no-edit
git commit --allow-empty -m "chore: trigger CI"

重要参数:

  • -m <信息>:直接写提交说明,可重复使用形成多段说明。
  • -a, --all:自动暂存已跟踪文件的修改和删除;不会加入新文件
  • --amend:替换最近一次提交,可修改内容或说明。
  • --no-edit:沿用原提交说明,常和 --amend 配合。
  • --author="姓名 <邮箱>":指定作者。
  • --date=<日期>:指定作者日期。
  • --no-verify:绕过提交钩子,除非明确知道原因,否则不要使用。
  • -S, --gpg-sign:对提交签名。
  • --allow-empty:允许没有文件变化的提交。
  • --fixup=<提交>:创建用于自动整理历史的修正提交。

推荐提交信息结构:

<类型>(可选范围): 简短说明

可选的详细原因、实现方式和影响

可选的关联事项

常见类型:featfixdocsrefactortestchore

5.4 git rmgit mv

git rm old.txt
git rm -r old-dir/
git rm --cached secret.env
git mv old-name.md new-name.md
  • git rm:从工作区删除并把删除放入暂存区。
  • --cached:只停止跟踪,保留本地文件,常用于误提交的配置文件。
  • -f:强制删除有未暂存/未提交变化的文件,慎用。
  • git mv:移动或重命名并暂存;本质相当于移动后执行 git add -A

Git 不存储“重命名”对象,而是根据内容相似度在展示历史时推断。

练习 2:完成第一次提交

mkdir git-basic-lab && cd git-basic-lab
git init -b main
# 创建 README.md,并写入项目标题
git status
git add README.md
git status --short
git commit -m "docs: initialize README"
git log --oneline

继续修改 README,分别观察修改前、暂存后、再次修改后的 git status --short


6. 查看历史与差异

6.1 git log:浏览提交历史

git log
git log --oneline --graph --decorate --all
git log -5
git log --author="Zhang"
git log --since="2026-01-01" --until="2026-02-01"
git log --grep="login"
git log -- src/auth.js
git log -p -- src/auth.js
git log -S"validateToken" --all
git log A..B
git log A...B

常用参数:

  • --oneline:一行一个提交。
  • --graph:绘制文本分支图。
  • --decorate:显示分支和标签。
  • --all:显示所有引用可达历史。
  • -p:显示每次提交的补丁。
  • --stat:显示文件及行数统计。
  • --name-only / --name-status:显示文件名 / 文件状态。
  • -n <数量>:限制条数。
  • --first-parent:只沿第一父提交查看,适合主分支发布史。
  • --follow -- <文件>:尝试跨重命名追踪单个文件。
  • -S<文本>:查找使某段文本出现次数改变的提交。
  • -G<正则>:查找补丁中匹配正则的提交。
  • --reverse:从旧到新显示。

范围语法:

  • A..B:B 可达但 A 不可达的提交,常理解为“B 比 A 多了什么”。
  • A...B:A、B 各自从共同祖先分叉后的提交。
  • ^A B:排除 A 可达、包含 B 可达,与 A..B 类似。

推荐别名:

git config --global alias.lg "log --graph --decorate --oneline --all"
git lg

6.2 git show:查看对象或提交

git show HEAD
git show HEAD~2
git show <提交>:path/to/file
git show --stat <提交>
git show --name-only <提交>
  • HEAD~2:沿第一父提交向前两次。
  • HEAD^2:合并提交的第二个父提交。
  • <提交>:<路径>:查看某次提交中的文件内容。

6.3 git diff:比较差异

git diff                       # 工作区 vs 暂存区
git diff --staged              # 暂存区 vs HEAD
git diff HEAD                  # 工作区+暂存区 vs HEAD
git diff <提交1> <提交2>
git diff <分支1>...<分支2>
git diff --stat
git diff --name-status
git diff --word-diff
git diff --check

关键参数:

  • --cached, --staged:查看准备提交的内容。
  • --stat:只看统计。
  • --name-only:只看文件名。
  • --name-status:看文件名和新增/修改/删除状态。
  • --word-diff:按词显示差异。
  • --check:检查空白错误。
  • --color-words:彩色词级差异。
  • -- <路径>:限定路径,避免路径被当成修订名。

6.4 git blame:逐行追溯

git blame src/app.js
git blame -L 20,40 src/app.js
git blame -w src/app.js
git blame -C src/app.js
  • -L <起始>,<结束>:限制行范围。
  • -w:忽略空白变化。
  • -C:检测从其他文件复制或移动的代码。

blame 用于了解上下文,不应用于“追责”;结合对应提交说明和评审记录判断原因。


7. 分支详解

7.1 git branch:创建、查看、删除分支

git branch
git branch --all
git branch --verbose --verbose
git branch feature/login
git branch feature/login <起点>
git branch -m new-name
git branch -d feature/login
git branch -D feature/login
git branch --merged
git branch --no-merged

重要参数:

  • -a, --all:本地和远程跟踪分支。
  • -r, --remotes:只看远程跟踪分支。
  • -v / -vv:显示最近提交;-vv 还显示上游关系。
  • -m:重命名分支;-M 强制重命名。
  • -d:仅删除已合并分支。
  • -D:强制删除,即使未合并。
  • --merged [提交]:列出已合入指定提交的分支。
  • --no-merged [提交]:列出尚未合入的分支。
  • --contains <提交>:列出包含某提交的分支。
  • -u, --set-upstream-to=<远程/分支>:设置当前分支的上游。

创建分支只是新建一个轻量指针,不会自动切换。

7.2 git switch:切换分支

git switch main
git switch -c feature/login
git switch -c hotfix v1.2.0
git switch -C temp <提交>
git switch -
git switch --detach <提交>
git switch --track origin/develop
  • -c:创建并切换,已有同名分支则失败。
  • -C:创建或重置并切换;可能移动已有分支,慎用。
  • -:返回上一个分支。
  • --detach:进入分离 HEAD,不依附分支。
  • --track:建立本地分支并跟踪远程分支。
  • --discard-changes:丢弃阻碍切换的本地变化,高风险。

7.3 git checkout:传统多用途命令

git checkout main
git checkout -b feature/login
git checkout -- file.txt
git checkout <提交> -- file.txt

历史上 checkout 同时负责切分支和恢复文件,容易混淆。新代码建议:

  • 切换分支用 git switch
  • 恢复文件用 git restore

7.4 分离 HEAD

执行 git switch --detach <提交> 后,HEAD 直接指向提交。此时可以试验和提交,但新提交没有分支名保护,之后可能被回收。若要保留:

git switch -c experiment

练习 3:功能分支

git switch -c feature/profile
# 修改 README 并提交
git add README.md
git commit -m "feat: add profile description"
git switch main
git log --oneline --graph --all
git switch feature/profile

目标:观察 main 没有移动,而 feature/profile 指向新提交。


8. 合并、冲突与变基

8.1 git merge:合并分支

先切到“接收变化”的分支:

git switch main
git merge feature/login

常见结果:

  1. Fast-forward:main 没有独立新提交,指针直接前移。
  2. 三方合并:双方都发展,创建一个有两个父提交的合并提交。
  3. 冲突:同一区域的变化无法自动统一,需要人工处理。

重要参数:

git merge --ff-only feature/login
git merge --no-ff feature/login
git merge --squash feature/login
git merge --no-commit feature/login
git merge --abort
git merge --continue
  • --ff-only:只允许快进,否则失败;适合强制线性历史。
  • --no-ff:即使可以快进也创建合并提交,保留功能分支边界。
  • --squash:把目标分支的综合变化放入暂存区,不保留其分支提交关系,之后需手动提交。
  • --no-commit:合并成功后先暂停,以便检查再提交;快进时不会暂停。
  • --abort:尽量回到合并前状态。
  • --continue:解决冲突并暂存后继续。
  • -X ours / -X theirs:冲突时偏好一侧,但不是无条件使用整侧内容,慎用。

8.2 解决冲突

冲突文件会出现:

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

标准步骤:

git status
# 编辑冲突文件,删除标记,保留正确结果
git add <已解决文件>
git merge --continue
# 若不想继续:git merge --abort

辅助命令:

git diff --name-only --diff-filter=U
git checkout --ours <文件>
git checkout --theirs <文件>
git mergetool

注意:在 rebase 冲突中,ours/theirs 的直觉可能与 merge 相反,务必查看内容,不要只凭名称选择。

8.3 git rebase:变基

假设功能分支从旧 main 分出:

      C---D  feature
     /
A---B---E---F  main

在 feature 上执行:

git switch feature
git rebase main

得到:

A---B---E---F  main
             \
              C'---D'  feature

C'D' 是新提交,提交哈希变化了。常用参数:

git rebase main
git rebase --onto <新基底> <旧基底> <分支>
git rebase --continue
git rebase --skip
git rebase --abort
git rebase --autostash main
git rebase -i HEAD~5
git rebase -i --autosquash main
  • --onto:精确移动一段提交。
  • --continue:解决冲突并 add 后继续。
  • --skip:跳过当前补丁,可能丢掉该提交的变化。
  • --abort:恢复到 rebase 前。
  • --autostash:临时保存工作区变化,完成后恢复;仍可能冲突。
  • -i, --interactive:交互式整理历史。
  • --autosquash:自动排列 fixup! / squash! 提交。

交互式 rebase 指令:

指令 简写 用途
pick p 保留提交
reword r 修改提交说明
edit e 暂停以修改提交
squash s 合入前一个提交并编辑说明
fixup f 合入前一个提交并丢弃本提交说明
drop d 删除提交
break b 在此暂停

黄金规则:不要随意 rebase 已被他人基于其开发的公共提交。 因为 rebase 改写提交身份,会迫使其他人处理重复历史。

8.4 merge 与 rebase 的选择

场景 建议
更新尚未共享的个人功能分支 rebase main,历史清晰
合入公共主分支并保留完整上下文 merge --no-ff
团队要求严格线性历史 更新时 rebase,合入时 fast-forward/squash
不确定提交是否已被他人使用 优先 merge

9. 远程仓库协作

9.1 git remote:管理远程地址

git remote -v
git remote add origin <URL>
git remote show origin
git remote get-url origin
git remote set-url origin <新URL>
git remote rename origin upstream
git remote remove origin

origin 只是默认名称,不是特殊关键字。Fork 工作流常见:

  • origin:自己的 Fork。
  • upstream:原项目仓库。

9.2 git fetch:只下载,不合并

git fetch origin
git fetch --all
git fetch --prune
git fetch origin main
git fetch --tags
git fetch --depth 100
git fetch --unshallow
  • --all:抓取所有远程。
  • -p, --prune:清理远端已删除的远程跟踪分支。
  • --tags:显式抓取全部标签。
  • --depth:调整浅克隆深度。
  • --unshallow:把浅克隆补为完整仓库。
  • --dry-run:预览。

origin/main 是本地记录的远程跟踪引用;只有 fetch 后才会更新。

9.3 git pull:抓取并整合

pull = fetch + merge/rebase

git pull
git pull --ff-only
git pull --rebase
git pull --rebase=merges
git pull --no-rebase
  • --ff-only:只接受快进,避免意外产生合并提交。
  • --rebase:把本地提交变基到远程新提交之后。
  • --rebase=merges:尝试保留本地合并结构。
  • --no-rebase:使用合并。

新手推荐先拆开执行以看清过程:

git fetch origin
git log --oneline --graph --all
git merge origin/main       # 或 git rebase origin/main

9.4 git push:上传引用和对象

git push -u origin feature/login
git push
git push origin main
git push --tags
git push origin v1.0.0
git push origin --delete feature/old
git push --force-with-lease
git push --dry-run

重要参数:

  • -u, --set-upstream:设置上游,以后可直接 git push / git pull
  • --all:推送所有本地分支。
  • --tags:推送全部标签;普通 push 不会自动推送所有标签。
  • --delete:删除远程分支或标签引用。
  • --force:无条件强推,可能覆盖他人工作,通常不要用。
  • --force-with-lease:仅当远端仍是自己预期的状态才强推;改写个人远程分支后首选。
  • --atomic:要求多个引用全部成功或全部失败,服务器需支持。
  • --dry-run:预览,不实际推送。

即使 --force-with-lease 更安全,推送前也应先 git fetch,并确认目标是个人分支而非受保护主分支。

9.5 上游分支

查看:

git branch -vv

设置或取消:

git branch --set-upstream-to=origin/main main
git branch --unset-upstream

练习 4:模拟远程协作

无需联网也可完成:

git init --bare remote.git
git clone remote.git alice
git clone remote.git bob
  1. Alice 创建文件、提交并推送 main。
  2. Bob 执行 git pull,修改另一个文件并推送。
  3. Alice 先制造一个本地提交,再抓取 Bob 的提交。
  4. 分别尝试 merge 和 rebase,画出两种历史图。

10. 撤销、回退与恢复

先判断变化位于哪里,再选择命令:

目标 推荐命令 是否改写历史
丢弃未暂存的文件修改 git restore <文件>
取消暂存,保留工作区修改 git restore --staged <文件>
修改最近一次未共享提交 git commit --amend
撤销已共享提交 git revert <提交> 否,新增反向提交
本地分支退回旧提交 git reset 可能
找回误删分支/提交 git reflog

10.1 git restore:恢复文件

git restore file.txt
git restore --staged file.txt
git restore --source=HEAD~2 file.txt
git restore --source=<提交> --staged --worktree file.txt
git restore -p file.txt
  • 默认:从暂存区恢复工作区,丢弃未暂存修改。
  • --staged:从 HEAD 恢复暂存区,即取消暂存,默认保留工作区。
  • -s, --source=<来源>:指定恢复来源。
  • -W, --worktree:恢复工作区。
  • -S, --staged:恢复暂存区。
  • -p:按块选择。

git restore file.txt 会覆盖未提交修改,执行前可先 git diffgit stash

10.2 git reset:移动分支并重置状态

三种主要模式:

git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1
模式 分支/HEAD 暂存区 工作区 典型用途
--soft 重置 保留 保留 撤销提交,重新组织提交
--mixed(默认) 重置 重置 保留 撤销提交和暂存,保留修改
--hard 重置 重置 重置 完全丢弃已跟踪文件变化

其他形式:

git reset HEAD file.txt            # 传统取消暂存
git reset -p                       # 按块取消暂存
git reset --keep <提交>            # 尽量保留本地修改
git reset --merge ORIG_HEAD        # 常用于撤销合并/拉取

reset <文件> 不会移动分支,只操作暂存区;这与 reset <提交> 的行为不同。现代写法优先 git restore --staged <文件>

10.3 git revert:安全撤销提交

git revert <提交>
git revert HEAD
git revert --no-commit A..B
git revert -m 1 <合并提交>
git revert --abort
git revert --continue
  • 创建一个反向变化的新提交,保留既有历史,适合已推送提交。
  • -n, --no-commit:只把反向变化放入暂存区,可一次撤销多项后统一提交。
  • -m <父编号>:撤销合并提交时指定保留哪条主线;语义影响后续合并,必须理解后使用。

范围 A..B 不包含 A。撤销多个提交时要检查顺序和结果。

10.4 git reflog:找回“消失”的提交

git reflog
git reflog show main
git show HEAD@{3}
git switch -c rescue <提交哈希>

reflog 记录本地引用曾经指向哪里,可恢复误 reset、误删分支或 rebase 前的提交。它主要是本地恢复日志,会过期,不是永久备份。

恢复流程:

  1. 立即停止大量改写操作。
  2. git reflog 找到正确提交。
  3. git show <哈希> 验证内容。
  4. git switch -c rescue <哈希> 建保护分支。

10.5 git clean:清理未跟踪文件

git clean -n
git clean -nd
git clean -fd
git clean -fdX
git clean -fdx
  • -n, --dry-run:预览,强烈建议先执行。
  • -f, --force:实际删除。
  • -d:包括未跟踪目录。
  • -X:只删除被忽略文件。
  • -x:连被忽略文件也删除,风险极高。
  • -i:交互式清理。

未跟踪文件不一定能被 Git 恢复。执行 clean 前必须预览。

练习 5:恢复事故

  1. 连续创建三个提交 A、B、C。
  2. 执行 git reset --hard HEAD~2,让 B、C 从分支历史消失。
  3. git reflog 找到原 C。
  4. 创建 rescue 分支恢复它。
  5. 回到 main,使用 git revert 撤销一个已提交变化,比较它与 reset 的历史差异。

11. 标签与版本发布

11.1 git tag

git tag
git tag --list "v1.*"
git tag v1.0.0
git tag -a v1.0.0 -m "Release 1.0.0"
git tag -s v1.0.0 -m "Release 1.0.0"
git show v1.0.0
git tag -d v1.0.0
  • 轻量标签:只是固定引用,git tag v1.0.0
  • 附注标签:独立对象,包含打标签者、日期和说明,发布版本推荐。
  • -a:创建附注标签。
  • -s:创建签名标签。
  • -v:验证签名。
  • -f:强制移动已有标签;公开标签不应随意移动。

推送与删除远程标签:

git push origin v1.0.0
git push origin --tags
git push origin --delete v1.0.0

检出标签会进入分离 HEAD。如需修复旧版:

git switch -c hotfix/v1.0.1 v1.0.0

推荐遵循语义化版本:主版本.次版本.修订号,如 v2.3.1


12. 暂存工作:stash

12.1 常用操作

git stash push -m "WIP: login form"
git stash push -u -m "include untracked"
git stash push -a
git stash push -p
git stash list
git stash show -p stash@{0}
git stash apply stash@{1}
git stash pop
git stash drop stash@{0}
git stash clear
git stash branch rescue-work stash@{0}

参数和区别:

  • push:创建 stash;推荐写说明。
  • -u, --include-untracked:包含未跟踪文件。
  • -a, --all:还包含被忽略文件。
  • -p, --patch:按块选择。
  • --keep-index:保留暂存区内容在当前工作树中。
  • apply:应用但保留 stash 记录。
  • pop:应用并在成功时删除记录。
  • branch:从 stash 原基点建分支并应用,处理旧 stash 冲突时很好用。
  • clear:删除全部 stash,难以恢复,慎用。

stash 适合短期切换任务,不是长期备份。长期工作应创建 WIP 分支并提交。


13. 选择性移植:cherry-pick

git cherry-pick <提交>
git cherry-pick A B C
git cherry-pick A..B
git cherry-pick A^..B
git cherry-pick -n <提交>
git cherry-pick -x <提交>
git cherry-pick --continue
git cherry-pick --skip
git cherry-pick --abort
  • 将指定提交的变化复制到当前分支,产生新的提交哈希。
  • A..B 包含 B、不包含 A;A^..B 包含 A 到 B。
  • -n, --no-commit:只应用变化,不立即提交。
  • -x:在提交说明中记录来源提交,适合跨发布分支回补修复。
  • -m <父编号>:挑选合并提交时指定主线。

典型场景:把 main 上的紧急修复同步到维护分支。不要用 cherry-pick 代替正常的长期分支合并,否则容易重复提交。

练习 6:回补线上修复

  1. v1.0.0release/1.x
  2. main 创建一个独立修复提交。
  3. release/1.x 执行 git cherry-pick -x <修复提交>
  4. 查看两个提交哈希与内容的关系。

14. 查错与定位

14.1 git bisect:二分定位引入缺陷的提交

git bisect start
git bisect bad                 # 当前版本有问题
git bisect good v1.0.0         # 已知正常版本
# Git 检出中间提交,测试后标记:
git bisect good                # 或 git bisect bad
git bisect reset

自动化:

git bisect start HEAD v1.0.0
git bisect run ./test.sh

测试脚本返回 0 表示 good,1–127(125 除外)表示 bad,125 表示无法测试、跳过。二分法定位 1024 个提交最多约需 10 次判断。

14.2 git grep:搜索受 Git 管理的内容

git grep "TODO"
git grep -n "validate" -- "*.js"
git grep -i "error" <提交>
git grep -l "deprecated"
  • -n:显示行号。
  • -i:忽略大小写。
  • -l:只显示文件名。
  • -c:显示匹配次数。
  • -E:扩展正则。

14.3 git shortlog:按作者汇总

git shortlog -sn
git shortlog -sne --all
  • -s:只显示提交数。
  • -n:按数量排序。
  • -e:显示邮箱。

14.4 git describe:根据标签描述版本

git describe --tags
git describe --tags --always --dirty

输出可能为 v1.2.0-5-gabc1234:距标签 5 个提交,当前提交缩写为 abc1234


15. 子模块、工作树与大文件

15.1 git submodule:仓库中引用另一个仓库

git submodule add <URL> libs/example
git submodule status
git submodule update --init --recursive
git submodule update --remote --merge
git clone --recurse-submodules <URL>
git submodule foreach 'git status --short'

主仓库记录的是子模块的特定提交,而不是其完整文件历史。常见陷阱:克隆后子模块为空、子模块处于分离 HEAD、主仓库忘记提交子模块指针变化。

更新流程:

cd libs/example
git switch main
git pull
cd ../..
git add libs/example
git commit -m "chore: update example submodule"

移除子模块涉及 .gitmodules、暂存区及模块元数据,操作前应先提交或备份并参考当前 Git 手册。

15.2 git worktree:一个仓库同时检出多个分支

git worktree list
git worktree add ../app-hotfix hotfix
git worktree add -b feature/docs ../app-docs main
git worktree remove ../app-hotfix
git worktree prune

适合正在开发功能时并行修复线上问题,避免反复 stash。通常同一分支不能同时被两个工作树检出。

15.3 Git LFS:管理大文件

Git LFS 是独立扩展:

git lfs install
git lfs track "*.psd"
git add .gitattributes
git add design.psd
git commit -m "assets: add design via LFS"
git lfs ls-files

Git 本身不擅长频繁变化的大型二进制文件。启用 LFS 前确认托管平台支持和配额;只写 .gitignore 不能移除已经进入历史的大文件。

15.4 git archive:导出源码快照

git archive --format=zip --output=release.zip v1.0.0
git archive --format=tar.gz --prefix=app-1.0/ -o app.tar.gz v1.0.0

它导出指定提交中的已跟踪文件,不包含 .git 历史。


16. 配置、别名、钩子与忽略规则

16.1 .gitignore

示例:

# 忽略依赖与构建结果
node_modules/
dist/

# 所有日志
*.log

# 但保留示例日志
!example.log

# 仅仓库根目录的环境文件
/.env

# 任意层级缓存目录
**/.cache/

规则:

  • / 结尾表示目录。
  • * 匹配一段路径内字符,** 可跨目录。
  • ! 取消忽略;若父目录已被完全排除,可能还需先放开父目录。
  • .gitignore 只影响未跟踪文件。

文件已经被跟踪时:

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

检查忽略来源:

git check-ignore -v path/to/file

个人全局忽略:

git config --global core.excludesFile ~/.config/git/ignore

仅当前仓库、又不想提交的忽略规则可写入 .git/info/exclude

16.2 .gitattributes

用于指定文本规范、diff 驱动和 LFS 等:

* text=auto
*.sh text eol=lf
*.bat text eol=crlf
*.png binary

改变换行规则后可重新规范化并仔细检查:

git add --renormalize .
git status
git diff --staged

16.3 实用别名

git config --global alias.st "status --short --branch"
git config --global alias.co switch
git config --global alias.br branch
git config --global alias.last "log -1 --stat"
git config --global alias.unstage "restore --staged --"
git config --global alias.lg "log --graph --decorate --oneline --all"

! 开头的别名会交给 shell,能力强但有安全和可移植性风险,不要执行来源不明的别名配置。

16.4 Git Hooks

钩子位于 .git/hooks/,常见:

  • pre-commit:提交前格式化或快速检查。
  • commit-msg:校验提交说明。
  • pre-push:推送前测试。
  • post-merge:合并后执行操作。

钩子默认不随 clone 分发。团队通常借助脚本或工具安装,也可配置:

git config core.hooksPath .githooks

钩子应快速、明确,并与 CI 互补;客户端钩子可被绕过,不能作为唯一安全控制。

16.5 签名

git commit -S -m "feat: signed commit"
git tag -s v1.0.0 -m "Release 1.0.0"
git log --show-signature
git verify-commit <提交>
git verify-tag v1.0.0

签名的密钥类型和托管平台配置因环境而异,可使用 GPG 或受支持的 SSH 签名方案。


17. 团队工作流

17.1 功能分支工作流

main ───────●──────────●─────────
             \        /
feature       ●──●──●
  1. 从最新 main 创建短生命周期功能分支。
  2. 小步提交并推送。
  3. 定期用 merge 或 rebase 更新。
  4. 发起评审,通过 CI 后合并。
  5. 删除已合并分支。
git switch main
git pull --ff-only
git switch -c feature/order-export
# 开发、提交
git push -u origin feature/order-export

17.2 Fork 工作流

git remote -v
git remote add upstream <原项目URL>
git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin main
git switch -c fix/issue-123

适合开源项目或严格权限隔离团队。

17.3 Trunk-based Development

  • 开发者频繁集成到主干。
  • 分支保持很短,通常不超过一两天。
  • 未完成功能使用 feature flag 隔离。
  • 强依赖自动化测试、代码评审和主分支保护。

17.4 Git Flow

包含 maindevelopfeature/*release/*hotfix/*。适合多个长期维护版本和固定发布窗口;持续交付团队常觉得它过重。

17.5 合并策略比较

策略 结果 优点 代价
Merge commit 保留分支结构 上下文完整,易整体回退 历史可能较复杂
Squash merge 一个综合提交 主分支简洁 丢失功能分支内部提交关系
Rebase + FF 线性保留各提交 便于逐提交追踪 合入前需整理历史和处理冲突

团队最重要的不是哪种“绝对最好”,而是统一策略并通过分支保护自动执行。


18. 综合练习案例

案例 A:个人项目完整循环

目标:掌握 init、add、commit、branch、merge、tag。

  1. 创建 notes-app 仓库和 README,完成初始提交。
  2. 创建 feature/search,新增 search.md 并分两次提交。
  3. 回到 main 修改 README。
  4. 合并功能分支,若没有冲突,刻意修改同一行再重做一次以制造冲突。
  5. 解决冲突,运行 git log --graph --oneline --all
  6. 创建附注标签 v0.1.0

验收:

git status
git log --graph --decorate --oneline --all
git show v0.1.0

案例 B:把一个混乱提交拆成两个

场景:你同时修改了文档和功能代码,但希望提交历史职责单一。

git add -p
git diff --staged
git commit -m "feat: add search filter"
git add docs/
git commit -m "docs: explain search filter"

若已误提交且尚未推送:

git reset --mixed HEAD~1
git add -p
# 分别提交

验收:两个提交各自可独立理解和回退。

案例 C:团队冲突演练

  1. 创建裸仓库,再克隆成 alice 和 bob 两份。
  2. 两人在同一文件同一行写不同内容。
  3. Alice 先推送。
  4. Bob 提交后执行 git pull --rebase
  5. Bob 解决冲突、git addgit rebase --continue,再推送。
  6. 查看远程最终历史。

思考:如果 Bob 使用普通 merge,历史图会有什么不同?

案例 D:线上紧急修复

  1. 从发布标签 v1.0.0 创建 hotfix/v1.0.1
  2. 修复并测试,提交 fix: ...
  3. 合入发布维护分支并打 v1.0.1 标签。
  4. 用 merge 或 cherry-pick 把修复同步回 main。
  5. 推送分支和标签。

验收:修复同时存在于维护线和未来版本中,提交来源可追踪。

案例 E:用 bisect 定位缺陷

  1. 创建 10 个顺序提交,其中第 4 个引入错误。
  2. 写一个能以退出码表示测试成功/失败的小测试脚本。
  3. 标记当前为 bad,第 1 个提交为 good。
  4. 手动二分,再用 git bisect run 自动二分。
  5. 执行 git bisect reset 回到原分支。

案例 F:恢复误操作

依次练习:

  1. restore:丢弃一个工作区修改。
  2. restore --staged:取消暂存但保留修改。
  3. commit --amend:补进漏掉的文件。
  4. revert:撤销一个模拟的已发布提交。
  5. reset --hard 后用 reflog 恢复。

要求:每一步执行前后都运行 statusdifflog,并用自己的话写出哪个指针或区域发生了变化。

案例 G:从需求到发布的期末项目

  1. 初始化仓库,配置 .gitignore.gitattributes
  2. 建立 main 和三个功能分支,每个分支至少两次原子提交。
  3. 故意创建并解决一次 merge 冲突。
  4. 用交互式 rebase 整理一个尚未共享的分支。
  5. 模拟代码评审后 squash 合并一个分支、普通合并另一个分支。
  6. cherry-pick -x 向维护分支回补修复。
  7. 创建签名或附注标签并导出源码包。
  8. reflog 完成一次灾难恢复演示。

最终应能解释每个提交为何存在,而不只是让命令“运行成功”。


19. 常见问题与危险命令

19.1 常见报错

fatal: not a git repository
当前目录及父目录没有 .git。先确认路径,或初始化/克隆仓库。

nothing to commit, working tree clean
没有未提交变化;也可能文件被忽略,用 git status --ignoredgit check-ignore -v 检查。

pathspec ... did not match
路径、分支名或文件名错误;用 git branch --allgit status 和文件列表核对。

non-fast-forward push 被拒绝
远端有本地没有的提交。先 fetch,检查历史,再 merge/rebase;不要立即强推。

refusing to merge unrelated histories
两个仓库没有共同祖先。确认是否选错仓库;确需合并才使用 --allow-unrelated-histories 并仔细解决冲突。

处于 detached HEAD
若要保留提交,立即 git switch -c <新分支>

19.2 风险等级

命令 风险 安全习惯
git reset --hard 丢弃已跟踪的未提交变化 先 status/diff,必要时 stash/建分支
git clean -fdx 删除全部未跟踪及忽略文件 永远先 git clean -ndx
git push --force 覆盖远程历史 --force-with-lease,确认目标分支
git branch -D 删除未合并分支引用 --no-merged,记下提交哈希
git stash clear 删除全部 stash stash list,逐项 drop
公共分支上 rebase 改写他人依赖的历史 只整理个人未共享提交

19.3 提交过敏感信息怎么办

  1. 立即撤销/轮换密钥、令牌和密码——从 Git 删除不等于秘密未泄露。
  2. 阻止继续传播并通知负责人。
  3. 用专用历史重写工具清理所有相关引用。
  4. 与协作者协调重新克隆或清理旧历史。
  5. 增加秘密扫描和提交前检查。

历史重写是协作破坏性操作,不能仅靠删除最新文件或普通 git revert 清除旧提交中的秘密。

19.4 操作前的五秒检查

git status --short --branch
git diff
git diff --staged
git log --oneline --decorate -5
git branch -vv

对清理和推送命令优先使用 --dry-run;对历史改写先创建临时保护分支:

git branch backup/before-rewrite

20. 命令速查表与进阶路线

20.1 高频速查

目的 命令
初始化 git init -b main
克隆 git clone <URL>
查看状态 git status -sb
暂存全部 git add -A
按块暂存 git add -p
提交 git commit -m "说明"
查看图形历史 git log --graph --oneline --decorate --all
查看未暂存变化 git diff
查看已暂存变化 git diff --staged
创建并切换分支 git switch -c <分支>
合并 git merge <分支>
变基 git rebase <基底>
抓取远程 git fetch --prune
拉取且只允许快进 git pull --ff-only
首次推送分支 git push -u origin <分支>
暂存工作 git stash push -u -m "说明"
取消暂存 git restore --staged <文件>
丢弃未暂存修改 git restore <文件>
安全撤销已共享提交 git revert <提交>
恢复丢失提交 git reflog
定位引入缺陷提交 git bisect

20.2 命令体系索引

除正文详解的高频命令外,Git 还有以下命令族。学习时按需查手册,不必一次背完:

  • 仓库与对象:initclonefsckgccount-objectscat-filehash-object
  • 快照:addcommitrmmvrestorereset
  • 分支与整合:branchswitchcheckoutmergerebasecherry-pickrevert
  • 历史查询:logshowdiffblamereflogshortlogdescriberev-parserev-list
  • 远程:remotefetchpullpushls-remoterequest-pull
  • 调试:bisectgreprange-diff
  • 补丁与邮件:format-patchamapplysend-email
  • 多仓库与多工作区:submodulesubtree(常以命令扩展提供)、worktree
  • 迁移与集成:bundlearchivefast-importsvn(取决于安装)。
  • 维护:maintenancegcprunerepackpack-refs
  • 凭据与签名:credentialverify-commitverify-tag
  • 管理:confighelpversionbugreportdiagnose

底层 plumbing 命令主要用于脚本、Git 内部机制和高级诊断。日常开发应优先使用 porcelain(面向用户)命令,避免直接修改 .git 内部文件。

20.3 推荐学习顺序

第一阶段:能独立保存版本
掌握 initstatusaddcommitlogdiff.gitignore

第二阶段:能使用分支开发
掌握 branchswitchmerge、冲突解决、标签。

第三阶段:能安全团队协作
掌握 remotefetchpullpush、上游分支和评审工作流。

第四阶段:能纠错和恢复
掌握 restorerevert、三种 reset、reflog、stash。

第五阶段:能整理和诊断历史
掌握交互式 rebase、cherry-pick、bisect、blame、高级 log 范围。

20.4 最终心法

  1. Git 的核心是提交图、引用和三个本地区域,不是命令口诀。
  2. 不确定时先看 statusdifflog --graph
  3. 共享历史优先用 revert;个人未共享历史才考虑 reset/rebase
  4. fetch 只下载,适合先观察;pull 会继续整合。
  5. 提交要小、完整、可解释、可测试、可回退。
  6. 分支要短命,及时集成,减少巨型冲突。
  7. 强推、硬重置和清理前,先确认对象、范围及恢复方案。
  8. Git 通常能通过 reflog 救回提交,但未跟踪文件和泄露的秘密是例外。

附录:常用修订表达式

表达式 含义
HEAD 当前提交
HEAD~1 当前提交的第一父提交
HEAD~3 沿第一父链向前三次
HEAD^ 第一个父提交
HEAD^2 合并提交的第二个父提交
main@{yesterday} main 昨天在 reflog 中的位置(若可解析)
tag^{} 解引用附注标签到其指向对象
A..B B 可达但 A 不可达的提交
A...B A 与 B 从共同祖先分开后的两侧提交
<提交>:<路径> 某提交中的文件内容
:/<文本> 提交说明匹配文本的最近提交

命令中同时出现修订和路径时,用 -- 消除歧义:

git log main -- src/main.js
git restore -- README.md

建议用一个可随意破坏的练习仓库逐条实验。真正的“精通”不是记住全部参数,而是能在操作前预测工作区、暂存区、分支指针和提交图将如何变化,并在出错后知道如何验证与恢复。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容