T0FV404 / Git 使用速记

Created Fri, 05 Dec 2025 23:15:17 +0800 Modified Sun, 07 Dec 2025 19:31:24 +0800
11542 Words

碎碎念

发现自己此前学习过于不仔细,闹了笑话。重新学了一遍,了解了许多用法,分清了概念,此处记录。

文章由 AI 生成,当然我做了些修改,请您甄别。

指令并不全,还是自己看 man 文档或者-h

概念

仓库

仓库分为本地仓库和远程仓库,本地仓库就是你电脑上 .git 所在的整个项目目录,远程仓库就是支持 ssh 的服务器上建立的 git 仓库。

仓库还可以分成非 bare 仓库和 bare 仓库,一个是正常仓库,另一个就是只有 .git 中的数据,而没有工作区的仓库

四个分区

  • 工作区:本地正在编辑的代码文件所在目录
  • 暂存区:一个准备提交的缓存区
  • 提交:见下面底层对象的 commit
  • Stash:一个临时存放空间

底层对象

  • blob:文件内容(每个版本的文件内容是一个 blob 对象)
  • tree:目录结构(记录“这个目录下有哪些文件/子目录,它们指向哪个 blob/tree”)
  • commit:提交(指向一个顶层 tree + 父提交 + 提交信息)
  • tag:带注释的标记(可以指向 commit)
  • refs:各种指针的统一称呼

Git 如何节省空间

Git 中相似的 blob 按 hash 计算,如果 hash 一样就全部指向同一个 blob。

同时对于相似的 blob 或 tree,git 会存放一个完整版本,其他的则用补丁记录。

注意点

提交相关

  • 提交粒度要合理 一个提交只做一件事,避免一次提交包含多个不相关的修改。这样后续回滚或查看历史时更精准。
  • 提交信息要规范 用简洁明了的语言描述 “做了什么”,最好遵循约定式提交:
    • feat: 新增xxx功能(新功能)
    • fix: 修复xxxbug(bug 修复)
    • docs: 更新接口文档(文档修改)
    • refactor: 重构用户模块代码(代码重构,不影响功能)
    • style: 调整代码风格(仅格式、空格、分号等,不改动逻辑)
    • perf: 性能优化(提升性能的改动)
    • test: 补充或修改测试(单测、集成测试等)
    • chore: 杂项任务(构建流程、依赖升级、脚手架配置等,不影响源码或测试逻辑)
    • build: 构建相关(打包、编译、CI 构建脚本等)
    • ci: 持续集成配置(GitHub Actions、GitLab CI、Jenkins 等配置修改)
    • revert: 回滚提交(回滚某次历史提交)
    • merge: 合并分支(有的团队会专门用这个,或直接用默认的 merge 信息)
    • deps: 依赖调整(添加、升级、移除依赖)
    • release: 发布相关(打 tag、版本号变更等)
  • 避免模糊描述
  • 提交前检查修改内容 提交前用 git status 确认要提交的文件,用 git diff 检查具体修改内容,避免误提交敏感信息(如密码、密钥)或无关文件(如日志、临时文件)。

分支管理

  • 使用有意义的分支命名 分支名应体现用途,例如:

    • feature/user-login(新功能:用户登录)
    • bugfix/payment-error(bug 修复:支付错误)
    • hotfix/security-vulnerability(紧急修复:安全漏洞)
    • refactor/xxx:重构,不改变外部行为
    • perf/xxx:性能优化
    • test/xxx:测试相关
    • docs/xxx:文档相关
    • release/xxx:发布版本分支
    • chore/xxx:杂项维护,不直接影响业务逻辑
    • build/xxx:构建、打包相关
    • build/xxx:构建、打包相关

    避免 dev1test 等模糊名称。

  • 保持主分支稳定 main/master 分支应始终保持可部署状态,开发新功能时从主分支创建新分支,完成后通过 PR/MR 合并,合并前必须经过代码 review 和测试。

  • 定期同步主分支更新 在功能分支开发时,定期从主分支同步最新代码(git merge maingit rebase main),避免后期合并时出现大量冲突。

协作相关

  • 推送前先拉取(pull) 推送本地修改到远程前,先用 git pull 拉取远程最新代码,解决本地与远程的冲突后再推送,减少远程仓库的冲突概率。
  • 不强行推送已公开的历史 已推送到远程的提交,不要用 git push --force 强行覆盖(除非团队明确约定),就算用也最好用--force-with-lease。若需修改已推送的历史,优先用 git revert 而非 git reset
  • 及时清理无用分支 功能上线或 bug 修复后,删除对应的本地和远程分支(git branch -d <分支名>git push origin --delete <分支名>),避免分支过多导致混乱。

撤销与备份

  • 谨慎使用 git reset --hard 该命令会彻底删除工作区和暂存区的修改,执行前务必确认没有需要保留的未提交内容,可先用 git stash 暂存(git stash save "临时暂存"),后续用 git stash pop 恢复。
  • 善用 git reflog 备份操作记录 所有分支切换、提交、重置等操作都会被 git reflog 记录,即使误删提交,也能通过它找到历史哈希值恢复(git reset --hard <哈希>)。
  • 重要节点打标签(tag) 发布版本时,用标签标记重要提交(git tag -a v1.0 -m "正式发布v1.0版本"),方便后续追溯特定版本的代码。

配置

git config [<scope>] <key> [<value>]
# 查看或设置配置项
# <scope>:--system(系统级)、--global(当前用户)、--local(当前仓库,默认)

git config --global user.name "你的名字"
# 设置全局用户名;默认无(未设置时提交会报错)
# 使用场景:为所有仓库统一配置作者名,用于提交记录中显示

git config --global user.email "你的邮箱"
# 设置全局邮箱;默认无(未设置时提交会报错)
# 使用场景:为所有仓库统一配置邮箱,用于关联 GitHub/GitLab 账号

git config --global init.defaultBranch main
# 设置 git init 时默认分支名为 main;默认视 Git 版本可能为 master 或 main
# 使用场景:新项目统一使用 main 而非 master

git config --global pull.rebase true
# 设置全局 git pull 默认使用 rebase;默认 false(merge)
# 使用场景:希望大多数仓库的提交历史保持线性,避免自动 merge 提交

git config pull.rebase true
# 仅对当前仓库启用 pull --rebase;默认继承全局配置
# 使用场景:只想在某个项目中使用 rebase,同机其他项目仍用 merge

git config branch.dev.rebase true
# 仅对 dev 分支启用 pull --rebase;默认继承 pull.rebase
# 使用场景:像 dev 这种长期分支希望历史更干净

git config --global core.autocrlf input
# 控制行结尾自动转换;默认因平台而异
# 使用场景(macOS/Linux):提交时将 CRLF 转为 LF,检出时保持 LF,避免混用换行符

git config --global core.autocrlf true
# Windows 常用;检出时将 LF 转为 CRLF,提交时转回 LF;默认取决于安装选项
# 使用场景:在 Windows 上开发且协作仓库以 LF 为主

git config --global core.editor "code --wait"
# 设置默认文本编辑器为 VS Code;默认可能是 vim 或系统编辑器
# 使用场景:在 commit/rebase 等需要编辑时使用更熟悉的编辑器

git config --list --show-origin
# 列出所有生效配置及其来源文件;默认不显示来源
# 使用场景:排查某个配置是在哪里被设置的

清理

git gc # 清理无用的对象数据,减小 .git 文件夹体积(Git 会自动执行,但手动执行可优化性能)。

初始化

git init [<options>] [<directory>]
# 在当前目录或 <directory> 初始化一个新的 Git 仓库

-b <name>, --initial-branch <name>
# 指定初始化时的首个分支名;默认使用 init.defaultBranch 或内置值(如 main/master)
# 使用场景:统一使用 main 作为默认分支;例:git init -b main

--template <目录>
# 从指定模板目录拷贝 hooks、配置等初始化仓库;默认使用系统模板目录
# 使用场景:团队统一预置 commit-msg hook 等;例:git init --template /path/to/template

--bare
# 创建 bare 仓库,仅含 .git 内容,无工作区;默认 false(普通仓库)
# 使用场景:作为远程中央仓库(服务器端);例:git init --bare repo.git

--separate-git-dir <gitdir>
# 将 Git 仓库存放在 <gitdir>,当前目录仅作为工作区;默认 .git 目录在当前目录
# 使用场景:共享对象库、多 worktree 高级用法;例:git init --separate-git-dir =/repo/store

远程

克隆

git clone [<options>] <repo> [<dir>]
# 克隆仓库到本地
# <repo> 远程地址
# <dir>  本地目录名(可选)

-b <branch>, --branch <branch>
# 克隆后直接检出指定分支;默认检出远程 HEAD
# 使用场景:只关心某个开发分支;例:git clone -b dev <url>

--depth <n>
# 浅克隆,只保留最近 n 次提交;默认完整历史
# 使用场景:CI 环境或大仓库只需最新代码,速度极快

--recurse-submodules
# 初始化并更新子模块;默认不处理子模块
# 使用场景:项目包含子模块且需要完整运行环境

-j <n>, --jobs <n>
# 并行拉取子模块的数量;默认 1
# 使用场景:加速包含大量子模块的仓库克隆

-o <name>, --origin <name>
# 自定义远程仓库名;默认 origin
# 使用场景:Fork 模式下将原仓库命名为 upstream

远程

git remote <subcommand> <args>
# 管理远程仓库连接

add <name> <url>
# 添加远程仓库;例:git remote add origin https://...

remove/rm <name> 
# 删除远程关联;例:git remote remove origin

set-url <name> <newurl>
# 修改远程 URL;例:git remote set-url origin git@github.com: user/repo.git
# 使用场景:从 HTTPS 切换到 SSH 协议

prune <name>
# 清理本地已失效的远程分支引用;同 git fetch -p
# 使用场景:远程分支已删,但 git branch -r 还能看到

-v, --verbose
# 显示详细 URL 信息;默认只显示名称
# 使用场景:查看 fetch/push 的具体地址;例:git remote -v

拉取

git fetch [<options>] [<repository>]
# 从远程拉取更新到本地跟踪分支(如 origin/main),不合并

-p, --prune
# 清理本地已过期的远程分支引用;默认不清理
# 使用场景:保持本地远程分支列表整洁

--all
# 拉取所有远程仓库的更新;默认只拉取当前关联的远程
# 使用场景:配置了多个远程(如 origin 和 upstream)时

--tags
# 强制拉取 tag;默认根据配置决定
# 使用场景:确保本地拥有所有发布版本标签
git pull [<options>] [<repository> [<refspec>...]]
# 拉取并合并(fetch + merge/rebase)

-r, --rebase
# 使用变基代替合并;默认 merge
# 使用场景:保持提交历史线性整洁;例:git pull -r

--autostash
# 拉取前自动 stash 本地修改,拉取后 pop;默认 false
# 使用场景:本地有未提交工作但也想同步远程代码;例:git pull -r --autostash

推送

git push [<options>] [<repository> [<refspec>...]]
# 推送本地提交到远程

-u, --set-upstream
# 建立追踪关系;默认不建立
# 使用场景:新分支首次推送;例:git push -u origin feature-a

-f, --force
# 强制推送(覆盖远程);默认拒绝非快进推送
# 使用场景:本地 rebase 重写历史后同步到远程(慎用)

--force-with-lease
# 安全的强制推送,仅当远程引用未被他人更新时才覆盖;默认无
# 使用场景:想强推但怕覆盖同事刚推上去的代码(推荐代替 -f)

-d, --delete
# 删除远程分支或标签;默认无
# 使用场景:删除已合并的远程分支;例:git push origin -d feature-a

--tags
# 推送所有本地标签;默认不推送标签
# 使用场景:发布版本时;例:git push origin --tags

--follow-tags
# 推送代码时顺带推送相关的 annotated tags;默认不推
# 使用场景:日常推送代码并附带版本号

查询

比较

比较两者差异

需要说明的是 <path> 是可选项,可以是目录或者文件。<字母> 是任意提交/分支/tag

git diff [<options>] [<commit>|<A>] [<B>] [--] [<path>...]
# 比较不同版本/状态之间的差异;不带提交时为 工作区 vs 暂存区

git diff
# 比较:工作区 vs 暂存区;默认显示未暂存改动
# 使用场景:提交前查看还没 add 的内容

git diff --staged
# 比较:暂存区 vs HEAD;默认关闭(需显式写出)
# 使用场景:查看即将提交的内容

git diff --cached
# 同 --staged;默认关闭
# 使用场景:与 --staged 等价命令

git diff <A>
# 比较:(工作区+暂存区) vs 提交 A;默认关闭
# 使用场景:当前状态与某历史提交的差异

git diff <A> <B> -- <path>
# 比较:提交 B vs 提交 A(顺序影响 + / - 方向);默认不限定 path
# 使用场景:对特定文件/目录在两个版本间的差异

git diff A...B
# 等价于 git diff $(git merge-base A B) B;即 B 相对 (A 和 B 的共同祖先) 的改动
# 使用场景:看分支 B 自从从 A 分出去之后自己做了什么
--no-index
# 即使不在 Git 仓库中,也比较两个路径;默认要求在仓库
# 使用场景:把 git 当通用 diff 工具;例:git diff --no-index a.txt b.txt

-u, -p
# 输出补丁格式(统一 diff),显示具体行级增删;默认即为补丁格式
# 使用场景:日常查看改动内容、代码审查

-z
# 输出 diff-raw,行结尾使用 NUL;默认用换行
# 使用场景:脚本逐行安全解析(含特殊字符路径)

-w, --ignore-all-space
# 完全忽略空白差异(缩进、空格数变化);默认不忽略
# 使用场景:统一格式化后只看逻辑变化;例:git diff -w HEAD~1

--ignore-space-at-eol
# 忽略行尾空白差异;默认不忽略
# 使用场景:避免无意义尾空格改动干扰

--ignore-space-change
# 忽略空白数量变化(多个空格变一个);默认不忽略
# 使用场景:仅关注 token 变化,不关心空格数量

--diff-filter=<ACDMRT>...
# 只显示特定类型变更(A 新增、D 删除、M 修改、R 重命名、C 拷贝、T 类型变化);默认全部
# 使用场景:只看新增/删除/重命名文件;例:git diff --diff-filter = D --name-only

--word-diff
# 行内级 diff,以词(或小片段)为单位显示差异;默认行级
# 使用场景:同一行小改动时更易看清

--color-moved
# 高亮显示移动的代码块;默认关闭
# 使用场景:大范围重构/移动代码时区分“移动 vs 新删”

--stat
# 显示每个文件增加/删除多少行及总改动摘要;默认显示完整补丁
# 使用场景:快速了解改动规模;例:git diff --stat

--numstat
# 每行输出 “新增行数 删除行数 文件名”;默认关闭
# 使用场景:脚本统计改动规模

--patch-with-stat
# 同时输出 stat 概览和详细 patch;默认关闭
# 使用场景:既要摘要又要行级细节

--name-only
# 只显示改动文件名;默认显示内容
# 使用场景:只关心哪些文件变了

--name-status
# 显示文件名及状态(A/M/D 等);默认不显示状态汇总
# 使用场景:查看新增/修改/删除文件列表

-R
# 交换比较方向;默认从左到右
# 使用场景:需要反向看 + / -(通常较少用)

-B
# 检测几乎完全重写的文件,将大量改动视为重写;默认轻度检测
# 使用场景:大量重写文件时更合理的 diff 表达

-M
# 检测重命名文件;默认有限检测(视设置)
# 使用场景:重命名文件不想被看成“删一个加一个”

-C
# 检测拷贝文件;默认不做复杂 copy 检测
# 使用场景:识别从现有文件复制出来的新文件

-l <n>
# 限制重命名检测的路径数上限;默认较大
# 使用场景:极大仓库提高 diff 性能

-O<file>
# 按 <file> 中列出的路径顺序输出 diff;默认按路径排序
# 使用场景:自定义审阅顺序

-S<string>
# 找到引入或删除特定字符串的变更;默认不筛选
# 使用场景:定位引入某函数/常量的提交

--pickaxe-all
# 配合 -S,在命中时显示所有文件的 diff;默认只显示匹配文件
# 使用场景:查看某个改动上下文的完整提交影响

-a, --text
# 将所有文件当作文本处理,即便看起来像二进制;默认按类型判断
# 使用场景:误判为二进制的文本文件(如含特殊字符的 log)

状态

显示 哪些文件已经暂存还未提交,哪些被跟踪且被修改,哪些未被跟踪的文件。

git status [<options>] [--] [<path>...]
# 显示工作区、暂存区相对于 HEAD 的状态:已暂存、未暂存、未跟踪文件等

git status
# 详细状态,包含提示信息;默认命令
# 使用场景:日常了解当前改动情况

-v, --verbose
# 显示更详细信息(某些模式下可以附带 diff);默认关闭
# 使用场景:希望在 status 中直接看到更多上下文

-s, --short
# 简洁输出(双列状态码,如 M 、A、D、??);默认长格式
# 使用场景:快速查看变更概况;例:git status -s

--porcelain[=<version>]
# 机器可读稳定格式(无颜色、固定字段);默认人类可读
# 使用场景:脚本解析仓库状态

-b, --branch
# 显示当前分支名和与上游的 ahead/behind 信息;默认已包含
# 使用场景:关注当前分支相对远程的进度

-z, --null
# 用 NUL 分隔路径名而不是换行;默认换行
# 使用场景:脚本安全处理包含空格/特殊字符的文件名

-u[=<mode>], --untracked-files[=<mode>]
# 控制未跟踪文件显示;默认 all
# mode: all(全部)、normal(普通)、no(不显示)
# 使用场景:大量生成文件时用 -uno 减少噪音;例:git status -uno

--ignore-submodules[=<when>]
# 忽略子模块变更;默认 all
# when: all(全部忽略)、dirty(忽略已提交状态的子模块中的本地改动)、untracked(忽略子模块内未跟踪文件)
# 使用场景:含子模块项目但暂时不关心其内部变更

--no-ignore-submodules
# 不忽略子模块变更;默认按 ignore-submodules 设置
# 使用场景:需要检查子模块状态时显式开启

--ignored[=<mode>]
# 显示被 .gitignore 忽略的文件;默认 traditional(有限提示)
# mode: traditional(传统行为)、matching(列出所有匹配忽略规则的文件)、no(不显示)
# 使用场景:检查 .gitignore 是否正确;例:git status --ignored = matching

日志

git log [<options>] [<revision range>] [[--] <path>...]
# 查看提交历史

--oneline
# 单行简洁显示(Hash + 标题);默认显示完整信息
# 使用场景:快速浏览历史脉络

--graph
# ASCII 图形化显示分支合并路径;默认无图
# 使用场景:查看分支分叉和合并结构

--decorate
# 显示分支和标签指向(通常配合 --oneline);默认 auto
# 使用场景:查看提交对应的引用名称

--all
# 显示所有分支的历史,不仅是当前分支;默认仅当前
# 使用场景:配合 --graph 查看整个仓库的演进;例:git log --oneline --graph --all

-p, --patch
# 显示每次提交的具体代码差异;默认仅显示元数据
# 使用场景:代码审查,查看具体改了什么

--stat
# 显示每次提交的文件增删统计;默认无
# 使用场景:了解每次提交的改动规模

-n <number>
# 限制显示最近 n 条;默认显示所有
# 使用场景:只看最近几次提交;例:git log -n 5

--author="<pattern>"
# 按作者筛选;默认所有作者
# 使用场景:查找某个人的提交

--grep="<pattern>"
# 按提交信息搜索;默认无
# 使用场景:查找包含 "bugfix" 的提交

-S "<string>"
# 搜索代码内容的变动(添加或删除);默认搜 message
# 使用场景:查找 "const API_URL" 这行代码是哪次提交写的

--since="2 weeks ago"
# 按时间筛选;默认所有时间
# 使用场景:生成周报或月报
git reflog [<options>]
# 查看引用日志

<无参数>
# 显示 HEAD 的变动历史
# 使用场景:找回被 reset --hard 丢弃的提交,或找回被删分支的末端提交

show <branch>
# 显示指定分支的变动历史
# 使用场景:查看某个分支的指针移动轨迹

--date=iso
# 显示具体操作时间;默认相对时间
# 使用场景:按时间点定位误操作

工作流

暂存

git add [<options>] [--] <pathspec>...
# 将工作区改动加入暂存区,为下一次提交做准备

git add file
# 添加单个文件当前改动到暂存区;默认不包含未指定文件
# 使用场景:选择性地提交部分文件

git add dir/
# 添加整个目录;默认递归子目录
# 使用场景:对模块目录整体暂存

git add .
# 添加当前目录下所有修改和新文件(但不记录删除);默认基于当前目录
# 使用场景:简单场景快速暂存所有新增和修改

git add -u
# 只添加已跟踪文件的修改和删除(不包含新文件);默认不启用
# 使用场景:只提交已有文件的变动;例:git add -u && git commit

git add -A
# 添加所有修改,包括新增/修改/删除;默认不启用
# 使用场景:本次提交希望完全反映当前状态;等价于旧版本中 git add . && git add -u

git add -n, --dry-run
# 预演,显示哪些文件将被添加,但不实际添加;默认 false
# 使用场景:确认 add 的文件范围;例:git add -n .

git add -v, --verbose
# 显示添加时的详细信息;默认关闭
# 使用场景:调试暂存内容

git add -i, --interactive
# 交互式暂存菜单(按文件/块操作);默认关闭
# 使用场景:需要更精细控制暂存内容

git add -p, --patch
# 按 hunk 交互式选择暂存;默认关闭
# 使用场景:一个文件里做了多处不相关改动,只想提交其中部分;例:git add -p app.js

-U<n>, --unified <n>
# 配合 -p,显示每个 hunk 前后 n 行上下文;默认 3 行
# 使用场景:在 patch 模式下看更多上下文方便判断

--inter-hunk-context <n>
# 若两个 hunk 之间行距 <= n,则合并显示中间上下文;默认仅分段显示
# 使用场景:相近多处改动希望作为一个整体查看/暂存

-e, --edit
# 在编辑器中手动编辑当前 diff 后再应用到暂存区;默认关闭
# 使用场景:需要非常精细地删减/重组补丁(高级)

-f, --force
# 强制添加被 .gitignore 忽略的文件;默认不添加被忽略文件
# 使用场景:个别 ignore 规则中的文件确实要入库;例:git add -f debug.log

--pathspec-from-file <file>
# 从 <file> 中读取要添加的路径列表;默认从命令行参数
# 使用场景:脚本/批量操作指定大量路径

提交

git commit [<options>] [--] [<pathspec>...]
# 将暂存区内容生成一个新的提交

-m "<message>"
# 在命令行直接指定提交说明;默认打开编辑器填写
# 使用场景:简洁说明提交内容;例:git commit -m "fix: 修复登录 bug"

-a, --all
# 在提交前自动暂存所有已跟踪文件的修改和删除(等价于 git add -u);默认不启用
# 使用场景:小改动、只涉及已跟踪文件时简化流程;例:git commit -am "small fix"

--dry-run
# 预演提交,显示将要提交的内容但不真正创建提交;默认 false
# 使用场景:确认暂存内容是否符合预期

--amend
# 修改上一次提交(内容和/或 message),生成新提交替换旧提交;默认 false
# 使用场景:刚提交完发现漏了文件或信息写错;例:git commit --amend

--amend --no-edit
# 仅修改上次提交内容,不更改提交说明;默认需要编辑信息
# 使用场景:只想往上一次提交里补文件

-v, --verbose
# 在编辑器中附带显示本次提交的 diff;默认不显示
# 使用场景:边看 diff 边写 message;例:git commit -v

--template <file>
# 使用指定模板文件作为默认提交信息;默认无模板
# 使用场景:团队统一 commit message 格式

--cleanup <mode>
# 控制提交信息中空行和注释的清理方式;默认 strip
# 使用场景:需要保留特定格式时调整

--fixup=<commit>
# 创建一个“修复”指定提交的特殊提交(自动带 fixup! 前缀);默认不启用
# 使用场景:配合 rebase -i --autosquash 将修复提交自动合并回原提交

# 与 fixup 搭配的常用命令:
git rebase -i --autosquash <base>
# 交互式变基并自动整理 fixup!/squash! 提交;默认不开启 autosquash
# 使用场景:清理历史前将多次修正压缩到原提交

还原

还原文件内容/状态

git restore [<options>] [--source=<tree-ish>] [--] <file>...
# 还原文件内容/状态到某个提交或暂存区状态

git restore <file>
# 恢复工作区中文件为 HEAD 版本;默认只影响工作区(等价 --worktree)
# 使用场景:丢弃尚未暂存的修改;例:git restore app.js

-W, --worktree
# 显式指定只恢复工作区;默认开启
# 使用场景:明确只想丢弃本地修改不动暂存区

-S, --staged
# 恢复暂存区(index)中文件为指定版本;默认不影响暂存区
# 使用场景:撤销 git add;例:git restore --staged app.js

--source=<branch/commit>
# 指定从哪个版本恢复,默认 HEAD
# 使用场景:从其他分支或历史版本恢复文件;例:git restore --source = main path/to/file

-q, --quiet
# 安静模式,抑制进度输出;默认关闭
# 使用场景:脚本中减少噪音

--progress / --no-progress
# 控制是否显示进度条;默认终端显示进度

-m, --merge
# 在恢复时进行三方合并,保留当前改动并尝试合并目标版本;默认直接覆盖
# 使用场景:希望将另一个版本的更改合并进当前修改而不是覆盖

-2, --ours
# 在冲突状态下,选择“我们的版本(当前分支)”;默认不改变冲突选择
# 使用场景:解决冲突时批量保留当前分支版本

-3, --theirs
# 在冲突状态下,选择“对方版本(合入分支)”;默认不改变冲突选择
# 使用场景:解决冲突时批量接受远程或目标分支版本

-p, --patch
# 交互式按 hunk 选择恢复哪些改动;默认恢复整个文件
# 使用场景:只想丢弃一部分改动;例:git restore -p file

-U <n>, --unified <n>
# 修补显示时的上下文行数(配合 -p);默认 3
# 使用场景:在 patch 模式恢复时查看更多上下文

--pathspec-from-file <file>
# 从文件中读取要恢复的路径列表;默认从命令行参数
# 使用场景:批量恢复大量路径

删除

git rm [<options>] [--] <path>...
# 删除工作区文件并将删除操作加入暂存区

git rm filename.txt
# 删除该文件并暂存删除;默认不强制删除有未提交修改的文件
# 使用场景:从项目中移除文件,并在下次提交中记录删除

--cached
# 只从暂存区和版本控制中删除,不删除工作区文件;默认会删除文件
# 使用场景:停止跟踪某文件但保留本地副本;例:git rm --cached config.local

-r
# 递归删除目录下所有受控文件;默认仅删除单个文件
# 使用场景:移除整个模块目录;例:git rm -r old_module/

-f, --force
# 强制删除即使本地有未提交修改;默认拒绝以防止数据丢失
# 使用场景:确认改动不再需要时

回退

git reset [<mode>] [<commit>] [--] [<paths>...]
# 重置当前分支 HEAD 和 index/工作区到指定状态,常用于“撤销提交/暂存”

git reset --soft HEAD~1
# 仅移动 HEAD(撤销最近一次提交),保留暂存区和工作区;默认模式为 --mixed
# 使用场景:想修改最近提交的 message 或合并提交内容

git reset --mixed HEAD~1
# 移动 HEAD 并重置暂存区为该提交,但保留工作区;这是默认模式
# 使用场景:撤销提交并退回到“只改了文件还没 add”的状态

git reset --hard HEAD~1
# 移动 HEAD 并同时重置暂存区和工作区为该提交;会丢失未保存改动
# 使用场景:完全放弃最近提交及当前修改(危险)

git reset --hard <commit>
# 将当前分支回退到指定提交;默认会丢弃之后所有提交和本地修改
# 使用场景:重置分支历史(未推送时使用)

git revert <commit>
# 创建一个新的提交来“反做”指定提交的改动;默认保留历史
# 使用场景:已推送的提交需要撤销,但不想重写历史;例:git revert HEAD

git revert HEAD
# 撤销最近一次提交,生成反向提交

-n, --no-commit
# 执行反转但不自动生成提交;默认自动提交
# 使用场景:连续撤销多个提交,最后一次性 commit

git ls-tree -r <commit> --name-only
# 列出某个提交中包含的所有文件名;默认不加 -r 只列当前目录
# 使用场景:查看某历史版本的文件列表

stash

git stash [push] [<options>]
# 临时保存工作区进度

-m "<message>"
# 保存当前进度并添加说明;默认无说明
# 使用场景:需切换分支处理紧急任务;例:git stash -m "WIP: login form"

-u, --include-untracked
# 同时暂存未追踪的文件;默认只暂存已追踪
# 使用场景:新增了文件还没 add,想一起暂存

pop [<index>]
# 恢复最近一次进度并从列表中删除;默认 stash@{0}
# 使用场景:回到之前的任务继续工作

apply [<index>]
# 恢复进度但不删除;默认 stash@{0}
# 使用场景:尝试恢复,确保无冲突后再 drop

list
# 列出所有 stash 记录
# 使用场景:查看保存了哪些现场

drop [<index>]
# 删除指定的 stash;默认 stash@{0}

clear
# 清空所有 stash;(慎用)

分支

管理

git branch [<options>] [<branch-name>]
# 管理分支

-a, --all
# 查看所有分支(本地+远程);默认仅本地
# 使用场景:查看远程有哪些分支

-d
# 删除已合并的分支;默认不删除未合并分支
# 使用场景:清理已完成的功能分支

-D
# 强制删除分支;默认拒绝
# 使用场景:丢弃无用的实验性分支或错误分支

-m <old> <new>
# 重命名分支;默认无
# 使用场景:纠正拼写错误;例:git branch -m masetr master

-vv
# 显示分支对应的远程上游及领先/落后情况;默认简略
# 使用场景:检查本地分支是否与远程同步

--contains <commit>
# 列出包含指定提交的分支;默认无
# 使用场景:判断某个 bug 修复是否已经合并到了 release 分支

切换

git switch [<options>] <branch>
# 切换分支

-c, --create <new-branch>
# 创建并切换到新分支;默认仅切换
# 使用场景:开始新功能开发;例:git switch -c feature-new

-
# 切换回上一个分支(类似 cd -);默认无
# 使用场景:在两个分支间频繁切换对比

--detach <commit>
# 切换到游离 HEAD 状态;默认切换分支
# 使用场景:只想查看某个历史提交,不创建新分支

合并

git merge [<options>] <commit>
# 合并指定分支到当前分支

--no-ff
# 禁止快进合并,强制生成 merge commit;默认允许快进
# 使用场景:保留分支合并的历史痕迹(Feature Flow 常用)

--squash
# 将目标分支所有提交压缩为一个变更,不自动提交;默认 false
# 使用场景:将琐碎的开发提交合并成一个整洁的提交

--abort
# 终止合并,恢复到合并前状态;默认无
# 使用场景:解决冲突太麻烦,想放弃合并

变基

git rebase [<options>] <upstream>
# 将当前分支的修改“移植”到上游分支之上

-i, --interactive
# 交互式变基;默认自动
# 使用场景:修改历史、合并提交、调整提交顺序、删除提交;例:git rebase -i HEAD~3

--continue
# 解决冲突后继续变基
# 使用场景:变基过程中断后继续

--abort
# 终止变基,回到操作前状态
# 使用场景:变基搞砸了,想重来

--onto <newbase> <oldbase> <branch>
# 变基移植,将 <branch> 从 <oldbase> 移植到 <newbase>;默认无
# 使用场景:高级分支修剪;例:把 feature 从 dev 移植到 main

Cherry-pick

git cherry-pick [<options>] <commit>...
# 将特定的提交应用到当前分支

-n, --no-commit
# 应用修改但不自动提交;默认自动提交
# 使用场景:想把改动拿过来再修补一下

-e, --edit
# 编辑提交信息;默认使用原提交信息

符号

名称与引用

  • HEAD:当前检出的提交/分支。
  • HEAD^ 或 HEAD~:父提交(上一个版本)。
  • HEADn:第 n 级父提交。例:HEAD2(爷爷节点)。
  • HEAD^n:第 n 个父节点(用于合并提交)。例:HEAD^2(Merge 时的第二个分支来源)。
  • hash:SHA-1 哈希值(前 7 位通常够用)。
  • branch@{n}:Reflog 引用。例:main@{yesterday}(昨天 main 分支指向哪里)。

标记

git tag [<options>] [<tagname> [<commit>]]
# 管理版本标签

-a <name> -m "<msg>"
# 创建附注标签(推荐);默认轻量标签
# 使用场景:发布版本 v1.0.0

-d <name>
# 删除本地标签

<无参数>
# 列出所有标签

通配符

*:通配符。
	例:git log *.js(查看所有 js 文件的历史)。
::魔术前缀。
  :/<text>:全局按提交信息搜索最近的提交。例:git show :/fix(显示最近包含 fix 的提交)。
  :(top) 或 :/:从项目根目录开始匹配。例:git grep "TODO" :/(在整个项目搜 TODO)。
  :(exclude) 或 :!:排除模式。例:git add . :!vendor/(添加当前目录但不包含 vendor 文件夹)。
.. 和 ...:范围选择。
  A..B:在 B 中但不在 A 中的提交(A 到 B 的补集)。
  A...B:A 和 B 的对称差(Diff 中常用,Log 中表示两个分支各自独有的提交)。

.gitignore

注意事项

  • .gitignore 只对未跟踪的文件生效
  • 项目根目录的 .gitignore 对整个仓库生效,子目录的 .gitignore 只对当前子目录生效(优先级高于根目录)。
  • ~/.gitignore_global 是全局配置,对所有仓库生效(需通过 git config --global core.excludesfile ~/.gitignore_global 配置)。

普通文件名 / 目录名

直接写文件名或目录名,匹配所有同名文件 / 目录。 示例:

# 忽略所有名为 "log.txt" 的文件
log.txt

# 忽略所有名为 "node_modules" 的目录
node_modules/  # 加斜杠 / 表示仅匹配目录(不加也能匹配,但加/更明确)

通配符 \*

匹配任意字符(不包含路径分隔符 /)。 示例:

# 忽略所有 .log 后缀的文件
*.log

# 忽略所有以 "temp_" 开头的文件
temp_*

递归匹配 \**

匹配任意层级的目录(包含路径分隔符 /)。 示例:

# 忽略所有目录下的 "dist" 文件夹(无论在哪个层级)
**/dist/

# 忽略 "src" 目录下所有层级的 ".test.js" 文件
src/**/*.test.js

路径分隔符 /

  • 放在开头:仅匹配仓库根目录下的文件 / 目录。
  • 放在中间:表示目录层级。 示例:
# 只忽略根目录下的 "config.ini",子目录的不忽略
/config.ini

# 忽略 "docs" 目录下的 "notes.txt",其他目录的不忽略
docs/notes.txt

否定规则 !

取消前面定义的忽略规则(即 “例外”),需放在被否定的规则之后。 示例:

# 忽略所有 .txt 文件,但保留 "readme.txt"
*.txt
!readme.txt

# 忽略 "build/" 目录,但保留 "build/output/"
build/
!build/output/

注释 #

# 开头的行是注释,会被 Git 忽略(注意:# 不能放在行中间,否则后面的内容会被当作注释)。 示例:

# 这是一条注释,不会生效
*.log  # 这部分也是注释(仅 *.log 生效)

转义特殊字符 \

#!* 等特殊字符进行转义,使其被当作普通字符处理。 示例:

# 忽略名为 "file#1.txt" 的文件(# 需转义)
file\#1.txt

# 忽略名为 "*.log" 的文件(* 需转义)
\*.log

Hook

Git Hooks(钩子)是 Git 在特定事件(如提交、推送、合并)发生时自动触发的脚本。它们允许开发者在 Git 执行流程的特定点“插入”自定义逻辑。

通常位于项目的 .git/hooks/ 目录下。

  • 位置.git/hooks/
  • 激活方式:去掉文件名后缀 .sample,并赋予可执行权限 (chmod +x)。
  • 语言:任意可执行脚本(Bash, Python, Node, Ruby 等),只要有正确的 Shebang (如 #!/bin/sh)。
  • 阻断机制:如果脚本退出码(Exit Code)非 0,Git 会中止当前操作(适用于 pre- 开头的钩子)。
钩子名称 类型 阻断操作? 典型用途
pre-commit 客户端 Lint 代码、格式化、简单的单元测试
prepare-commit-msg 客户端 自动生成提交信息模板
commit-msg 客户端 校验提交信息的格式规范
post-commit 客户端 发送通知
pre-push 客户端 运行全量测试、防止推送到受保护分支
pre-receive 服务端 权限校验、大文件拦截
post-receive 服务端 触发 CI 构建、自动部署脚本