Bootstrap

git flow 相关知识 & git rebase 与 merge 合并差异比较

概要

Git Flow将branch分成2个主要分支和3个临时的辅助分支

  1. master: 永远处在即将发布(production-ready)状态

  2. develop: 最新的开发状态

  3. feature/*: 开发新功能的分支, 基于 develop, 完成后 merge 回 develop

  4. release/*: 准备要发布版本的分支, 用来修复 bug. 基于 develop, 完成后 merge 回 develop 和 master

  5. hotfix/*: 修复 master 上的问题, 等不及 release 版本就必须马上上线. 基于 master, 完成后 merge 回 master 和 develop

使用工具

git-flow是一个辅助使用git flow模式的工具。

分别体验了以下git-flow和git-flow-avh,nvie的git-flow上一次更新已经是7年前了,好多bug没有改,好多功能也没有完善。petervanderdoes的gitflow-avh的功能就相对完善一些,帮助文档也更详细一些。所以使用git-flow-avh来辅助使用git flow模式

安装

官网链接:https://github.com/petervanderdoes/gitflow-avh/wiki/Installation(好像已不维护了但不影响使用)

Mac 需要独立安装
brew install git-flow-avh
Windows 已经包含在 git.exe 安装包里了,无需额外安装

初始化

# 如果已经初始化过了,-f 可以强制重新初始化

git flow init [-f]

# 选择生产分支,开发分支及其他临时分支的前缀

Which branch should be used for bringing forth production releases?
- develop
- master
Branch name for production releases: [master]

Which branch should be used for integration of the "next release"?
- develop
Branch name for "next release" development: [develop]

How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []

开发

# 从develop分支创建并切换到功能分支feature/<name>,<base>可以指定commit,必须是develop分支上的commit
git flow feature start <name> [<base>]
# 提交开发内容
git add/commit
# 提交分支到远程
git flow feature publish <name>
# 切换到,并拉取feature分支
git flow feature track <name>
# 本地合并feature/<name>分支到develop分支,并删除feature/<name>分支
git flow feature finish -F --push <name>

# 合并后其他开发同学需要删除本地的feature分支
# 新版本会在finish后自动将本地的分支和远程的分支都清理,无需额外处理了
git flow feature delete -f <name>
# 在本地删除远程feature分支(服务器上的远程分支已经删除,但是本地git branch -r还是可以看到)
git branch -r -d origin/feature/<name>

备注

git flow feature finish -F --push <name> 完成的操作
Summary of actions:
- The feature branch 'feature/f9' was merged into 'develop'
- Feature branch 'feature/f9' has been locally deleted; it has been remotely deleted from 'origin'
- You are now on branch 'develop'
git flow feature finish 帮助
$ git flow feature finish --help
usage: git flow feature finish [-h] [-F] [-r] [-p] [-k] [-D] [-S] [--no-ff] <name|nameprefix>
Finish feature <name>

-h, --help Show this help
--showcommands Show git commands while executing them
-F, --[no]fetch Fetch from origin before performing finish
-r, --[no]rebase Rebase before merging
-p, --[no]preserve-merges
Preserve merges while rebasing
--[no]push Push to origin after performing finish
-k, --[no]keep Keep branch after performing finish
--keepremote Keep the remote branch
--keeplocal Keep the local branch
-D, --[no]force_delete
Force delete feature branch after finish
-S, --[no]squash Squash feature during merge
--no-ff Never fast-forward during the merge

上线

# 从develop分支新建release分支: release/<release>,<base>可以指定commit,必须是develop分支上的commease> [<base>]
# 提交release分支
git flow release publish <release>
# 切换到,并拉取release分支
git flow release track <release>
# 进行测试和bug修复
git add/commit/push

git rebase 和 merge 合并知识

rebase 变基的特点是切换到当前分支,然后把自己的继承链迁移到需要变基的最新分支上去,此时可能出现一些冲突,产生新的初始合并点,这样新开分支的迭代过程也可以详细地记录在合并分支上去,但是合并分支的话,则并不在

什么时候需要用 Rebase?

适合使用 Rebase 的场景:
  1. 个人开发分支:

    • 在开发一个功能时,可以使用 rebase 将自己的分支基于主分支最新状态,避免合并冲突。

    • 示例:

  2. 在推送代码之前整理提交历史:

    • 提交前使用 git rebase -i 清理不必要的提交,例如删除测试提交、实验性提交等。

  3. 协作开发时保持代码最新:

    • 和团队成员协作时,先 pull --rebase 远程分支,让自己的更改基于最新代码。

示例:

git pull --rebase origin main
  1. 清理历史记录,构建开源项目:

    • 对于需要发布的分支,Rebase 可以让整个项目的提交历史更清晰,利于用户理解和使用。

Rebase 可以将当前特性分支基于 master 最新提交,减少合并时的冲突

原因:

当你在一个 feature 分支上开发时,master 分支的代码可能已经更新(比如团队其他成员提交了新的更改)。如果你直接尝试将 feature 合并到 master,可能会遇到多个冲突。

而 rebase 是通过将你的 feature 分支重新“基于” master 的最新提交,使得你的分支看起来像是从最新的 master 开始开发的。

效果:
  • 避免历史分叉,提交历史看起来更干净。

  • 提交记录像是直接基于最新的 master 开发的,不会带来无意义的分支分叉。

  • 合并时冲突减少,因为 feature 分支已经包含了 master 的最新内容。

代码示例:

假设当前分支情况如下:

master:  A --- B --- C
feature:       D --- E --- F

此时有人在 master 提交了 G

master:  A --- B --- C --- G
feature:       D --- E --- F

如果直接合并:

git checkout master
git merge feature

结果是:

          D --- E --- F
         /             \
A --- B --- C ----------- M (merge commit)

如果先使用 rebase

git checkout feature
git rebase master

结果是:

master:  A --- B --- C --- G
feature:                          D' --- E' --- F'

然后合并到 master

git checkout master
git merge feature

历史记录看起来像是:

master:  A --- B --- C --- G --- D' --- E' --- F'

Rebase 的冲突时机

  • Rebase 的本质是 逐个提交重新应用到目标分支的最新状态上。

  • 因此,冲突可能发生在 将每个提交重新应用的过程中。

冲突场景:

假设我们有以下分支:

plaintext复制代码master:   A --- B --- C
feature1:       D --- E

在 rebase 操作中,feature1 的提交会逐一应用到 C 之后:

  1. 首先应用 D,生成 D'

  2. 然后应用 E,生成 E'

如果 D 的修改与 C 的代码有冲突,冲突会在 D 应用到 C 时发生。

Rebase 的冲突点:
  • D 合并到 C 时(生成 D')。

  • E 合并到 D' 时(生成 E')。

冲突原因:

  • D 或 E 的更改与 master 最新提交(C)之间存在代码上的矛盾。

Rebase 和 Merge 冲突的比较两者冲突的对比

特性

Rebase

Merge

冲突时机

每个提交在被重新应用时可能产生冲突。

在所有改动合并时(创建合并提交时)可能产生冲突。

冲突复杂度

可能多次出现冲突(逐一提交应用)。

只需解决一次冲突(合并整个分支时)。

冲突原因

每个提交的更改与目标分支的最新状态发生矛盾。

两个分支的所有改动之间有矛盾。

解决冲突的步骤

修复冲突后,逐一继续 Rebase。

修复冲突后完成合并提交。

适用场景

历史需要线性化,适合整理个人分支。

需要保留完整分支轨迹,适合公共分支或团队协作场景。


4. 冲突的处理方式

  • Rebase 的冲突解决更细粒度,因为它会逐一重新应用每个提交。如果提交很多且有冲突,会比较耗时。

  • Merge 的冲突解决更集中,因为所有改动会一次性合并到一起,只需解决一次冲突。


5. 为什么冲突出现的位置不同?

这是由两者的合并方式决定的:

  • Rebase 是逐步将每个提交重新应用到目标分支上,每次应用都需要检查是否与目标分支的代码冲突。

  • Merge 是直接将整个分支合并到目标分支,一次性处理所有冲突。


总结:冲突的核心差异

  • Rebase 冲突:发生在每个提交应用到目标分支的过程中,冲突粒度较小但可能多次出现。

  • Merge 冲突:发生在两个分支合并的整体过程中,冲突粒度较大但只出现一次。

两者的冲突处理并没有本质的优劣,关键是根据场景选择合适的合并策略:

  • 选择 Rebase:当需要整理历史、保持线性记录时。

  • 选择 Merge:当需要保留分支结构、避免篡改历史时。

;