无惧分支

发布: (2026年3月15日 GMT+8 16:27)
13 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将为您翻译成简体中文。

Git 精通系列 第3部分

← 第2部分:有意的提交(Committing with Intention) | 第4部分:不会制造混乱的协作 →

有一种开发者会避免使用分支。他们直接在 main 上工作,所有提交都在这里完成,并且每十五分钟紧张地推送一次,以确保自己的工作“安全”。当被问及为什么不使用分支时,他们会给出类似的答案:

  • “只有我一个人。”
  • “分支感觉是多余的步骤。”
  • “我总是把合并弄得一团糟。”

讽刺的是,分支正是让 Git 安全的关键。它让你可以在不触碰生产代码的情况下尝试风险较大的改动,并且可以瞬间切换上下文而不丢失位置。那些最害怕 Git 的开发者,往往恰恰是最少使用能够消除这种恐惧的功能的人。

分支让你能够做什么

在第 1 部分,你已经知道分支只是一个指针。但让我们来谈谈这在实际操作中意味着什么。

当你创建并切换到一个分支时,你正处于完全隔离的环境中。你所做的任何提交都不会影响 main。如果你决定整个实验是错误的,只需删除该分支,一切就消失了——main 从未知道它的存在。

git switch -c experiment/try-new-payment-flow
# … 编写代码,提交,测试,发现这是个坏主意 …
git switch main
git branch -D experiment/try-new-payment-flow
# 消失。main 保持不变。

思维模型: 分支是一个沙盒。对每一个非平凡的工作——新功能、修复、重构,甚至是你不确定的快速实验——都可以随意创建一个分支。成本几乎为零,安全性却是真实的。

实际可行的分支策略

关于分支策略(GitFlow、基于主干的开发、GitHub Flow)已经有整本书在讲。它们各有利弊。下面是一套适用于大多数团队的大多数情况下都能奏效的简易策略:

main          → 可直接上线的代码,始终可部署
feature/*     → 新功能,从 main 分支创建
bugfix/*      → bug 修复,从 main 分支创建
hotfix/*      → 紧急修复,从 main 分支创建,立即合并回去

就这么简单。不要再使用 develop 分支来创建第二个合并地点。除非你真的需要分阶段发布,否则不要使用 release 分支。保持结构扁平,除非有明确的理由另作安排。

命名很重要。 feature/user-login 能让人一目了然。feat-login-new 只能提供一点信息。johns-branch-v2 完全没有意义,三个月后(包括 John 在内)都会感到困惑。

# 能传达意图的命名
git switch -c feature/razorpay-integration
git switch -c bugfix/cart-total-rounding-error
git switch -c hotfix/payment-crash-on-null-address
git switch -c refactor/extract-notification-service

合并 vs 变基:别把它当成宗教

在一些开发者社区,合并与变基的争论几乎上升到了神学层面。强烈的观点、热烈的辩护以及对对立阵营的偶尔轻蔑。

实际真相: 它们是针对不同情境的不同工具,了解每个工具到底做了什么,选择自然明了。

合并

保留完整的历史记录。当你把 feature/login 合并到 main 时,Git 会创建一个拥有两个父提交的合并提交——main 的最新提交和特性分支的最新提交。历史记录准确显示了分支的创建时间和合并时间,诚实可信。

git switch main
git merge feature/login
# 创建一个拥有两个父提交的合并提交

变基

把你的提交重新播放到另一个分支之上,生成新的提交(内容相同,但父提交哈希不同)。结果看起来就像你在最新的 main 上直接编写了特性,即使在你工作期间 main 已经前进。历史变得线性且整洁——易读但略带“虚构”。

git switch feature/login
git rebase main
# 将你的提交重新播放到 main 之上

Source:

git switch main
git merge feature/login   # Now a fast‑forward, no merge commit needed

何时使用哪种方式

情境推荐工具
将已完成的功能合并到共享分支Merge(合并提交标记了功能落地的时间)
main 的最新更改更新功能分支Rebase(保持分支最新且不会出现冗余的 “Merge branch ‘main’ into …” 提交)
公共/共享分支Merge
私有、未推送的工作Rebase

这条规则可以解决约 90 % 的困惑。

快进(Fast‑Forward)及其重要性

当一个分支没有与目标分支产生分叉——即自从你创建分支后 main 没有任何新提交——Git 可以进行 快进,而不是创建合并提交。它只需把指针向前移动:

# main:   A → B → C
# feature: A → B → C → D → E

git switch main
git merge feature
# 结果: main: A → B → C → D → E(没有合并提交)

在合并前进行 rebase 能产生干净的历史,因为在 rebase 之后,你的分支始终领先于 main,没有分叉,所以合并总是快进的。

如果你想 强制生成合并提交(即使可以快进),以保留分支曾经存在的记录,可使用 --no-ff

git merge --no-ff feature/login

有些团队会对功能分支采用这种做法,这样每个功能都有可见的合并提交,使项目历史更易于浏览。

解决冲突而不慌乱

合并冲突拥有不该有的声名。它们并不危险——它们只是 Git 在告诉你:

“两个人编辑了同一个文件的同一块区域,我不知道该保留哪个版本。由你决定。”

仅此而已。Git 在提出一个问题。

当你遇到冲突时:

git merge feature/update-user-model
# CONFLICT (content): Merge conflict in src/User.php

打开 src/User.php,你会看到冲突标记:

<<<<<< HEAD
// your changes
=======
 // changes from feature/update-user-model
>>>>>>> feature/update-user-model
  • >>>>>>> 之后的部分是 你正在合并的分支

编辑文件,使其反映正确的最终状态,例如:

protected $fillable = ['name', 'email', 'phone', 'role'];

然后完成合并:

git add src/User.php
git commit   # completes the merge

减少冲突痛苦的三件事

  1. 小而频繁地合并。
    分支分叉两周的冲突会远多于分叉两天的冲突。等待的时间越长,合并就越痛苦。

  2. 使用可视化合并工具。
    git mergetool 会打开一个三窗格编辑器,显示基准、你的更改以及对方的更改。比起原始冲突标记,这种方式更容易理解。

  3. 先和对方沟通。
    解决冲突的最佳方式是先弄清楚为什么会有这两处更改,再决定保留哪一个。冲突本身就是一次对话的前奏。

删除分支:停止囤积

已合并的分支应当 删除,而不是归档。拥有 200 条陈旧分支的仓库是没人能理解的仓库。你无法分辨哪些分支是活跃的,哪些是被抛弃的,哪些已经合并——信号被噪音淹没。

# Delete local branch (after merging)
git branch -d feature/login

# Delete remote branch
git push origin --delete feature/login

# See remote branches that no longer exist locally
git remote prune origin --dry-run

大多数 Git 托管平台(GitHub、GitLab、Bitbucket)在仓库设置中提供“合并后自动删除分支”。打开它。分支应该是创建成本低、可以快速丢弃的——而不是你需要维护的集合。

改变一切的唯一分支习惯

这里有一个习惯,能把热爱 Git 的开发者和仅仅容忍 Git 的开发者区分开来:

在开始任何工作之前创建一个分支,每一次——即使是很小的改动。

  • 一个会演变成三小时调试的“快速修复”?你需要一个分支。
  • 一个涉及六个文件的“小重构”?你需要一个分支。
  • 一个可能需要回滚的“实验性改动”?你需要一个分支。

运行 git switch -c fix/whatever 的十秒钟时间,每次都值得。它在已完成、可运行的代码进行中的工作之间建立了清晰的分界。它意味着 main 始终保持可部署状态,并且如果出现更高优先级的任务,你可以干净利落地放弃当前工作。

一旦这个习惯变成自动化的动作,Git 就像安全网一样,而不是焦虑的来源。因为它本来就是这样。

快速参考

# Create and switch to a new branch
git switch -c feature/branch-name

# Switch to an existing branch
git switch branch-name

# List all branches (including remote)
git branch -a

# Merge a branch into the current one
git merge branch-name

# Rebase current branch onto another
git rebase main

# Delete a merged local branch
git branch -d branch-name

# Force‑delete an unmerged branch
git branch -D branch-name

# Delete a remote branch
git push origin --delete branch-name

# See branches with their last commit
git branch -v

# See which branches are merged into main
git branch --merged main

第 2 部分:有意的提交 | Next: 第 4 部分 — 不制造混乱的协作

如果这对你有帮助,我把整套系列做成了 23 页 PDF 参考手册 —— 检查清单、钩子模板、80 多条命令、reflog 与 bisect 深入解析,以及针对 12 种真实紧急情况的恢复手册。

Git 精通实战指南 →

0 浏览
Back to Blog

相关文章

阅读更多 »

Magit 中的变基

markdown Rebasing in Magit !Rebasing in Magit https://entropicthoughts.com/image/banner/rebasing-in-magit.jpg 我阅读了Ian Whitlock的文章《Why He Can’t Quit...》。