Git 从入门到协作:原理、命令与安全工作流
从三棵树和内容寻址模型开始,掌握日常 Git 命令、分支协作、冲突处理、回退与发布。
核心理念:AI 时代不再需要死背命令行参数,但理解 Git 底层机制比以往任何时候都更加关键。精准的原理认知能帮我们写出最精确的 AI 提示词,让 AI 成为你的 Git 协驾,而非盲目执行的隐患。
本指南以 「底盘原理 📘 + VS Code 视觉操作 💻 + AI 降维打击 🤖」 的卡片化形式,带你通关 Git 核心模型。
📋 基础命令速查表
以下是日常开发中最常用的 Git 基础命令,涵盖仓库初始化、状态查看、分支管理、远程协作等场景。本文后续章节会深入讲解进阶操作(stash、rebase、cherry-pick 等),此处先打好地基。
仓库初始化与克隆
| 命令 | 作用 | 示例 |
|---|---|---|
git init | 在当前目录初始化一个新的 Git 仓库 | git init my-project |
git clone <url> | 克隆远程仓库到本地 | git clone https://github.com/user/repo.git |
git clone <url> <dir> | 克隆到指定目录名 | git clone https://github.com/user/repo.git my-local |
状态查看与信息浏览
| 命令 | 作用 | 示例 |
|---|---|---|
git status | 查看工作区和暂存区的状态 | git status |
git status -s | 简洁模式输出状态 | git status -s |
git log | 查看提交历史 | git log |
git log --oneline | 单行简洁模式 | git log --oneline |
git log --oneline -10 | 只看最近 10 条 | git log --oneline -10 |
git log --graph | 图形化显示分支合并历史 | git log --oneline --graph --all |
git diff | 查看工作区与暂存区的差异 | git diff |
git diff --staged | 查看暂存区与上次 commit 的差异 | git diff --staged |
git diff <branch1> <branch2> | 比较两个分支的差异 | git diff main feature/login |
git show <commit> | 查看某次 commit 的详细内容 | git show a1b2c3d |
git blame <file> | 逐行查看文件的最后修改者 | git blame src/app.py |
分支管理
| 命令 | 作用 | 示例 |
|---|---|---|
git branch | 查看本地分支列表(* 标记当前分支) | git branch |
git branch -a | 查看所有分支(含远程分支) | git branch -a |
git branch -v | 查看各分支最后一次提交 | git branch -v |
git branch <name> | 创建新分支(不切换) | git branch feature/login |
git branch -d <name> | 删除已合并的分支 | git branch -d feature/old |
git branch -D <name> | 强制删除分支(即使未合并) | git branch -D feature/abandoned |
git branch -m <old> <new> | 重命名分支 | git branch -m feature/login feature/auth |
git switch <branch> | 切换到指定分支(推荐,Git 2.23+) | git switch main |
git switch -c <name> | 创建并切换到新分支 | git switch -c feature/signup |
git checkout <branch> | 切换分支(传统方式) | git checkout main |
git checkout -b <name> | 创建并切换分支(传统方式) | git checkout -b feature/signup |
💡 switch vs checkout:Git 2.23 引入了
git switch和git restore,将checkout的「切换分支」和「恢复文件」两个职责拆分开来,语义更清晰。新手推荐用switch/restore,老手两者皆可。
暂存与提交
| 命令 | 作用 | 示例 |
|---|---|---|
git add <file> | 将指定文件加入暂存区 | git add src/app.py |
git add . | 将当前目录所有修改加入暂存区 | git add . |
git add -p | 交互式选择要暂存的代码块 | git add -p |
git commit -m "msg" | 提交暂存区内容 | git commit -m "feat: add login" |
git commit -am "msg" | 跳过 add,直接提交已跟踪文件的修改 | git commit -am "fix: typo" |
git commit --amend | 修改最近一次 commit(message 或内容) | git commit --amend -m "fix: typo" |
git restore <file> | 丢弃工作区的修改(恢复到暂存区状态) | git restore src/app.py |
git restore --staged <file> | 取消暂存(将文件从暂存区移回工作区) | git restore --staged src/app.py |
git reset HEAD <file> | 取消暂存(传统方式,同上) | git reset HEAD src/app.py |
远程仓库操作
| 命令 | 作用 | 示例 |
|---|---|---|
git remote -v | 查看远程仓库地址 | git remote -v |
git remote add <name> <url> | 添加远程仓库 | git remote add origin https://github.com/user/repo.git |
git remote remove <name> | 移除远程仓库 | git remote remove upstream |
git fetch <remote> | 拉取远程更新(不合并) | git fetch origin |
git fetch --all | 拉取所有远程仓库的更新 | git fetch --all |
git pull | 拉取远程更新并合并到当前分支 | git pull origin main |
git pull --rebase | 拉取并用 rebase 代替 merge 合并 | git pull --rebase origin main |
git push | 推送本地提交到远程 | git push origin main |
git push -u origin <branch> | 推送并设置上游跟踪关系 | git push -u origin feature/login |
git push origin --delete <branch> | 删除远程分支 | git push origin --delete feature/old |
标签管理
| 命令 | 作用 | 示例 |
|---|---|---|
git tag | 查看所有标签 | git tag |
git tag <name> | 创建轻量标签 | git tag v1.0.0 |
git tag -a <name> -m "msg" | 创建附注标签(含描述信息) | git tag -a v1.0.0 -m "首次正式发布" |
git tag -d <name> | 删除本地标签 | git tag -d v1.0.0 |
git push origin <tag> | 推送标签到远程 | git push origin v1.0.0 |
git push origin --tags | 推送所有本地标签 | git push origin --tags |
撤销与回退
| 命令 | 作用 | 示例 |
|---|---|---|
git reset --soft <commit> | 回退到指定 commit,保留修改在暂存区 | git reset --soft HEAD~1 |
git reset --mixed <commit> | 回退到指定 commit,保留修改在工作区(默认) | git reset --mixed HEAD~1 |
git reset --hard <commit> | 回退到指定 commit,丢弃所有修改 ⚠️ | git reset --hard HEAD~1 |
git revert <commit> | 创建一个新 commit 来撤销指定 commit(安全) | git revert a1b2c3d |
git reflog | 查看 HEAD 的移动历史(后悔药) | git reflog |
⚠️
git reset --hard会永久丢弃未提交的修改,谨慎使用!如果误操作了,git reflog是你的后悔药 —— 它记录了 HEAD 的每一次移动,可以找回被 reset 掉的 commit。
📘 关于本速查表:以上命令覆盖了 Git 日常使用 90% 的场景。接下来的章节会深入讲解 Detached HEAD、Worktree、Stash、Rebase、Cherry-pick 等进阶操作的原理与 AI 协作范式。
0. 先理解 Git 的真正面目:内容寻址文件系统
🤖 AI 提示词要点:当你告诉 AI「帮我找回某个版本的文件」或「查找这个 blob 在哪些 commit 中出现过」,背后依赖的都是这套对象模型。
在深入操作之前,先把 Git 的底层世界观掰清楚。Git 本质上不是一个「版本管理工具」,而是一个 内容寻址文件系统(Content-Addressable Filesystem),版本管理只是构建在它之上的上层应用。
Git 内部只有 4 种对象,全部以 SHA-1 哈希命名,存储在 .git/objects/ 中:
| 对象类型 | 核心职责 | 类比 |
|---|---|---|
| blob | 存储文件内容(不含文件名) | 一页手稿 |
| tree | 存储目录结构,指向 blob 和子树 | 文件夹 |
| commit | 指向一棵 tree,记录作者、时间、父 commit | 一张快照照片 |
| tag | 给某个 commit 打上可读标签 | 便签条 |
一张图讲清楚它们的关系:
commit (SHA: a1b2c3...)
├── tree (根目录)
│ ├── blob "README.md"
│ ├── blob "main.py"
│ └── tree "src/"
│ └── blob "utils.py"
├── parent: commit (SHA: d4e5f6...)
├── author: "张三 <zhang@example.com>"
└── message: "feat: add utils module"
几个关键推论:
- 相同内容 = 相同哈希。两个文件内容一样,Git 只存一份 blob。这天然实现了去重。
- commit 不存 diff,存完整快照。每个 commit 都指向一棵完整的 tree。Git 之所以省空间,靠的是对象压缩(packfile),不是存差异。
- parent 指针形成 DAG(有向无环图)。你的提交历史本质上就是一条或分叉的哈希链。
📘 原理笔记:用
git cat-file -p <hash>可以窥探任意对象的内部结构。这是理解 Git 最直接的实验手段 —— 随便拿个 commit hash,一层层往下追踪。
1. 把仓库回退到历史 —— 分离头指针(Detached HEAD)
有时需要回到某个历史提交看一眼代码 —— 不是回退,只是「穿越」。VS Code 给了两条路:
方案 A:分离头指针(不推荐)
右键某个 commit 记录,选择「分离头指针(Detached HEAD)」。
此时 HEAD 直接指向该 commit,而非指向分支名。在这种状态下:
- 如果你 不做修改,随便切走就行,毫发无损。
- 如果你做了新 commit,它们会挂在匿名指针上 —— 一旦切走就找不到了(虽然可以用
git reflog救援,但没必要给自己添麻烦)。
⚠️ 不推荐在 detached HEAD 状态下直接改代码,容易搞丢。
方案 B:基于历史提交新建分支(推荐 ✅)
右键目标 commit → 创建分支。
它会从那个历史点分出一条新分支。此时 HEAD → 分支名 → commit,安全可靠。你可以随意在上面实验、提交,完成后合并回去或直接删掉。
🤖 AI 提示词范例:「基于 commit abc123 创建一个叫 hotfix-old-bug 的分支,我需要在那个历史节点修一个老问题。」
2. Worktree:同时活在多个分支里
传统痛点
正在 feature-a 上开发到一半,突然需要切到 main 修个紧急 bug。但工作区没清理干净(编译产物、中间文件),直接 git checkout main 会报错或带来混乱。
Worktree 解决一切
git worktree 允许你在同一仓库的 不同目录 中同时 checkout 多个分支。每个 worktree 有自己的工作区 + 暂存区 + HEAD,但共享同一个 .git/objects 数据库。
# 从当前仓库的 main 分支创建一个新 worktree
git worktree add ../hermes-hotfix main
# 列出所有 worktree
git worktree list
# 用完后删除(但分支还在)
git worktree remove ../hermes-hotfix
AI 工具的原生支持
部分 AI 编码工具已将 worktree 提升为一等公民:
- Claude Code 支持
claude --worktree add-xxxx,一步创建独立 worktree 并在其中启动 AI 开发会话。 - Codex 同样原生支持 worktree 隔离。
AI 会在独立的 worktree 里完成任务,完成后合并回主干并清理:
claude --worktree add-fix-bug-42 # AI 在隔离空间工作
# ... AI 完成修改并提交 ...
git checkout main && git merge add-fix-bug-42
git worktree remove ../add-fix-bug-42
🤖 AI 提示词范例:「用 worktree 隔离一个环境,在 fix-login-timeout 分支上修复超时问题,完成后再合并回 main 并删除 worktree。」
3. 修改同一个文件产生冲突 —— AI 来裁判
当两个人(或两个分支)修改了同一个文件的同一处代码,Git 无法自动选择,就会标记为冲突:
<<<<<<< HEAD
const TIMEOUT = 3000; // 你的版本
=======
const TIMEOUT = 5000; // 对方的版本
>>>>>>> feature/new-api
传统做法
手动判断保留哪一侧,或两者都保留后重新编排逻辑。VS Code 会在冲突处提供内联按钮:Accept Current / Accept Incoming / Accept Both。
AI 时代的做法
直接把冲突文件和上下文丢给 AI,让它判断:
🤖 AI 提示词范例:「这段代码发生了合并冲突,HEAD 把超时改成了 3000ms,incoming 分支改成了 5000ms 并且增加了重试逻辑。分析一下应该怎么合并,然后帮我解决冲突。」
AI 能做的事情远超「选左边还是右边」:
- 理解双方的修改意图
- 提出第三种整合方案
- 检查合并后是否有逻辑矛盾
避免冲突的最佳实践
| 策略 | 说明 |
|---|---|
| 小步提交 | commit 粒度越细,冲突概率越低 |
| 频繁 rebase | 在开发分支上 git rebase main,把冲突消解在前 |
| 模块化设计 | 不同功能修改不同文件,天然避免碰撞 |
| 沟通 | 知道队友在改什么,主动避让 |
4. Git 分区概念:三棵树模型
Git 的核心工作流可以抽象为 三棵树(或叫三个区域):
| 区域 | 在哪里 | 作用 |
|---|---|---|
| 工作区(Workspace) | 你硬盘上的文件 | 修改、新增、删除 —— 干活的地方 |
| 暂存区(Staging / Index) | .git/index | 你决定哪些修改要纳入下一次 commit |
| 本地仓库(Repository) | .git/objects/ | commit 后的历史存档 |
工作区 → (git add)→ 暂存区 → (git commit)→ 本地仓库
为什么现代工具淡化了暂存区
VS Code 默认行为是「提交全部更改」—— 相当于自动 git add -A && git commit,跳过了显式的暂存步骤。对 90% 的日常场景这完全够用,但有两类场景仍需要精确控制暂存区:
- 拆碎一次修改为多个逻辑 commit:比如改了 5 个文件,其中 3 个是功能实现、2 个是无关的代码格式化。用
git add -p(交互式补丁选择)就能把一次修改拆成两次语义清晰的 commit。 - 只提交文件的一部分:修改了一个大文件的多处,但只想先提交其中一处改动。
🤖 AI 提示词范例:「帮我把我当前未暂存的修改拆分:和功能相关的变更放到一个 commit,纯格式化的变更放到另一个 commit。」
冷知识 💡
在 GitHub 仓库页面按 ? 可以查看全部快捷键 —— 不少资深开发者都不知道这个。
5. 贡献:Fork → PR 的完整流程
GitHub 的开放性建立在 Fork + Pull Request 的工作流上。标准流程如下:
① Fork 原仓库到自己的账号下
↓
② git clone <你的 fork>
↓
③ git checkout -b feature/xxx # 建立特性分支
↓
④ 修改代码 → commit → git push origin feature/xxx
↓
⑤ 在 GitHub 上发起 Pull Request(提出合并请求)
↓
⑥ 原仓库维护者 Review → 讨论 → 修改
↓
⑦ 维护者接受合并(Merge)
你有两个选择
方式 1:Fork 工作流(外部贡献者)
标准流程,适合没有原仓库写权限的贡献者。
方式 2:协作成员直推(内部团队 ✅)
如果你被邀请为原仓库的 Collaborator,就跳过了 Fork 这一步。可以直接在原仓库上建分支、推送,然后发起 PR。核心流程不变,但更轻量:
git checkout -b feature/my-work
# ... 写代码,提交 ...
git push origin feature/my-work
# → 在 GitHub 上发起 PR,等待 Review
🤖 AI 提示词范例:「我在协作仓库中,帮我从 main 创建一个 feature/refactor-auth 分支,把认证模块重构后提交,然后给我生成一段好的 PR 描述。」
PR 发生冲突怎么办
冲突不可怕,但需要正确处理:
# 1. 切换到你的 PR 分支
git checkout feature/my-work
# 2. 拉取最新的 main 并合并进来
git fetch origin
git merge origin/main
# 3. 手动解决冲突,mark as resolved
# 4. 提交并推送
git push origin feature/my-work
GitHub 会在 PR 页面自动更新状态,冲突标记消失后即可合并。
🤖 AI 提示词范例:「我的 PR 分支 feature/xxx 和 main 产生了冲突。帮我执行 git merge origin/main,把冲突文件列出来,然后逐个解决。」
6. Cherry-pick:精准摘樱桃 🍒
是什么
一个分支上有多个 commit,你只想把其中一两个「摘」到另一个分支上,而不是合并整个分支。
main: A --- B --- C --- D
↑ (你想把 C 摘过来)
fix: A --- B --- C'
怎么用
# 在目标分支上执行
git cherry-pick <commit-hash>
# 一次摘多个
git cherry-pick abc123 def456
# 摘一个连续区间(abc 不包含,def 包含)
git cherry-pick abc123..def456
Cherry-pick 会在目标分支上 创建全新的 commit(哈希不同),即使改动内容完全一样。Git 拿原 commit 的 diff,应用到目标分支的当前状态上。
典型场景
| 场景 | 示例 |
|---|---|
| 热修复回迁 | 在 release 分支上修了个紧急 bug,需要把它单独摘回 main |
| 跨分支复用 | feature-a 写了一个通用工具函数,想单独搬到 feature-b |
| 错分支修正 | 不小心在 main 上提交了,用 cherry-pick 搬到正确分支后,在原分支 git reset 撤销 |
🤖 AI 提示词范例:「把 commit f3a2b1(修复了空指针异常的那个)cherry-pick 到 release/v2.1 分支上。如果出现冲突,帮我分析为什么并解决。」
注意事项
- Cherry-pick 会产生冲突,解决方式与 merge 一样。
- 如果原始 commit 依赖了它之前的 commit,单独摘下来可能缺少上下文,需要手动补齐。
- 不要对已经推送到公共仓库的 commit 做 cherry-pick 后再 rebase,会让协作混乱。
7. Stash:代码暂存,随时归位
是什么
写到一半被叫走处理紧急任务?在 A 分支修改了很多文件但不想提交?由 stash 出场:
# 保存当前所有未提交修改(包括已暂存和未暂存)
git stash push -m "WIP: 正在重构认证模块"
# 保存时包含未跟踪的新文件
git stash push -u -m "WIP: 含新增文件"
# 查看 stash 列表
git stash list
# 输出:stash@{0}: On main: WIP: 正在重构认证模块
# 取回最近一次 stash 的内容(保留 stash 记录)
git stash apply
# 取回并删除 stash 记录
git stash pop
# 取回指定的 stash
git stash apply stash@{2}
# 删除某个 stash
git stash drop stash@{0}
典型场景
🔄 场景流程示意:
正在 feature-a 上写代码(工作区一堆未保存修改)
↓ 老板/同事突然需要你切到 main 修 bug
git stash push -m "WIP: feature-a half done"
↓ 工作区瞬间干净
git checkout main → 修 bug → commit → push
↓ 回到原来的战场
git checkout feature-a
git stash pop
↓ 修改全部回来了,继续写
Stash 的本质
Stash 内部也是 commit —— Git 会创建两个临时 commit(一个存暂存区内容,一个存工作区内容),存储在一个特殊的 ref refs/stash 下。所以 stash 本质上是一个 没有关联任何分支的、特殊的 merge commit。
🤖 AI 提示词范例:「我需要在 main 分支上临时修个 bug,帮我把当前 feature/new-ui 上的所有未提交改动 stash 起来,切到 main,修完后切回来再 pop。」
8. Rebase:重写历史,保持线性
是什么
Rebase 是 Git 中最强大也最容易误用的操作之一。它的本质是:把当前分支的提交,逐个「搬家」到目标分支的最新提交之后。
Git Merge(产生分叉):
main: A --- B --- C
\ \
feature: D --- E --- M (merge commit)
Git Rebase(线性历史):
main: A --- B --- C
\
feature: D' --- E'
Rebase 之后,D' 和 E' 是全新的 commit(哈希不同),但包含相同的改动。
基础用法
# 在 feature 分支上,把当前分支 rebase 到 main
git checkout feature
git rebase main
# 等价于一个指令
git rebase main feature
交互式 Rebase(Interactive Rebase)⭐
这是 Git 最强大的历史整理工具:
# 整理最近 3 次提交
git rebase -i HEAD~3
打开编辑器后会看到:
pick a1b2c3d feat: 添加登录功能
pick e4f5g6h fix: 修复登录按钮样式
pick i7j8k9l chore: 移除调试日志
可用的操作命令:
| 命令 | 作用 | 使用时机 |
|---|---|---|
pick | 保留该 commit | 默认操作 |
reword | 保留但修改 commit message | 上一个人写的信息含糊不清 |
squash | 合并到上一个 commit,合并 messages | 多次提交其实是一个功能的迭代 |
fixup | 合并到上一个 commit,丢弃 message | 同 squash,但不需要保留那坨提交信息 |
drop | 删除该 commit | 我不小心提交了 API key 😱 |
edit | 暂停,让你修改这次提交的内容 | 漏了一个文件没加 |
⚠️ 黄金法则
永远不要 rebase 已经推送到公共仓库的 commit。
Rebase 会重写哈希。如果你 rebase 了别人已经 pull 下来的分支,协作者的本地历史会和你产生分歧,强制推送(force push)会让其他人的工作陷入混乱。
| 可以 rebase | 不可以 rebase |
|---|---|
| 本地的、未推送的分支 ✅ | 已经 push 到共享仓库的分支 ❌ |
| 自己的 feature 分支(推送前整理)✅ | main / master / develop 等公共分支 ❌ |
| PR 分支在 Review 过程中(团队协商后)⚠️ | 已经被别人基于它开了新分支的分支 ❌ |
Rebase vs Merge:什么时候用什么?
| 维度 | Merge | Rebase |
|---|---|---|
| 历史记录 | 保留真实的分叉与合并轨迹 | 线性、整洁 |
| 冲突解决 | 一次性解决所有冲突 | 可能每个 commit 都遇到冲突 |
| 安全性 | 不改变历史,安全 | 改变历史,需要 force push |
| 适用场景 | 公共分支合并、发布记录 | 整理个人分支、保持主干线性 |
🤖 AI 提示词范例:「帮我把 feature/my-work 分支 rebase 到最新的 main 上。如果有冲突,在每次冲突时暂停让我确认。完成后,用 git rebase -i 把最后 4 个 commit squash 成 2 个语义清晰的 commit。」
9. 进阶融合:AI 时代的 Git 工作范式
9.1 AI 提示词怎么写才算好
把前面学到的原理套进去,你的 prompt 就会从「帮我用 git」变成「帮我做这件事」:
❌ 模糊 prompt:
"帮我把代码合并一下"
✅ 精准 prompt:
"我在 feature/payment 分支上,想用 rebase 的方式合并最新的 main,
过程中如果有冲突先让我手动确认。完成后帮我 squash 最近 5 个 commit
为 2 个:一个关于 API 对接,一个关于 UI 调整。"
区别在于:精确的 prompt 里包含了 操作对象、操作方法、冲突策略、整理策略。你不需要背命令,但需要知道这些概念叫什么名字、在什么场景下用。
9.2 一张速查表
| 我要做什么 | 操作 | AI 关键词 |
|---|---|---|
| 回到历史看看代码 | checkout 到历史 hash / 分离头指针 | detached HEAD, checkout |
| 基于老版本开新分支 | 从 old commit 创建分支 | create branch from commit |
| 同时干两个分支的活 | worktree | worktree add, 多工作树 |
| 保存现场,先去干别的 | stash | stash push, stash pop |
| 两个分支合并但有冲突 | merge + 解决冲突 | merge conflict, accept incoming |
| 只想把某一个 commit 搬过来 | cherry-pick | cherry-pick, 摘取提交 |
| 整理自己的提交历史 | interactive rebase | rebase -i, squash, fixup |
| 让自己的分支跟上 main | rebase | rebase onto main |
| 贡献给别人的仓库 | Fork → PR | fork, pull request, PR |
| 找人 Review 代码 | 推送 + 发起 PR | create PR, request review |
📘 结尾的话:Git 真正的学习曲线不在命令,而在心智模型。一旦你内化了「内容寻址」「DAG」「三棵树」这几个概念,剩下的所有操作 —— 无论是 stash、rebase 还是 cherry-pick —— 都不过是这些基础模型的排列组合。AI 是你的操作员,你来做架构师。