git 使用指南
这是一份“命令优先、可快速检索”的 Git 速用指南。
如果你在找log,直接跳到## 1. command > ### 1.2 log。
# 0. 核心概念(精简)
# 0.1 三区模型
- 工作区(Working Directory):你正在改的文件
- 暂存区(Staging Area):准备提交的变更
- 仓库(Repository):提交历史
Untracked -> git add -> Staged -> git commit -> Committed
# 0.2 HEAD 与引用
HEAD:当前分支或当前提交指针HEAD~1:上一个提交HEAD^2:合并提交的第二父提交
# 0.3 第一父路径(first-parent)
first-parent表示:遇到 merge commit 时,只沿着“当前主线分支”的父提交继续回溯。它适合查看主分支的演进,不被功能分支内部提交干扰。
# 只看主线提交历史
git log --first-parent --oneline
# 忽略所有繁杂的开发细节,只按时间线列出最近合并进来的 Pull Request(PR)或分支合并历史
# <number>: 限制显示
# <branch>: 指定目标分支
git log --first-parent --merges --oneline -n <number> <branch>
# 统计主线提交数量
git rev-list --first-parent --count main
2
3
4
5
6
7
8
9
10
举例
HEAD → M → B → A
main: A ── B ──────────────── M ── HEAD
↑
只走这条线,忽略右边合并进来的
2
3
--first-parent 的视角:merge 只是主线上的一个普通节点,合并进来的分支完全不可见。
# 0.4 祖先路径(ancestry-path)
从目标 commit 出发,能追溯到它的所有路径上的节点。
假设目标是 commit D,问"D 如何到达 HEAD?":
D → E → F → M → HEAD
main: A ── B ──────────────── M ── HEAD
╱
feature: C ── D ── E ── F
↑
目标 commit
2
3
4
5
--ancestry-path 只保留从 D 通往 HEAD 的路径,A B C 这些不在路径上的全部排除。
# 1. command
# 1.1 common
# 1.1.1 状态与差异(status / diff / show)
# 当前状态(推荐先看这个)
git status
git status -s
# 差异查看
git diff # 工作区 vs 暂存区
git diff --staged # 暂存区 vs 最新提交
git diff --name-only
git diff --stat
# 查看提交详情
git show
git show <commit>
git show <分支名>:<文件路径> # 查找指定分支的文件内容
2
3
4
5
6
7
8
9
10
11
12
13
14
使用场景:
- 不确定改了什么:
git status -s && git diff --stat - 提交前做最后检查:
git diff --staged
# 1.1.2 暂存与提交(add / commit / restore)
# 暂存
git add <file>
git add .
git add -A
# 提交
git commit -m "feat(scope): message"
# 撤销工作区修改
git restore <file>
# 取消暂存
git restore --staged <file>
2
3
4
5
6
7
8
9
10
11
12
13
# 1.1.3 分支操作(branch / switch / checkout)
# 创建与切换
git switch -c feature/demo
git switch main
git switch -
#
git checkout -b <new-branch>
git checkout - # 切换到上一个分支
# 查看分支
git branch
git branch -vv
git branch -a
# 删除分支
git branch -d feature/demo
git branch -D feature/demo
# 列出所有包含该 commit 的远程分支
git branch -r --contains <sha>
# 列出所有包含该 commit 的分支
git branch -a --contains <sha>
# 删除远程分支
git push origin --delete dev
# 清理某个远程
git remote prune origin
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1.1.4 远程同步(fetch / pull / push / remote)
git remote -v
git fetch origin
# 抓取并清理
git fetch -p
git pull origin main
git push origin main
git push -u origin feature/demo
2
3
4
5
6
7
pull默认是fetch + merge。
想先看变更再合并,优先fetch。
# 1.1.5 stash(暂存工作区)
# 暂存所有改动并带备注
git stash push -m "正在改登录功能"
# 暂存指定文件
git stash push -m "暂存main.js" src/main.js
# 暂存未跟踪的新文件
git stash push -u -m "包含新文件"
# 查看 stash 列表
git stash list
# 查看某个 stash 的内容(不恢复)
git stash show -p stash@{0}
# 恢复最近的 stash 并删除
git stash pop
# 恢复指定 stash 但不删除(多个 stash 时用)
git stash apply stash@{0}
# 删除 stash
git stash drop stash@{0}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1.1.6 其他常用命令(tag / cherry-pick / reset / revert)
# tag
git tag
git tag -a v1.2.0 -m "release v1.2.0"
git push origin --tags
# cherry-pick
git cherry-pick <commit-hash>
git cherry-pick --continue
git cherry-pick --abort
# reset(改本地历史,未推送用)
git reset --soft HEAD~1 # 撤销提交,保留改动在暂存区
git reset --mixed HEAD~1 # 撤销提交,保留改动在工作区
git reset --hard HEAD~1 # 撤销提交,丢弃所有改动(危险)
# revert(不改历史,已推送用)
git revert <commit-hash>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1.1.7 rebase vs merge
| 场景 | merge | rebase |
|---|---|---|
| 历史记录 | 保留完整合并节点 | 线性历史,更干净 |
| 冲突解决 | 一次解决 | 可能要多次解决 |
| 公共分支 | ✅ 推荐 | ❌ 不要在 main 上 rebase |
| 本地 feature 分支 | 可以 | ✅ 推荐,保持干净 |
# 同步主分支(本地 feature 推荐 rebase)
git fetch origin
git rebase origin/main
# 解决冲突后继续
git rebase --continue
git rebase --abort # 放弃 rebase,回到之前状态
# merge 方式
git fetch origin
git merge origin/main
2
3
4
5
6
7
8
9
10
11
# 1.2 log
# 1.2.1 基础查看
git log --help
git log --oneline
git log --graph --oneline --all --decorate
git log -n 20 --oneline
2
3
4
# 1.2.2 场景化检索(最常用)
# 按时间
git log --since="2026-01-01" --until="2026-12-31" --oneline
# 按作者
git log --author="AUTHOR_NAME" --oneline
# 按提交信息关键字
git log --grep="fix" --oneline
# 按代码内容变化(非常常用)
# --source: 显示改动来自哪个分支
# --all: 所有本地分支 + 远程跟踪分支
git log -S "function_name" --source --all
# 按文件追踪
git log -- <file-path>
git log --follow -- <file-path> # 文件重命名后继续追踪
# 只看新增/删除文件相关提交
git log --diff-filter=A --summary
git log --diff-filter=D --summary
# 过滤信息查看
git log --all --oneline --no-merges --grep="<>"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1.2.3 排障与审查场景
# 找“谁在什么时候改了什么”
git log --oneline --decorate -- <file-path>
# 看某个功能分支与 main 的差异历史
git log main..feature --oneline
git diff main...feature
2
3
4
5
6
# 1.3 rev-list
rev-list适合脚本和统计;log适合人读。
# 1.3.1 双点 / 三点 / 计数
# 在 feature 但不在 main
git rev-list main..feature
# 两边差异计数(左边/右边)
git rev-list --left-right --count main...feature
# 提交数量统计
git rev-list --count HEAD
git rev-list --count main..feature
2
3
4
5
6
7
8
9
# 1.3.2 first-parent 与 ancestry-path
# 主分支演进统计(忽略被合入分支细节)
git rev-list --first-parent --count main
# 只看祖先路径
git rev-list <commit>..main --ancestry-path
2
3
4
5
# 1.3.3 查“某提交何时被合并”
commit_hash="abc1234"
git rev-list "$commit_hash"..main --ancestry-path --merges --reverse | head -1
2
# 1.4 blame
# 1.4.1 定位某行是谁改的
git blame <file>
git blame -L 10,40 <file>
2
# 1.4.2 与 log 联动追溯
# 先用 blame 找到提交 hash,再看该提交详情
git show <commit-hash>
# 追踪文件历史
git log --follow -- <file>
2
3
4
5
使用建议:
- 先
blame -L锁定行范围 - 再
show看提交上下文 - 最后
log --follow看文件演进
# 1.5 rev-parse
git rev-parse 是一个功能非常强大的底层工具,主要用于将各种 Git 对象名、引用或标志解析为可用的格式(例如具体的 Hash 值或路径)。
最常用的几个参数:
1. 解析引用(最常用)
git rev-parse <branch_name>:获取指定分支或提交的完整 SHA-1 哈希值git rev-parse --abbrev-ref <ref>:获取引用的简短名称(例如直接得到当前分支名)git rev-parse --short <commit>:获取短哈希值
2. 路径与环境信息
git rev-parse --show-toplevel:返回 Git 仓库的根目录绝对路径(写脚本时常用)git rev-parse --git-dir:返回.git目录的路径git rev-parse --is-inside-work-tree:检查当前目录是否在 Git 工作区内(返回 true / false)
3. 其他常用标志
--verify <ref>:校验引用是否存在(失败则非 0 退出,适合脚本判断)
# 当前提交 hash
git rev-parse HEAD
git rev-parse --short HEAD
# 当前分支名(detached HEAD 时输出 HEAD)
git rev-parse --abbrev-ref HEAD
# 把任意引用解析为 hash
git rev-parse main
git rev-parse origin/main
git rev-parse v1.2.0
# 父提交(^ 与 ~1 等价)
git rev-parse HEAD^
git rev-parse HEAD~1
# 仓库根目录(写脚本、拼路径时常用)
git rev-parse --show-toplevel
# 是否在 git 工作区内
git rev-parse --is-inside-work-tree
# 校验引用是否存在(失败则非 0 退出,适合脚本判断)
git rev-parse --verify main
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
脚本示例:
ROOT="$(git rev-parse --show-toplevel)"
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
SHORT_SHA="$(git rev-parse --short HEAD)"
echo "${BRANCH}@${SHORT_SHA} (${ROOT})"
2
3
4
# 1.6 worktree
一个 Git 仓库可以同时检出多个分支到不同目录,共享同一份
.git历史。适合并行开发多 feature、紧急 hotfix 不打断当前工作、PR 本地 review。
# 1.6.1 核心概念
- 主工作区:
git clone得到的原始目录,通常绑定main分支 - 附加工作区:通过
git worktree add创建的目录,绑定其他分支 - 共享:所有 worktree 共享同一个
.git对象库,提交/分支对所有 worktree 立即可见 - 隔离:每个 worktree 有独立的工作区文件,改文件互不影响
repo/ ← 主工作区,main 分支
├── .git
├── src/
└── ...
repo-feature/ ← 附加工作区,feature/xxx 分支
├── src/
└── ...
2
3
4
5
6
7
8
# 1.6.2 常用命令
# 列出所有 worktree(路径 + 分支 + commit)
git worktree list
# 基于已有分支创建 worktree
git worktree add ../repo-feature feature/xxx
# 基于 main 创建新分支并检出到 worktree
git worktree add -b feature/xxx ../repo-feature main
# 基于某个 tag/commit 创建 worktree(detached HEAD)
git worktree add ../repo-v1.0.0 v1.0.0
# 删除 worktree(工作区干净时)
git worktree remove ../repo-feature
# 强制删除(有未提交改动时,会丢弃改动)
git worktree remove --force ../repo-feature
# 清理已被手动删除目录的 worktree 记录
git worktree prune
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1.6.3 使用场景
场景 1:并行开发两个 feature
# 主工作区开发 feature/A
cd repo
git switch feature/A
# 同时开一个 worktree 做 feature/B
git worktree add -b feature/B ../repo-featureB main
cd ../repo-featureB
# 开发、提交、推送
2
3
4
5
6
7
8
场景 2:紧急 hotfix 不打断当前工作
# 当前在 feature/A 上改到一半,线上出 bug
git worktree add -b hotfix/xxx ../repo-hotfix main
cd ../repo-hotfix
# 修复、提交、推送、提 PR
# 修复完清理
cd ../repo
git worktree remove ../repo-hotfix
git branch -d hotfix/xxx
# 回到 feature/A 继续,完全没被打断
2
3
4
5
6
7
8
9
场景 3:本地 review 别人的 PR
git fetch origin pull/123/head:pr-123
git worktree add ../repo-pr123 pr-123
cd ../repo-pr123
# 运行、测试、review
2
3
4
# 1.6.4 注意事项
- 依赖安装:每个 worktree 有独立的
node_modules,需要重新npm install/yarn,或从主工作区复制 - 环境变量:
.env、.envrc等文件在每个 worktree 中独立,需单独配置 - 端口冲突:多个 worktree 同时跑 dev server 会端口冲突,需改端口
- 同一分支不能同时检出:Git 不允许两个 worktree 检出同一个分支(会报
is already checked out) - 删除前确认:
git worktree remove前确认改动已提交或备份,--force会丢弃未提交改动
# 1.7 hooks(钩子)
Git 在关键动作前后自动执行的脚本,存放在
.git/hooks/。把 sample 文件去掉.sample后缀(或新建同名可执行文件)即生效;脚本退出码非 0 则中断该动作。
# 1.7.1 常用客户端 hooks
| hook | 触发时机 | 典型用途 |
|---|---|---|
pre-commit | git commit 执行前 | lint / 格式检查 / 拦截大文件、敏感信息 |
prepare-commit-msg | 提交信息模板生成后 | 自动填充模板、注入分支号 |
commit-msg | 提交信息写完后 | 校验 Conventional Commits 规范 |
post-commit | 提交完成后 | 通知、统计 |
pre-push | git push 执行前 | 跑测试、阻止向 main 直推 |
post-merge | merge / pull 完成后 | 自动同步依赖(npm install 等) |
服务端 hooks(pre-receive / post-receive)部署在远端仓库,用于 CI 拦截和权限校验,日常开发较少直接接触。
# 1.7.2 原生示例
# .git/hooks/pre-commit —— 拦截大于 1M 的文件
if find . -type f -size +1M -not -path "./.git/*" | grep -q .; then
echo "存在大于 1M 的文件,禁止提交"
exit 1
fi
2
3
4
5
# .git/hooks/commit-msg —— 校验提交信息符合 <type>(<scope>): <subject>
if ! grep -qE '^(feat|fix|docs|refactor|test|chore)(\(.+\))?: .+' "$1"; then
echo "提交信息不符合 Conventional Commits 规范"
exit 1
fi
2
3
4
5
# 1.7.3 团队共享:husky(前端项目事实标准)
.git/hooks/ 不进版本库,团队共享 hooks 一般用 husky 把 hooks 目录指到仓库内:
npm install -D husky
npx husky init # 生成 .husky/ 目录并设置 core.hooksPath
2
原理:
git config core.hooksPath .husky,Git 改从该目录找 hooks,从而实现 hooks 随仓库分发。
# 完整配置流程
# 1. 安装 husky
npm install -D husky
# 2. 初始化
npx husky init
# 3. 在 .husky/ 目录下创建 hook 文件
# 例如 .husky/pre-commit:
python3 scripts/git/check_tags.py
python3 scripts/git/check_filename.py
python3 scripts/git/check_link.py check
# 4. package.json 加 prepare 脚本(npm install 自动运行)
{
"scripts": {
"prepare": "husky"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 工作流程
别人 clone 项目后:
git clone xxx
npm install # 自动运行 prepare → husky → 自动 install hooks
git commit # 自动跑 .husky/pre-commit
2
3
不用手动 cp scripts/pre-commit .git/hooks/。
# 1.7.4 跳过 hooks(危险)
git commit --no-verify # 跳过 pre-commit / commit-msg
git push --no-verify # 跳过 pre-push
2
仅应急使用。CI 侧应有同等校验兜底,否则本地 hooks 形同虚设。
# 1.8 reflog(本地"后悔药")
记录 HEAD 与分支每次移动的本地日志(默认保留 90 天)。
reset --hard、误删分支、rebase 翻车后,都能靠它找回。
git reflog # 查看所有 HEAD 移动记录
git reflog show main # 查看某分支的移动记录
git reset --hard HEAD@{2} # 回到 reflog 中第 2 条记录的状态
2
3
典型找回流程:
git reflog # 1) 找到出事前的 hash,如 a1b2c3d
git switch -c recovered a1b2c3d # 2) 基于它建新分支
2
reflog 只存在于本地,clone 不带 reflog;过期条目会被
git gc清理,越早恢复越好。
# 1.9 submodule(子模块)
在仓库中引用另一个仓库,并固定到某个 commit。适合 vendor 依赖、跨仓库公共库。
# 添加子模块
git submodule add <url> path/to/sub
# 克隆含子模块的仓库
git clone --recursive <url>
# 已克隆的仓库补拉子模块
git submodule update --init --recursive
# 更新子模块后,父仓库要提交"指针变更"
cd path/to/sub && git pull
git add path/to/sub
git commit -m "chore: bump submodule"
2
3
4
5
6
7
8
9
10
11
12
13
常见坑:
- 父仓库记录的是子模块的 commit 而非分支,子模块一变父仓库就要跟着提交
- 克隆时忘记
--recursive,子模块目录是空的
# 1.10 config 与 alias
# 三个作用域(就近覆盖):--system(全机) > --global(当前用户) > --local(当前仓库,默认)
git config --list --show-origin # 查看所有配置及来源
git config user.name # 读写身份(公司/个人仓库可用 local 分开配)
# 高频推荐配置
git config --global pull.rebase true # pull 默认 rebase,保持线性历史
git config --global fetch.prune true # fetch 自动清理已删除的远程分支
git config --global push.default simple # 只推当前分支
2
3
4
5
6
7
8
实用 alias:
git config --global alias.st "status -s"
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.last "log -1 HEAD --stat"
# 之后即可:git st / git lg / git last
2
3
4
# 1.11 partial clone 与 sparse checkout
GitHub 支持 Git 的 partial clone,不需要专用 GitHub 命令。常用参数是 git clone --filter=blob:none:先获取所需历史中的提交和目录树,文件内容(blob)按需下载;它不是只克隆某个目录。
| 机制 | 控制什么 | 不代表什么 |
|---|---|---|
--filter=blob:none | 延迟下载文件内容,使用时从远端补取 | 不是禁止下载某些文件,也不是目录权限控制 |
--depth=1 | 只获取浅层提交历史 | 不限制当前版本包含哪些文件 |
--sparse / git sparse-checkout | 限制哪些跟踪文件出现在工作目录 | 单独使用不保证对应内容没有进入本地对象库 |
--no-checkout | 克隆后暂不生成工作目录中的跟踪文件 | 单独使用仍可能下载全部对象 |
可执行例子:先不取文件内容,再选择目录
下面使用公开的 actions/checkout 仓库及其 v4 标签,在新目录中执行,不修改已有项目。检出标签会进入 detached HEAD,适合本例阅读和验证;需要开发时另建分支。
# 克隆提交和目录树,暂不检出文件;这里没有使用 --depth,仍可查看该版本历史
git clone --filter=blob:none --no-checkout --single-branch --no-tags \
--branch v4 https://github.com/actions/checkout.git checkout-partial-demo
cd checkout-partial-demo
# 配置需要的目录,再检出;缺少的文件内容由 Git 自动补下载
git sparse-checkout set --cone src
git checkout v4
# 扩大选择范围;新增目录需要的内容也可能触发下载
git sparse-checkout add __test__
git sparse-checkout list
2
3
4
5
6
7
8
9
10
11
12
cone 模式会保留仓库根目录的文件以及所选目录路径上的父目录文件,不是“严格只取 src”;这里的选择范围是 src 与随后加入的 __test__,不要把这一示例当作私人目录隔离规则。
更短的组合是 git clone --filter=blob:none --sparse <URL> <新目录>,再运行 git sparse-checkout set --cone <目录>;初始检出会取根目录文件。单独执行 git clone --filter=blob:none <URL> 时,默认 checkout 仍会下载当前版本所需的文件,主要省下暂时未用到的历史文件内容。
“尚未获取的对象”具体指什么
Git 用对象 ID 保存数据:commit 表示提交,tree 记录文件名、目录结构与对象引用,blob 保存一份文件内容。blob:none 允许本地已有目录树,但其中一些 blob 尚未下载。
例如先只检出 src、还没有执行上面的 add __test__ 时,Git 已知道 __test__/input-helper.test.ts 在哪个提交中存在及其 blob ID,但不一定已有它的内容。下面的命令需要读取内容,可能自动向 GitHub 补下载;它不会把该文件写进工作目录:
git show HEAD:__test__/input-helper.test.ts
所以“目录里没有文件”和“对象库里没有文件内容”不同:git show 可能已经取回内容,但 sparse checkout 仍不把文件放进工作目录。修改选择范围、切换提交、查看历史 diff 等操作,也可能需要补下载;尚未下载的内容不能保证离线访问。
检查 partial clone 是否生效
git config --get remote.origin.promisor # 通常为 true
git config --get remote.origin.partialclonefilter # blob:none
# 列出当前可达对象及缺失对象;? 开头表示尚未获取的对象 ID
git rev-list --objects --all --missing=print
2
3
4
5
promisor remote 可以理解为“答应在需要时补发对象的远端”。这里的缺失是 partial clone 允许的状态,不等于仓库损坏;访问所需对象时若补下载失败,该次操作仍会失败。
为什么私有仓库 CI 的第二次 checkout 也需要认证
actions/checkout@v4 在设置 sparse-checkout、且没有显式设置其他 filter 时,会使用 blob:none 获取对象。第一次只检出配置文件,后续扩大范围到公开文档时,需要从私有仓库补取尚未下载的内容。私有仓库中的“公开文档”仍受仓库权限保护,补下载同样需要认证。
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
persist-credentials: true
sparse-checkout: |
.github/config
scripts/docs
sparse-checkout-cone-mode: true
2
3
4
5
6
7
8
9
10
11
这里保留只读 GITHUB_TOKEN 的认证配置,供后续 Git 命令补下载;checkout 的 post-job 步骤会清理认证。设置 persist-credentials: false 又未提供其他认证时,后续请求可能报 could not read Username 或 could not fetch ... from promisor remote。这不是要求提高令牌权限,也不是 Node 或依赖安装问题。
本项目仍需读取发布黑名单后,按排除规则扩大 sparse checkout;不能用 git sparse-checkout disable 绕过错误,因为它会恢复所有跟踪文件。排除发布也不是仓库访问权限:有仓库读取权限的人仍可单独读取未检出的路径;partial clone 不负责保密。不要把令牌直接写进 clone URL 或日志。
来源:Git clone 参数 (opens new window)、sparse checkout 与 cone 模式 (opens new window)、GitHub 的 partial/shallow clone 说明 (opens new window)、checkout v4 的过滤逻辑 (opens new window)、checkout v4 的认证清理说明 (opens new window)。
# 2. workflow(工作流与规范)
# 2.1 常规协作流程
# 1) 基于主分支拉新分支
git switch main
git pull origin main
git switch -c feature/xxx
# 2) 开发提交
git add .
git commit -m "feat(module): add xxx"
# 3) 同步主分支
git fetch origin
git rebase origin/main
# 4) 推送并发起 PR
git push -u origin feature/xxx
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2.2 提交规范(Conventional Commits)
<type>(<scope>): <subject>
常见 type:
feat新功能fix修复问题docs文档更新refactor重构test测试chore杂项
示例:
fix(auth): 修复 token 刷新失败
# 2.3 分支命名建议
feature/user-auth
fix/login-error
hotfix/critical-bug
release/v1.2.0
2
3
4
# 2.4 worktree 并行开发流程
适合:同时进行多个 feature、需要频繁切换上下文、不想 stash 来 stash 去。
基础命令详见 1.6 worktree。
# 1) 主工作区保持在 main,只做 fast-forward pull
cd repo
git switch main
git pull --ff-only origin main
# 2) 为每个 feature 创建独立 worktree
git worktree add -b feature/aaa ../repo-aaa main
git worktree add -b feature/bbb ../repo-bbb main
# 3) 在各自 worktree 中开发(互不干扰)
cd ../repo-aaa
# 开发、提交、推送
# 4) 合并后清理
cd ../repo
git worktree remove ../repo-aaa
git branch -d feature/aaa
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
对比传统切换分支:
| 维度 | 传统 switch | worktree |
|---|---|---|
| 切换成本 | 需 stash / 丢弃改动 | 零,目录独立 |
| 并行开发 | 只能一个分支 | 多个分支同时 |
| node_modules | 共享,切换后可能需重装 | 独立,互不影响 |
| 适用场景 | 单任务线性开发 | 多任务并行 / hotfix |
# 3. troubleshoot(故障排除)
# 3.1 推送被拒绝(non-fast-forward)
git pull --rebase origin main
git push origin main
2
# 3.2 提交到错误分支
git reset --soft HEAD~1
git switch correct-branch
git commit -m "fix: move commit to correct branch"
2
3
# 3.3 忘记把文件放进上次提交
git add <forgotten-file>
git commit --amend --no-edit
2
# 3.4 撤销合并
# 未推送
git reset --hard HEAD~1
# 已推送(推荐)
git revert -m 1 <merge-commit-hash>
2
3
4
5
# 3.5 误删分支恢复
git reflog
git switch -c recovered-branch <commit-hash>
2
# 3.6 冲突处理
git status
git diff
# 解决后
git add <resolved-file>
git commit -m "fix: resolve merge conflict"
2
3
4
5
6
# 3.7 大文件清理(历史已污染)
# 推荐 git-filter-repo
git filter-repo --path large-file --invert-paths
2
该操作会改写历史,执行前先备份仓库。
# 3.8 提交了敏感信息(先止血)
# 先从当前跟踪中移除(保留本地文件)
git rm --cached sensitive-file
git commit -m "chore: remove sensitive file from tracking"
# 之后再做历史清理(会改写历史)
# git filter-repo --path sensitive-file --invert-paths
2
3
4
5
6
# 3.9 worktree 常见问题
报错:is already checked out
同一分支不能被两个 worktree 同时检出。换一个分支,或先在原 worktree 切走。
fatal: 'main' is already checked out at '/path/to/repo'
worktree 目录被手动删除了,git worktree list 还显示
git worktree prune
删除 worktree 时报 working tree contains modifications
# 确认改动已提交或不需要后,强制删除
git worktree remove --force ../repo-feature
2
worktree 中依赖安装失败(如 esbuild 旧版本可选依赖下架)
从主工作区复制 node_modules,比重新安装更可靠:
cp -R ../repo/node_modules .
# 3.10 二分定位坏提交(bisect)
"之前是好的,现在坏了"——Git 自动二分 checkout 中间提交,人工验证后标记,快速锁定引入 bug 的提交。
git bisect start
git bisect bad # 当前提交是坏的
git bisect good v1.0.0 # 这个版本是好的
# Git 自动检出中间提交,逐轮验证:
git bisect good # 验证通过
git bisect bad # 验证失败
git bisect reset # 定位完成后退出,回到原分支
2
3
4
5
6
7
# 3.11 detached HEAD
现象:git switch 到某个 commit / tag 后提示 HEAD is now detached。此时新提交不属于任何分支,直接切走后提交会"消失"。
git switch -c new-branch # 先把当前状态固化成新分支
# 已经切走才发现:用 git reflog 找回(见 1.8)
2
# 3.12 .gitignore 不生效
原因:ignore 规则只对未跟踪文件生效,已被跟踪的文件加进 .gitignore 无效。
git rm --cached <file> # 先取消跟踪(保留本地文件)
git commit -m "chore: stop tracking xxx"
2
# 4. quick-reference(速查清单)
# 4.1 我想看最近提交
git log --oneline -n 20
# 4.2 我想找“谁改了这一行”
git blame -L 120,160 src/foo.ts
# 4.3 我想查某个函数在哪次提交里变了
git log -S "myFunction" --source --all
# 4.4 我想比较两个分支谁领先多少提交
git rev-list --left-right --count main...feature
# 4.5 我想撤销本地未提交改动
git restore <file>
git restore .
2
# 4.6 我想取消已暂存但不丢改动
git restore --staged <file>
# 4.7 我想查看某次修改何时合并到主分支
# 4.8 Git 清理过期分支
# 更新并清理(推荐)
git fetch -p
# 专门针对某个远程仓库清理
git remote prune origin
# 自动清理配置(每次 fetch 自动带 -p)
git config --global fetch.prune true
2
3
4
5
6
7
8
# 4.9 跨分支查询已合并分支
# 当前在 A 分支,查看哪些分支已合入 main(无需切换)
git branch --merged main | grep -v "\*" | grep -v "main"
2
# 4.10 我想清理未跟踪的文件
git clean -n # 预览会删什么(务必先跑)
git clean -df # 删除未跟踪的文件与目录(不可恢复,慎用)
2
# 4.11 我想把 feature 分支压成一个提交合入
git merge --squash feature/xxx
git commit -m "feat: merge feature/xxx"
2
# 4.12 我想临时跳过 hooks
git commit --no-verify # 跳过 pre-commit / commit-msg
git push --no-verify # 跳过 pre-push
2
应急专用,见 1.7.4。