Jacky's blog
首页
  • Android
  • Web
  • Server
  • Python
  • iOS
  • Java
  • Vue
  • 计算机基础
  • AI
  • 全部工具
  • CLI 工具
  • 编辑器
  • 开发工具
  • 构建工具
  • 版本控制
  • 调试工具
  • 网络工具
  • 个人成长
  • 博客
  • 分类
  • 标签
  • 归档
收藏
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • Android
  • Web
  • Server
  • Python
  • iOS
  • Java
  • Vue
  • 计算机基础
  • AI
  • 全部工具
  • CLI 工具
  • 编辑器
  • 开发工具
  • 构建工具
  • 版本控制
  • 调试工具
  • 网络工具
  • 个人成长
  • 博客
  • 分类
  • 标签
  • 归档
收藏
GitHub (opens new window)
  • git 使用指南

    • 0. 核心概念(精简)
      • 0.1 三区模型
      • 0.2 HEAD 与引用
      • 0.3 第一父路径(first-parent)
      • 0.4 祖先路径(ancestry-path)
    • 1. command
      • 1.1 common
      • 1.1.1 状态与差异(status / diff / show)
      • 1.1.2 暂存与提交(add / commit / restore)
      • 1.1.3 分支操作(branch / switch / checkout)
      • 1.1.4 远程同步(fetch / pull / push / remote)
      • 1.1.5 stash(暂存工作区)
      • 1.1.6 其他常用命令(tag / cherry-pick / reset / revert)
      • 1.1.7 rebase vs merge
      • 1.2 log
      • 1.2.1 基础查看
      • 1.2.2 场景化检索(最常用)
      • 1.2.3 排障与审查场景
      • 1.3 rev-list
      • 1.3.1 双点 / 三点 / 计数
      • 1.3.2 first-parent 与 ancestry-path
      • 1.3.3 查“某提交何时被合并”
      • 1.4 blame
      • 1.4.1 定位某行是谁改的
      • 1.4.2 与 log 联动追溯
      • 1.5 rev-parse
      • 1.6 worktree
      • 1.6.1 核心概念
      • 1.6.2 常用命令
      • 1.6.3 使用场景
      • 1.6.4 注意事项
      • 1.7 hooks(钩子)
      • 1.7.1 常用客户端 hooks
      • 1.7.2 原生示例
      • 1.7.3 团队共享:husky(前端项目事实标准)
      • 1.7.4 跳过 hooks(危险)
      • 1.8 reflog(本地"后悔药")
      • 1.9 submodule(子模块)
      • 1.10 config 与 alias
      • 1.11 partial clone 与 sparse checkout
    • 2. workflow(工作流与规范)
      • 2.1 常规协作流程
      • 2.2 提交规范(Conventional Commits)
      • 2.3 分支命名建议
      • 2.4 worktree 并行开发流程
    • 3. troubleshoot(故障排除)
      • 3.1 推送被拒绝(non-fast-forward)
      • 3.2 提交到错误分支
      • 3.3 忘记把文件放进上次提交
      • 3.4 撤销合并
      • 3.5 误删分支恢复
      • 3.6 冲突处理
      • 3.7 大文件清理(历史已污染)
      • 3.8 提交了敏感信息(先止血)
      • 3.9 worktree 常见问题
      • 3.10 二分定位坏提交(bisect)
      • 3.11 detached HEAD
      • 3.12 .gitignore 不生效
    • 4. quick-reference(速查清单)
      • 4.1 我想看最近提交
      • 4.2 我想找“谁改了这一行”
      • 4.3 我想查某个函数在哪次提交里变了
      • 4.4 我想比较两个分支谁领先多少提交
      • 4.5 我想撤销本地未提交改动
      • 4.6 我想取消已暂存但不丢改动
      • 4.7 我想查看某次修改何时合并到主分支
      • 4.8 Git 清理过期分支
      • 4.9 跨分支查询已合并分支
      • 4.10 我想清理未跟踪的文件
      • 4.11 我想把 feature 分支压成一个提交合入
      • 4.12 我想临时跳过 hooks
    • 5. 资源
    • tools
    • version_control
    Jacky
    2019-05-20
    目录

    git 使用指南

    这是一份“命令优先、可快速检索”的 Git 速用指南。
    如果你在找 log,直接跳到 ## 1. command > ### 1.2 log。

    • Learn Git Online (opens new window)
    • Git 官方文档 (opens new window)

    # 0. 核心概念(精简)

    # 0.1 三区模型

    1. 工作区(Working Directory):你正在改的文件
    2. 暂存区(Staging Area):准备提交的变更
    3. 仓库(Repository):提交历史
    Untracked -> git add -> Staged -> git commit -> Committed
    
    1

    # 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
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    举例

    HEAD → M → B → A
    
    1
    main:    A ── B ──────────────── M ── HEAD
                                     ↑
                             只走这条线,忽略右边合并进来的
    
    1
    2
    3

    --first-parent 的视角:merge 只是主线上的一个普通节点,合并进来的分支完全不可见。

    # 0.4 祖先路径(ancestry-path)

    从目标 commit 出发,能追溯到它的所有路径上的节点。

    假设目标是 commit D,问"D 如何到达 HEAD?":

    D → E → F → M → HEAD
    
    1
    main:    A ── B ──────────────── M ── HEAD
                                    ╱
    feature:        C ── D ── E ── F
                          ↑
                       目标 commit
    
    1
    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 <分支名>:<文件路径>  # 查找指定分支的文件内容
    
    1
    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>
    
    1
    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 
    
    1
    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
    
    1
    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}
    
    1
    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>
    
    1
    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
    
    1
    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
    
    1
    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="<>"
    
    1
    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
    
    1
    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
    
    1
    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
    
    1
    2
    3
    4
    5

    # 1.3.3 查“某提交何时被合并”

    commit_hash="abc1234"
    git rev-list "$commit_hash"..main --ancestry-path --merges --reverse | head -1
    
    1
    2

    # 1.4 blame

    # 1.4.1 定位某行是谁改的

    git blame <file>
    git blame -L 10,40 <file>
    
    1
    2

    # 1.4.2 与 log 联动追溯

    # 先用 blame 找到提交 hash,再看该提交详情
    git show <commit-hash>
    
    # 追踪文件历史
    git log --follow -- <file>
    
    1
    2
    3
    4
    5

    使用建议:

    1. 先 blame -L 锁定行范围
    2. 再 show 看提交上下文
    3. 最后 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
    
    1
    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})"
    
    1
    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/
    └── ...
    
    1
    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
    
    1
    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
    # 开发、提交、推送
    
    1
    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 继续,完全没被打断
    
    1
    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
    
    1
    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
    
    1
    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
    
    1
    2
    3
    4
    5

    # 1.7.3 团队共享:husky(前端项目事实标准)

    .git/hooks/ 不进版本库,团队共享 hooks 一般用 husky 把 hooks 目录指到仓库内:

    npm install -D husky
    npx husky init    # 生成 .husky/ 目录并设置 core.hooksPath
    
    1
    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"
      }
    }
    
    1
    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
    
    1
    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
    
    1
    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 条记录的状态
    
    1
    2
    3

    典型找回流程:

    git reflog                        # 1) 找到出事前的 hash,如 a1b2c3d
    git switch -c recovered a1b2c3d   # 2) 基于它建新分支
    
    1
    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"
    
    1
    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   # 只推当前分支
    
    1
    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
    
    1
    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
    
    1
    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
    
    1

    所以“目录里没有文件”和“对象库里没有文件内容”不同: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
    
    1
    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
    
    1
    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
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # 2.2 提交规范(Conventional Commits)

    <type>(<scope>): <subject>
    
    1

    常见 type:

    • feat 新功能
    • fix 修复问题
    • docs 文档更新
    • refactor 重构
    • test 测试
    • chore 杂项

    示例:

    fix(auth): 修复 token 刷新失败
    
    1

    # 2.3 分支命名建议

    feature/user-auth
    fix/login-error
    hotfix/critical-bug
    release/v1.2.0
    
    1
    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
    
    1
    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
    
    1
    2

    # 3.2 提交到错误分支

    git reset --soft HEAD~1
    git switch correct-branch
    git commit -m "fix: move commit to correct branch"
    
    1
    2
    3

    # 3.3 忘记把文件放进上次提交

    git add <forgotten-file>
    git commit --amend --no-edit
    
    1
    2

    # 3.4 撤销合并

    # 未推送
    git reset --hard HEAD~1
    
    # 已推送(推荐)
    git revert -m 1 <merge-commit-hash>
    
    1
    2
    3
    4
    5

    # 3.5 误删分支恢复

    git reflog
    git switch -c recovered-branch <commit-hash>
    
    1
    2

    # 3.6 冲突处理

    git status
    git diff
    
    # 解决后
    git add <resolved-file>
    git commit -m "fix: resolve merge conflict"
    
    1
    2
    3
    4
    5
    6

    # 3.7 大文件清理(历史已污染)

    # 推荐 git-filter-repo
    git filter-repo --path large-file --invert-paths
    
    1
    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
    
    1
    2
    3
    4
    5
    6

    # 3.9 worktree 常见问题

    报错:is already checked out

    同一分支不能被两个 worktree 同时检出。换一个分支,或先在原 worktree 切走。

    fatal: 'main' is already checked out at '/path/to/repo'
    
    1

    worktree 目录被手动删除了,git worktree list 还显示

    git worktree prune
    
    1

    删除 worktree 时报 working tree contains modifications

    # 确认改动已提交或不需要后,强制删除
    git worktree remove --force ../repo-feature
    
    1
    2

    worktree 中依赖安装失败(如 esbuild 旧版本可选依赖下架)

    从主工作区复制 node_modules,比重新安装更可靠:

    cp -R ../repo/node_modules .
    
    1

    # 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               # 定位完成后退出,回到原分支
    
    1
    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)
    
    1
    2

    # 3.12 .gitignore 不生效

    原因:ignore 规则只对未跟踪文件生效,已被跟踪的文件加进 .gitignore 无效。

    git rm --cached <file>          # 先取消跟踪(保留本地文件)
    git commit -m "chore: stop tracking xxx"
    
    1
    2

    # 4. quick-reference(速查清单)

    # 4.1 我想看最近提交

    git log --oneline -n 20
    
    1

    # 4.2 我想找“谁改了这一行”

    git blame -L 120,160 src/foo.ts
    
    1

    # 4.3 我想查某个函数在哪次提交里变了

    git log -S "myFunction" --source --all
    
    1

    # 4.4 我想比较两个分支谁领先多少提交

    git rev-list --left-right --count main...feature
    
    1

    # 4.5 我想撤销本地未提交改动

    git restore <file>
    git restore .
    
    1
    2

    # 4.6 我想取消已暂存但不丢改动

    git restore --staged <file>
    
    1

    # 4.7 我想查看某次修改何时合并到主分支

    详见 1.3.3 查"某提交何时被合并"。

    # 4.8 Git 清理过期分支

    # 更新并清理(推荐)
    git fetch -p
    
    # 专门针对某个远程仓库清理
    git remote prune origin
    
    # 自动清理配置(每次 fetch 自动带 -p)
    git config --global fetch.prune true
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 4.9 跨分支查询已合并分支

    # 当前在 A 分支,查看哪些分支已合入 main(无需切换)
    git branch --merged main | grep -v "\*" | grep -v "main"
    
    1
    2

    # 4.10 我想清理未跟踪的文件

    git clean -n       # 预览会删什么(务必先跑)
    git clean -df      # 删除未跟踪的文件与目录(不可恢复,慎用)
    
    1
    2

    # 4.11 我想把 feature 分支压成一个提交合入

    git merge --squash feature/xxx
    git commit -m "feat: merge feature/xxx"
    
    1
    2

    # 4.12 我想临时跳过 hooks

    git commit --no-verify   # 跳过 pre-commit / commit-msg
    git push --no-verify     # 跳过 pre-push
    
    1
    2

    应急专用,见 1.7.4。

    # 5. 资源

    • Git 官方文档 (opens new window)
    • Git 教程(官方 Book) (opens new window)
    • Learn Git Branching (opens new window)
    #Git#tool#tutorial#git
    上次更新: 2026/10/18, 22:14:48
    最近更新
    01
    Codex 使用与会话管理指南
    10-17
    02
    Tailscale 入门:让自己的设备跨网络互联
    10-15
    03
    GitHub CLI(gh)工作流与 CI 排查
    10-08
    更多文章>
    Theme by Vdoing | Copyright © 2019-2026 Jacky | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式