Git分支管理:在平行宇宙中优雅开发
在上一期,我们学会了Git的基本操作。你可能会想:不就是存代码历史嘛,为什么要搞得这么复杂?
答案就在于分支。分支是Git最强大的特性,它允许你在不影响主线的情况下,创建一个平行的开发线,自由实验、开发新功能、修复bug。当你完成工作后,再把分支合并回去。
这就像在写小说时,你想尝试一个大胆的剧情转折。你不需要直接改原文,而是复制一份,在新文件里尽情发挥。如果写得好,就把新版本作为正式版;如果写得烂,直接删掉,原文丝毫不受影响。
分支的本质
在深入之前,先理解分支的本质。
Git的提交不是孤立的,每个提交都包含一个指针,指向上一个提交(父提交)。这样一串提交就形成了一条链。
graph LR
A[提交1] --> B[提交2] --> C[提交3] --> D[提交4]
分支本质上只是一个指向某个提交的可移动指针。默认分支叫main(以前叫master)。
HEAD是一个特殊指针,指向当前所在的分支。
graph TB
HEAD --> main
main --> D[提交4]
D --> C[提交3] --> B[提交2] --> A[提交1]
当你创建新分支时,Git只是创建了一个新指针,指向当前提交。仓库本身没有变化,快到飞起。
graph TB
HEAD --> main
main --> D[提交4]
feature --> D
D --> C[提交3] --> B[提交2] --> A[提交1]
分支基本操作
创建分支
# 创建分支(停留在当前分支)
git branch feature-login
# 创建并切换
git checkout -b feature-login
# 新语法(推荐)
git switch -c feature-login
切换分支
# 老语法
git checkout feature-login
# 新语法(推荐)
git switch feature-login
查看分支
# 查看本地分支
git branch
# 查看所有分支(包括远程)
git branch -a
# 查看分支及其最后一次提交
git branch -v
删除分支
# 删除已合并的分支
git branch -d feature-login
# 强制删除(未合并也删)
git branch -D feature-login
重命名分支
# 重命名当前分支
git branch -m new-name
# 重命名指定分支
git branch -m old-name new-name
分支工作流
Feature Branch工作流
这是最常用的分支策略。每开发一个新功能,就创建一个分支,开发完成后合并回主线。
# 1. 从main创建功能分支
git checkout main
git pull
git checkout -b feature-user-profile
# 2. 开发中...频繁提交
git add .
git commit -m "feat: 添加用户头像上传"
git add .
git commit -m "feat: 添加用户简介编辑"
# 3. 开发完成,合并回main
git checkout main
git pull # 先更新main
git merge feature-user-profile
# 4. 删除功能分支
git branch -d feature-user-profile
Git Flow工作流
更复杂的团队协作模型,定义了几种分支类型:
- main:生产环境代码,永远是稳定的
- develop:开发分支,集成各个功能
- **feature/***:功能分支,从develop分出,合并回develop
- **release/***:发布分支,从develop分出,准备发布
- **hotfix/***:紧急修复分支,从main分出,合并回main和develop
gitGraph
commit id: "init"
branch develop
checkout develop
commit id: "dev1"
branch feature-A
checkout feature-A
commit id: "feat1"
commit id: "feat2"
checkout develop
merge feature-A id: "merge-A"
checkout main
branch hotfix
checkout hotfix
commit id: "fix"
checkout main
merge hotfix id: "hotfix-merge"
checkout develop
merge hotfix
小团队不需要这么复杂,Feature Branch足够用了。
合并分支
快进合并(Fast-forward)
如果main分支在你分出feature后没有任何新提交,Git会直接把main指针移到feature的最新提交。
git checkout main
git merge feature-login
# 输出:Fast-forward
这种合并不产生新的提交节点,历史是线性的。
三方合并(Three-way merge)
如果main分支有了新提交,Git会找到两个分支的共同祖先,做一个三方合并,产生一个新的合并提交。
git checkout main
git merge feature-login
# 输出:Merge made by the 'recursive' strategy.
graph TB
subgraph 合并前
A[共同祖先] --> B[main新提交]
A --> C[feature提交]
end
graph TB
subgraph 合并后
A[共同祖先] --> B[main新提交]
A --> C[feature提交]
B --> D[合并提交]
C --> D
end
禁用快进合并
有时候你想保留分支历史,即使是快进合并也产生一个合并节点:
git merge --no-ff feature-login
这样历史会更清晰地显示"这里曾经有个分支"。
冲突解决
这是新手最害怕的部分。当两个分支修改了同一文件的同一行,Git无法自动合并,就会产生冲突。
模拟冲突:
# 分支1修改readme.md第一行
echo "Version A" > readme.md
git add readme.md
git commit -m "Version A"
# 切回main,也修改第一行
git checkout main
echo "Version B" > readme.md
git add readme.md
git commit -m "Version B"
# 尝试合并
git merge feature-xxx
# 冲突!
Git会告诉你哪些文件冲突了:
CONFLICT (content): Merge conflict in readme.md
Automatic merge failed; fix conflicts and then commit the result.
打开冲突文件,你会看到:
<<<<<<>>>>>> feature-xxx
<<<<<<>>>>>> feature-xxx:要合并的分支内容
手动解决冲突:
- 删除标记符号
- 保留正确的内容(或两者都保留)
- 保存文件
git add+git commit
# 解决后
echo "Version A and B combined" > readme.md
git add readme.md
git commit -m "merge: 解决readme冲突"
使用合并工具:
# 使用配置的合并工具(如VSCode)
git mergetool
放弃合并:
git merge --abort
变基(Rebase)
merge会产生分叉的历史,有些人喜欢线性的历史。这时候可以用rebase。
merge vs rebase:
gitGraph
commit id: "A"
commit id: "B"
branch feature
checkout feature
commit id: "C"
commit id: "D"
checkout main
commit id: "E"
用merge:
gitGraph
commit id: "A"
commit id: "B"
branch feature
checkout feature
commit id: "C"
commit id: "D"
checkout main
commit id: "E"
merge feature id: "M"
用rebase:
gitGraph
commit id: "A"
commit id: "B"
commit id: "E"
commit id: "C'"
commit id: "D'"
# 在feature分支上执行
git checkout feature
git rebase main
rebase会把feature上的提交"移到"main的最新提交之后,历史变成线性的。
黄金法则:永远不要rebase已经push到远程的提交。因为这会改写历史,给队友带来灾难。
交互式rebase:
# 修改最近3个提交
git rebase -i HEAD~3
这会打开编辑器,你可以:
pick:保留提交squash:合并到前一个提交reword:修改提交信息drop:删除提交edit:暂停以便修改这个提交
Stash:临时保存
开发到一半,突然需要切换分支处理紧急事务,但当前工作还没完成,不想commit怎么办?
# 保存当前工作
git stash
# 带描述保存
git stash save "WIP: 用户登录功能"
# 查看stash列表
git stash list
# 恢复最近的stash
git stash pop
# 恢复但不删除
git stash apply
# 恢复指定stash
git stash apply stash@{2}
# 删除stash
git stash drop stash@{0}
# 清空所有stash
git stash clear
Cherry-pick:选择性合并
只想合并某个分支的一个特定提交?用cherry-pick:
# 先在目标分支找到提交hash
git checkout feature-A
git log --oneline
# 假设要合并的提交是 a1b2c3d
# 切回main,cherry-pick
git checkout main
git cherry-pick a1b2c3d
这会把那个提交的改动复制到当前分支,产生一个新的提交。
分支最佳实践
-
分支命名规范:
feature/xxx:新功能bugfix/xxx:bug修复hotfix/xxx:紧急修复release/x.x.x:发布准备experiment/xxx:实验性功能
-
及时删除已合并分支:
# 删除本地 git branch -d feature-xxx # 删除远程 git push origin --delete feature-xxx -
保持分支短小:
- 一个分支只做一件事
- 频繁合并回主线,避免长寿命分支
-
提交前先pull:
git checkout main git pull --rebase # 用rebase避免不必要的merge提交 -
使用Pull Request / Merge Request:
不要直接在本地合并,通过PR让代码经过review再合并。
小结
你现在掌握了Git分支的核心技能:
- 理解分支的本质(可移动指针)
- 创建、切换、删除分支
- 合并分支(快进和三方合并)
- 解决合并冲突
- 使用rebase保持线性历史
- stash临时保存工作
- cherry-pick选择性合并
下一期,我们将进入团队协作。你会学到如何用Git在团队中高效工作,包括Pull Request流程、代码审查、多人协作的最佳实践。
练习任务:
- 创建一个feature分支,提交几次后合并回main
- 制造一个冲突并手动解决
- 尝试
git rebase -i合并最近2个提交 - 用stash保存当前工作,切换分支后再恢复
下期预告:《Git协作:团队开发流程》—— Pull Request怎么做?代码审查看什么?多人协作怎么避免灾难?
Views: 1
