1. 问题场景
假设你正在开发一个新功能,预计要花两周时间。在这个过程中,代码每天都处于"半成品"状态------功能没写完,运行不起来,甚至可能把现有功能搞崩。如果你直接在主分支(master)上开发,这些不完整的提交会污染整个项目历史,还可能影响其他同事的工作。但你又需要每天保存进度,不能等两周后才一次性提交。
解决这个矛盾的答案就是分支。你可以在一个独立的分支上自由实验、随时提交,完全不影响主分支的稳定性。等功能开发完成且测试通过,再把分支上的工作合并回主分支。这就像在"平行宇宙"里工作,互不干扰,随时可以汇合。
2. 核心概念
2.1 什么是分支
在 Git 里,分支仅仅是一个指向某个提交对象的可移动指针。创建一个新分支,其实就是创建了一个新的指针,让你可以在当前工作基础上分叉出一条独立的开发线。不同分支上的修改相互隔离,直到你明确将它们合并。
2.2 HEAD 指针
HEAD 是一个特殊的指针,它始终指向你当前正在工作的分支(或者说当前分支的最新提交)。当你切换分支时,HEAD 也会跟着移动。
2.3 为什么 Git 分支如此轻量
有些老旧的版本控制系统创建分支时需要完整拷贝一份代码,非常缓慢。Git 的分支只包含一个指针和极少量的元数据,创建和切换几乎瞬间完成。这也是 Git 强烈鼓励分支策略的原因------你可以毫无心理负担地为每一个小功能、每一次 Bug 修复新建分支。
3. 核心命令
以下是分支管理最基础的一组命令,先整体列出,后面会逐个演示。
bash
# 查看所有本地分支,当前分支前会有 * 号
git branch
# 创建一个新分支(但不会切换过去)
git branch <branch_name>
# 切换到指定分支
git checkout <branch_name>
# 创建并切换到新分支(常用组合)
git checkout -b <branch_name>
# 将指定分支合并到当前所在分支
git merge <branch_name>
# 删除指定分支
git branch -d <branch_name>
4. 实战演示
继续使用之前的 myproject 仓库,或者创建一个全新的仓库来练习。假设当前只有 master 分支,上面已经有一些提交记录。
4.1 查看分支
bash
$ git branch
* master
输出显示只有一个 master 分支,前面的 * 表示当前 HEAD 指向它,也就是你正在这个分支上工作。
4.2 创建并切换到新分支
假设你要开发一个支付功能,需要创建一个专门的分支:
bash
$ git checkout -b feature-payment
Switched to a new branch 'feature-payment'
这个命令等价于 git branch feature-payment 再 git checkout feature-payment,一步到位。再查看一下分支列表:
bash
$ git branch
* feature-payment
master
当前分支已经切换到 feature-payment。
4.3 在新分支上开发和提交
现在开始"开发新功能"。往 ReadMe 文件里添加一行标记性内容并提交:
bash
$ echo "Implement payment module" >> ReadMe
$ git add ReadMe
$ git commit -m "start payment module development"
[feature-payment a2b3c4d] start payment module development
1 file changed, 1 insertion(+)
这个提交记录到了 feature-payment 分支上,对 master 分支毫无影响。
4.4 切换回 master 分支
现在回到 master 分支看看:
bash
$ git checkout master
Switched to branch 'master'
查看 ReadMe 文件的内容:
bash
$ cat ReadMe
# 没有 "Implement payment module" 这一行
新功能分支上的那行内容消失了,因为 master 分支的指针还停留在原来的提交点,没有包含支付分支上的修改。此时 git log 也看不到那条 start payment module development 的提交,除非你显式查看 feature-payment 分支的日志。
4.5 合并分支
支付功能已经开发完成,需要把它合并回主分支。确保你在 master 分支上,然后执行合并:
bash
$ git merge feature-payment
Updating 14c12c3..a2b3c4d
Fast-forward
ReadMe | 1 +
1 file changed, 1 insertion(+)
Git 提示采用了 Fast-forward 模式。这是因为 master 分支在分离之后没有产生新的提交,Git 只需要将 master 的指针直接"快进"到 feature-payment 指向的提交即可,无需额外的合并提交。合并后 master 分支就包含了支付功能的代码,ReadMe 文件里那行内容也回来了。
4.6 删除分支
功能已经合并,feature-payment 分支不再需要了,可以删掉以保持分支列表的整洁:
bash
$ git branch -d feature-payment
Deleted branch feature-payment (was a2b3c4d).
此时 git branch 只会显示 master。注意,被删除的分支只是在本地被移除,如果已经推送到远程仓库,还需要额外的步骤删除远程分支(后面文章会讲)。
5. 分支策略建议
在实际项目中,直接在主分支(master)上开发是大忌。一个常用的策略是:
- master 分支:始终保持稳定,只用来发布正式版本。任何人不直接在 master 上提交。
- dev 分支:开发集成分支,日常开发的工作都在这个分支或其衍生分支上进行。
- 个人功能分支:每个新功能、每个 Bug 修复,都从 dev 分支上拉出一个独立的分支。开发完成后合并回 dev,再删除该功能分支。
这种层级结构让项目历史清晰可追溯,也最大程度降低了多人协作时的冲突概率。
6. 注意事项
- 分支的创建和切换非常轻量,不要怕多建分支。即使是一个极小的改动,也可以单独建分支,这样如果发现方向不对,直接删掉分支即可,主分支完全不受影响。
- 在合并分支之前,建议先用
git status确认当前处在正确的目标分支(通常是 master 或 dev)。很多人会把方向搞反,把主分支合并到了功能分支上。 - 如果删除分支时 Git 提示 "the branch is not fully merged",说明该分支上还有没有被合并到其他分支的提交。Git 是在保护你,防止不小心丢失工作成果。如果确认那些提交确实没用了,可以用
git branch -D <branch_name>强制删除。
7. 要点总结
- 分支是 Git 的核心功能,让你在隔离环境中自由开发,不影响主线。
git checkout -b <name>创建并切换分支,是最常用的操作之一。git merge将分支上的工作合并到当前分支,默认采用快进模式。- 合并完成且确认不再需要后,应及时用
git branch -d删除分支,保持仓库整洁。 - 养成良好的分支习惯:为每个独立功能或修复都新建一个分支。
8. 练习题
一定要练习,玩起来,你可以随便玩,不需要局限于我举得例子,你想怎么搞就怎么搞,不要害怕搞坏了,只要你不用自己的真实项目,其他的,大不了把git删了重新下,搞起来,只有这样才学的扎实
- 在你的
learning-git仓库中,创建一个feature-login分支并切换过去。 - 在该分支上新建一个
login.py文件,写入一些模拟代码,然后add和commit。 - 切换回 master 分支,确认
login.py不存在。然后用git merge feature-login将其合并进来。 - 合并成功后,删除
feature-login分支。 - 尝试创建一个新分支
temp并做一次提交,然后不合并就直接用git branch -d temp删除它,观察 Git 的警告信息。再用git branch -D temp强制删除。 - (思考题)如果 master 分支在
feature-login创建之后又有了新的提交,合并时还会是快进模式吗?试着模拟并观察结果。