什么是分支
分支(Branch)是 Git 中最强大的概念之一。很多人把分支想象成代码的"副本",但这是误解。
Git 中的分支本质上只是一个指针——指向某个 commit 的可移动引用。它实际上是一个只有 41 字节(40字符哈希 + 换行符)的文本文件,存放在 .git/refs/heads/ 目录中。
这意味着:创建一个分支的成本是 O(1),不论仓库有多大历史、多少文件,创建分支都是瞬间完成的。
└── E ── F ◄── feature/login
上图中,main 和 feature/login 都只是指向不同 commit 的指针。C 和 D 在 main 上,E 和 F 只在 feature/login 上,但 A、B 两个 commit 被两条分支共享——没有数据重复,只是指针不同。
HEAD 是什么
HEAD 是一个特殊的指针,始终指向你当前所在的分支(或者当前检出的某个 commit)。
# .git/HEAD 文件的内容通常是:
ref: refs/heads/main
# 当你 switch 到 feature 分支后,它变成:
ref: refs/heads/feature/login
当你运行 git log 看到 HEAD -> main 时,意思是:
HEAD指向main分支main分支指向某个 commit- 你目前在
main分支上工作
Detached HEAD(游离 HEAD):当你直接 checkout 到某个 commit 的哈希时,HEAD 不再指向任何分支,而是直接指向那个 commit。此时如果创建新 commit,它不属于任何分支,很容易丢失。应立即创建一个分支:git switch -c new-branch。
分支操作命令
查看分支
# 列出所有本地分支(当前分支前有 *)
git branch
# 列出所有分支(包括远程追踪分支)
git branch -a
# 列出远程分支
git branch -r
# 显示每个分支的最新提交
git branch -v
创建分支
# 仅创建分支,不切换
git branch feature-login
# 基于特定 commit 创建分支
git branch hotfix-v1.2 abc1234
切换分支
# 旧语法(仍然有效)
git checkout feature-login
# 新语法(Git 2.23+,推荐)— 语义更清晰
git switch feature-login
# 创建并切换(新语法,推荐)
git switch -c feature-payment
# 等同于上面的旧语法
git checkout -b feature-payment
# 切换回上一个分支
git switch -
为什么推荐 git switch?git checkout 功能太多(切换分支、恢复文件、检出特定 commit),语义模糊。Git 2.23 引入的 git switch(切换分支)和 git restore(恢复文件)语义更清晰。
删除分支
# 删除已合并的分支(安全删除,如果未合并会报错)
git branch -d feature-login
# 强制删除(不管是否已合并,危险!)
git branch -D feature-experiment
# 删除远程分支
git push origin --delete feature-login
合并分支
合并(Merge)是将两条分支的历史整合在一起。在 main 上执行 git merge feature-login 意味着:把 feature-login 的变更整合进 main。
# 先切换到目标分支(接收变更的分支)
git switch main
# 将 feature-login 合并进来
git merge feature-login
# 合并时强制创建 merge commit(--no-ff)
git merge --no-ff feature-login -m "Merge feature/login into main"
Fast-forward 合并
当目标分支(main)是源分支(feature)的直接祖先时,Git 默认进行 fast-forward 合并。此时不创建新的 merge commit,只是简单地移动 main 指针。
main ──▶ A ── B ── C
└── D ── E ◄── feature
合并后(fast-forward,main 指针移动到 E):
A ── B ── C ── D ── E ◄── main ◄── feature
优点:历史保持线性,干净整洁。缺点:看不出哪些 commit 是在哪个 feature 上做的。
三方合并(Three-way Merge)
当两条分支都有各自的新 commit 时,Git 需要找到它们的共同祖先(merge base),进行三方比较,然后创建一个新的 merge commit。
main: A ── B ── C ── D ◄── main
└── E ── F ◄── feature
合并后(创建 merge commit M):
A ── B ── C ── D ────────── M ◄── main
└── E ── F ──────┘
merge commit M 有两个父 commit(D 和 F),完整保留了两条分支的开发历史。
冲突(Conflict)
当两条分支都修改了同一个文件的同一部分时,Git 无法自动决定用哪个版本,就会产生冲突。
冲突标记
Git 会在冲突文件中插入标记:
<<<<<<< HEAD
function greet(name) {
return `Hello, ${name}!`;
}
=======
function greet(user) {
return `Hi there, ${user}! Welcome back.`;
}
>>>>>>> feature/greeting-update
- <<<<<<< HEAD从这里到
=======是你当前分支(HEAD)的内容 - =======分隔线,上面是当前分支,下面是被合并进来的分支
- >>>>>>> branch从
=======到这里是被合并分支的内容
解决冲突的步骤
# 1. 查看哪些文件有冲突
git status
# 2. 打开冲突文件,手动编辑,删除冲突标记,保留正确内容
# (选择其中一个,或者合并两者)
# 3. 标记冲突已解决(重新 add)
git add src/utils.js
# 4. 完成合并
git commit # 会自动生成 merge commit 信息
# 或者,如果不想继续合并,可以放弃
git merge --abort
使用图形化工具解决冲突:VS Code 内置了优秀的冲突解决 UI,点击 "Accept Current Change" / "Accept Incoming Change" / "Accept Both Changes" 即可。也可配置 git config --global merge.tool vscode,然后 git mergetool 打开。
分支命名规范
良好的分支命名让团队一目了然地知道每个分支的用途:
| 分支类型 | 命名格式 | 示例 | 说明 |
|---|---|---|---|
| 主分支 | main / master | main | 生产就绪代码,永久存在 |
| 开发主线 | develop | develop | Git Flow 中的开发集成分支 |
| 功能分支 | feature/xxx | feature/user-auth | 新功能开发,完成后合并删除 |
| Bug 修复 | bugfix/xxx | bugfix/login-error | 修复非紧急 bug |
| 热修复 | hotfix/xxx | hotfix/security-patch | 紧急生产 bug 修复 |
| 发布分支 | release/x.x.x | release/2.1.0 | 发布准备,只做小修改 |
查看分支图
这个命令组合是日常工作的必备,强烈建议为其设置别名:
# 图形化、单行、显示所有分支
git log --oneline --graph --all
# 设置别名(推荐!)
git config --global alias.lg "log --oneline --graph --all --decorate"
# 之后只需要输入
git lg