什么时候建议使用git rebase
与git merge
?
成功改造后我还需要合并吗?
#1楼
在合并/ rebase之前:
A <- B <- C [master]
^
\
D <- E [branch]
在git merge master
:
A <- B <- C
^ ^
\ \
D <- E <- F
在git rebase master
:
A <- B <- C <- D' <- E'
(A,B,C,D,E和F是提交)
关于git的这个例子和更好的插图信息可以在这里找到: http : //excess.org/article/2008/07/ogre-git-tutorial/
#2楼
pro git book是对rebasing页面的一个非常好的解释。
基本上合并将需要2次提交并将它们组合在一起。
一个rebase将转到2上的共同祖先,并逐步将更改应用于彼此之上。 这使得“更清洁”和更线性的历史。
但是当你重新放弃以前放弃之前的提交并创建新的提交时。 所以你永远不应该改变一个公开的回购。 处理回购的其他人会讨厌你。
仅仅因为这个原因,我几乎完全合并。 99%的时间我的分支没有那么大的差异,所以如果有冲突,它只在一两个地方。
#3楼
Git rebase用于使历史记录中的分支路径更清晰,存储库结构是线性的。
它还用于保持您创建的分支是私有的,因为在重新定位并将更改推送到服务器之后,如果删除分支,则不会有您已经处理过的分支的证据。 所以你的分公司现在是你当地的关注点。
在做了rebase之后我们也摆脱了额外的提交,我们常常看看我们是否正常合并。
然而,一个成功的rebase之后仍然需要进行合并,因为rebase命令只是将你的工作放在你在rebase中提到的分支的顶部说master并且使你的分支的第一次提交作为master分支的直接后代。 这意味着我们现在可以进行快速合并以将更改从此分支转移到主分支。
#4楼
这里有很多答案说合并将你所有的提交变为一个,因此建议使用rebase来保存你的提交。 这是不正确的。 如果你已经提交了你的提交,这是一个坏主意 。
合并未泯你的提交。 合并保留了历史! (只看gitk)Rebase重写了历史,这是你推动它之后的坏事。
使用合并 -只要你已经推动,就不要使用rebase 。
这是Linus'(git的作者)对它的看法 。 这是一个非常好的阅读。 或者您可以在下面阅读我自己的相同想法版本。
在master上重新分支:
- 提供了如何创建提交的错误想法
- 使用一堆可能未经过良好测试的中间提交来污染master
- 实际上可能会在这些中间提交中引入构建中断,因为在创建原始主题分支和重新分配时之间进行了更改。
- 在高手找到好位置难以结账。
- 导致提交时间戳与树中的时间顺序不一致。 所以你会看到提交A在master中提交B之前,但是提交B首先被创作。 (什么?!)
- 产生更多冲突是因为主题分支中的各个提交都可能涉及必须单独解决的合并冲突(进一步包含在每个提交中发生的事件的历史记录中)。
- 是历史的重写。 如果被重新分支的分支已被推送到任何地方(与除您之外的任何人共享),那么您已经搞砸了自从您重写历史以来拥有该分支的所有其他人。
相反,将主题分支合并为master:
- 保留创建主题分支的历史记录,包括从主分支到主题分支的任何合并,以帮助保持最新。 您真正了解开发人员在构建时使用的代码。
- master是一个主要由合并组成的分支,每个合并提交通常都是历史上可以安全检查的“好点”,因为这是主题分支准备集成的地方。
- 保留主题分支的所有单独提交,包括它们位于主题分支中的事实,因此隔离这些更改是很自然的,您可以在需要的位置钻取。
- 合并冲突只需要解析一次(在合并时),因此不必单独解决在主题分支中进行的中间提交更改。
- 可以顺利完成多次。 如果您定期将主题分支集成到master,那么人们可以继续构建主题分支,并且可以继续独立合并。
#5楼
这句话得到了:
一般来说,充分利用这两个世界的方法是重新定义你所做的本地更改,但是在推送它们之前还没有共享以便清理你的故事,但是从不改变任何你推到某个地方的东西。
资料来源: http : //www.git-scm.com/book/en/v2/Git-Branching-Rebasing#Rebase-vs.-Merge
#6楼
一些实际的例子,与大规模开发有些联系,其中gerrit用于审查和交付集成。
当我将我的功能分支提升到一个新的远程主控时,我合并。 这提供了最小的提升工作,并且很容易跟踪功能开发的历史,例如gitk。
git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature
我在准备交付提交时合并。
git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master
当我的交付提交由于某种原因导致集成失败时,我会重新定义,我需要将其更新为新的远程主机。
git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
#7楼
TL; DR
如果您有任何疑问,请使用合并。
简答
rebase和merge之间的唯一区别是:
- 生成的历史树结构(通常只在查看提交图时才会显着)是不同的(一个将具有分支,另一个将不具有)。
- 合并通常会创建一个额外的提交(例如树中的节点)。
- 合并和rebase将以不同方式处理冲突。 Rebase将一次提交一个提交的冲突,其中merge将同时呈现它们。
所以简短的回答是根据您希望历史记录的样子选择rebase或merge 。
答案很长
在选择要使用的操作时,您应该考虑几个因素。
分支机构是否与团队外部的其他开发人员共享变更(例如开源,公共)?
如果是这样,请不要改变。 Rebase会破坏分支,这些开发人员会破坏/不一致的存储库,除非他们使用git pull --rebase
。 这是快速打乱其他开发人员的好方法。
您的开发团队技术娴熟吗?
Rebase是一种破坏性的操作。 这意味着,如果您没有正确应用它, 您可能会失去承诺的工作和/或破坏其他开发人员的存储库的一致性。
我曾经在团队中工作,开发人员都来自公司能够负担得起专职人员来处理分支和合并的时候。 那些开发人员对Git了解不多,也不想了解太多。 在这些团队中,我不会冒任何理由推荐变基。
分支本身是否代表有用的信息
一些团队使用每个功能分支模型,其中每个分支代表一个功能(或错误修复或子功能等)。在此模型中,分支有助于识别相关提交的集合。 例如,可以通过恢复该分支的合并来快速恢复特征(公平地说,这是一种罕见的操作)。 或者通过比较两个分支(更常见)来区分特征。 Rebase将破坏分支,这不会是直截了当的。
我还参与了使用每个开发者分支模型的团队(我们都在那里)。 在这种情况下,分支本身不传达任何其他信息(提交已经有作者)。 变基不会有任何伤害。
您是否想以任何理由还原合并?
与恢复合并相比,恢复(如在撤消中)相当困难和/或不可能(如果rebase存在冲突)。 如果您认为有可能需要恢复,请使用合并。
你在团队工作吗? 如果是这样,你愿意在这个分支上采取全有或全无的方法吗?
需要使用相应的git pull --rebase
来提取Rebase操作。 如果您自己工作,您可能能够记住在适当的时候应该使用哪些。 如果您在团队中工作,那么协调将非常困难。 这就是为什么大多数rebase工作流建议使用rebase进行所有合并(以及git pull --rebase
for all pulls)。
常见的神话
合并破坏历史(南瓜提交)
假设您有以下合并:
B -- C
/ \
A--------D
有些人会说合并“破坏”提交历史记录,因为如果你只查看主分支(A - D)的日志,你会错过B和C中包含的重要提交消息。
如果这是真的,我们就不会有这样的问题 。 基本上,你会看到B和C,除非你明确要求不要看到它们(使用--first-parent)。 这很容易为自己尝试。
Rebase允许更安全/更简单的合并
这两种方法的合并方式不同,但不清楚一种方法总是优于另一方,它可能取决于开发人员的工作流程。 例如,如果开发人员倾向于定期提交(例如,他们可能每天提交两次,因为他们从工作转移到家庭),那么对于给定的分支可能会有很多提交。 许多提交可能看起来不像最终产品(我倾向于每个功能重构一次或两次我的方法)。 如果其他人正在处理相关的代码区域并且他们试图改变我的更改,那么这可能是一项相当繁琐的操作。
Rebase更酷/更性感/更专业
如果你想将rm
为rm -rf
以“节省时间”,那么也许rebase适合你。
我的两分钱
我总是认为有一天我会遇到一个场景,git rebase是解决问题的绝佳工具。 就像我想我会遇到一个场景,git reflog是一个很好的工具,可以解决我的问题。 我已经和git合作了五年多了。 它没有发生。
凌乱的历史对我来说从未真正成为问题。 我永远不会像一部令人兴奋的小说那样阅读我的承诺历史。 大多数时候我需要一个历史,我将使用git blame或git bisect。 在这种情况下,合并提交对我来说实际上是有用的,因为如果合并引入了对我来说有意义的信息的问题。
更新(4/2017)
尽管我的一般建议仍然存在,但我觉得有义务提到我个人已经软化了使用rebase。 我最近与Angular 2 Material项目进行了很多交互。 他们使用rebase来保持非常干净的提交历史。 这使我能够非常轻松地查看修复给定缺陷的提交内容以及该提交是否包含在发布中。 它是正确使用rebase的一个很好的例子。
#8楼
这个答案广泛针对Git Flow 。 这些表是使用漂亮的ASCII表生成器生成的 ,并且历史树具有这个奇妙的命令( 别名为git lg
):
git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'
表格按时间倒序排列,与历史树木更加一致。 另请参阅git merge
和git merge --no-ff
之间的区别git merge --no-ff
首先是git merge --no-ff
(您通常希望使用git merge --no-ff
因为它会使您的历史更贴近现实):
git merge
命令:
Time Branch "develop" Branch "features/foo"
------- ------------------------------ -------------------------------
15:04 git merge features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
结果:
* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
| Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
| Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge --no-ff
命令:
Time Branch "develop" Branch "features/foo"
------- -------------------------------- -------------------------------
15:04 git merge --no-ff features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
结果:
* 1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/ Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge
vs git rebase
第一点: 始终将功能合并到开发中,从不根据功能进行重新开发 。 这是重新黄金法则的结果 :
git rebase
的黄金法则是永远不要在公共分支上使用它。
换句话说 :
永远不要贬低你推到某个地方的东西。
我个人补充说: 除非它是一个功能分支,否则你和你的团队都会意识到后果 。
所以git merge
vs git rebase
的问题几乎只适用于特征分支(在下面的例子中,在合并时总是使用--no-ff
)。 请注意,由于我不确定是否有一个更好的解决方案( 存在争议 ),我将仅提供两个命令的行为方式。 在我的情况下,我更喜欢使用git rebase
因为它产生了一个更好的历史树:)
功能分支之间
git merge
命令:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
结果:
* c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\ Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | | Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | | Fourth commit - Christophe
* | | 98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \ Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
命令:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git rebase features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
结果:
* 7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
从develop
到功能分支
git merge
命令:
Time Branch “develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m “Sixth commit"
15:08 git merge --no-ff development
15:07 git merge --no-ff features/foo
15:06 git commit -m “Fifth commit"
15:05 git commit -m “Fourth commit"
15:04 git commit -m “Third commit"
15:03 git commit -m “Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m “First commit"
结果:
* 9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\ Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* | 5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | | Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ / Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
命令:
Time Branch “develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m “Sixth commit"
15:08 git rebase development
15:07 git merge --no-ff features/foo
15:06 git commit -m “Fifth commit"
15:05 git commit -m “Fourth commit"
15:04 git commit -m “Third commit"
15:03 git commit -m “Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m “First commit"
结果:
* b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
旁注
git cherry-pick
当你只需要一个特定的提交时, git cherry-pick
是一个很好的解决方案( -x
选项会在原始提交消息体中附加一行“ (从提交中挑选出来的樱桃) ” ) ,所以通常是一个好主意使用它 - git log <commit_sha1>
来查看它:
命令:
Time Branch “develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m “Sixth commit"
15:07 git cherry-pick -x <second_commit_sha1>
15:06 git commit -m “Fifth commit"
15:05 git commit -m “Fourth commit"
15:04 git commit -m “Third commit"
15:03 git commit -m “Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m “First commit"
结果:
* 50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| | Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git pull --rebase
不确定我能否比Derek Gourlay更好地解释...基本上,使用git pull --rebase
而不是git pull
:)文章中缺少的是, 你可以默认启用它 :
git config --global pull.rebase true
git rerere
再次, 在这里很好地解释。 但简单来说,如果启用它,则不必再多次解决相同的冲突。
#9楼
虽然合并绝对是集成更改的最简单和最常用的方式,但它并不是唯一的方法: Rebase是一种替代的集成方式。
理解合并更好一点
当Git执行合并时,它会查找三个提交:
- (1)共同祖先提交如果你遵循项目中两个分支的历史记录,它们总是至少有一个共同的提交:在这个时间点,两个分支具有相同的内容,然后进化不同。
- (2)+(3)每个分支的端点集成的目标是组合两个分支的当前状态。 因此,他们各自的最新修订是特别感兴趣的。 结合这三个提交将导致我们的目标集成。
快进或合并提交
在非常简单的情况下,自分支发生以来,两个分支中的一个没有任何新的提交 - 它的最新提交仍然是共同的祖先。
在这种情况下,执行集成非常简单:Git可以在共同的祖先提交之上添加另一个分支的所有提交。 在Git中,这种最简单的集成形式称为“快进”合并。 然后两个分支共享完全相同的历史。
要进行集成,Git必须创建一个包含它们之间差异的新提交 - 合并提交。
人类提交和合并提交
通常,提交是由人类仔细创建的。 它是一个有意义的单元,仅包含相关更改并使用注释对其进行注释。
合并提交有点不同:它不是由开发人员创建的,而是由Git自动创建的。 而不是包装一组相关的更改,其目的是连接两个分支,就像一个结。 如果您想稍后了解合并操作,则需要查看两个分支的历史记录和相应的提交图。
与Rebase集成
有些人喜欢没有这样的自动合并提交。 相反,他们希望项目的历史看起来好像是在一条直线上演变而来。 没有任何迹象表明它在某些时候被分成了多个分支。
让我们一步一步地完成一个rebase操作。 该场景与前面的示例相同:我们希望将branch-B中的更改集成到branch-A中,但现在使用rebase。
我们将分三步完成
-
git rebase branch-A // syncs the history with branch-A
-
git checkout branch-A // change the current branch to branch-A
-
git merge branch-B // merge/take the changes from branch-B to branch-A
首先,Git将“撤消”在行开始分支之后发生的分支-A上的所有提交(在共同的祖先提交之后)。 但是,当然,它不会丢弃它们:相反,您可以将这些提交视为“暂时保存”。
接下来,它应用我们想要集成的branch-B的提交。 此时,两个分支看起来完全相同。
在最后一步中,现在重新应用分支-A上的新提交 - 但是在新的位置上,在分支-B的集成提交之上(它们是重新基础的)。 结果看起来发展是直线发生的。 代替包含所有组合更改的合并提交,保留了原始提交结构。
最后,你得到一个干净的分支-A ,没有不需要的和自动生成的提交。
注意:取自git-tower
的精彩帖子 。 在同一篇文章中, rebase
的缺点也很好。
#10楼
我什么时候使用git rebase
? 几乎从来没有,因为它改写了历史。 git merge
几乎总是首选,因为它尊重项目中实际发生的事情。
#11楼
有人多次解释什么是rebase和什么合并,但何时使用什么?
什么时候使用rebase?
- 当你没有推动分支/没有其他人正在努力
- 你想要完整的历史
- 你想避免所有自动生成的“合并..”提交消息
随着git rebase改变了历史。 因此,当其他人在同一分支上工作时,您不应该使用它/如果您已经推动它。 但是如果你有一个本地分支,你可以在将你的分支合并回master之前做一个合并rebase master,以保持更清晰的历史记录。 这样做,在合并到主分支之后,在主分支中使用分支将不可见 - 历史是“更干净”,因为您没有自动生成的“合并...”但仍然具有完整主分支中的历史记录没有自动生成的“merged ..”提交。 但请确保您使用git merge feature-branch --ff-only
来确保在将功能合并回main时没有冲突创建单个提交。 如果您在获取功能分支的历史记录时使用功能分支用于处理的每个任务,而不是“合并...”提交,则这很有趣
第二种情况是,如果您从分支分支并想知道主分支中的更改。 rebase为您提供信息,因为它包含每一次提交。
何时使用合并?
- 当你推动分支/其他人正在努力
- 你不需要完整的历史
- 简单的合并对你来说已经足够了
如果您不需要或想要在主分支中拥有功能分支的所有历史记录,或者其他人正在同一分支上工作/您已将其推送。 如果您仍想拥有历史记录,只需将master合并到功能分支,然后再将功能分支合并到master中。 这将导致快进合并,您在主服务器中具有功能分支的历史记录(包括功能分支中的合并提交,因为您将主服务器合并到其中)。
#12楼
我们来看一个例子。 在基于master
分支的名为login
的分支上工作时,您的一个团队成员将一些更改推送到master
。 您需要这些更改才能完成分支中的login
功能。
图1.在master
分支(E和F)中的新提交是完成login
分支中的工作所必需的。
将 master
分支合并回您的分支将导致合并提交,其中包括两个分支之间的更改,并且存在以显示合并发生的位置。
我们将来不需要知道何时将master
合并到login
分支中。 相反,我们想假装login
分支上的所有提交都是基于master
分支的新状态发生的。
Git的rebase命令暂时撤销当前分支上的提交,从另一个分支中提取提交并重新应用重新提交的提交。 通过切换电流这将当前分支基于另一个分支。
图3. Rebasing应用来自master
分支顶部的login
分支的提交。
来源就在这里
#13楼
精简版
- Merge在一个分支中获取所有更改,并在一次提交中将它们合并到另一个分支中。
- Rebase说我想要分支的点移动到一个新的起点
所以你什么时候使用其中任何一个?
合并
- 假设您已经创建了一个分支,用于开发单个功能。 当您想要将这些更改带回master时,您可能需要合并 (您不关心维护所有临时提交)。
变基
- 第二种情况是,如果您开始进行一些开发,然后另一位开发人员进行了无关的更改。 你可能想拉,然后变基到从回购目前的版本为基础修改。
#14楼
在合并之前,一个rebase通常是一个好主意,因为我们的想法是你在你的分支
Y
集成你将合并的分支B
的工作。
但同样,在合并之前,你解决了你的分支中的任何冲突(即:“rebase”,如“从分支B
的最近点开始重放我在我的分支中的工作”)
如果正确完成,从分支到分支B
的后续合并可以快速进行。合并直接影响到目标分支
B
,这意味着合并更好是微不足道的,否则分支B
可以很长时间恢复到稳定状态(解决所有冲突的时间)
在一次变革之后合并的重点是什么?
在我描述的情况下,我将B
绑定到我的分支上,只是为了有机会从B
最近点重播我的工作,但是留在我的分支中。
在这种情况下,仍需要合并才能将我的“重播”作品带到B
。
另一种情况(例如在Git Ready中描述 )是通过rebase将您的工作直接带入B
(这确实可以保存您所有的好提交,甚至可以让您通过交互式rebase重新排序它们)。
在那种情况下(你在B分支中进行重组),你是对的:不需要进一步的合并:
我们没有合并也没有重新定义的默认git树
我们通过变基来获得:
第二种情况是关于:如何将新功能重新用于主服务器。
我的观点是,通过描述第一个rebase场景,提醒大家一个rebase也可以作为一个初步步骤(即“让新功能重新成为主人”)。
您可以使用rebase首先将主要“引入”新功能分支:rebase将重播来自HEAD master
新功能提交,但仍然在新功能分支中,有效地从旧主提交中移动分支起点到HEAD-master
。
这允许您解决您的分支任何冲突(意思,隔离,同时允许主继续同步发展,如果你的解决冲突的阶段时间过长)。
然后,你可以切换到主和合并new-feature
(或重订new-feature
到master
,如果你想保留你的提交完成new-feature
的分支)。
所以:
- “rebase vs. merge”可以被视为导入作品的两种方式,比如
master
。 - 但是“rebase then merge”可以是一个有效的工作流程,可以首先独立解决冲突,然后恢复你的工作。
#15楼
这很简单,你说使用另一个分支作为你工作的新基础 。
如果你有一个分支master
并且你创建了一个分支来实现一个新功能,那么你可以说它名为cool-feature
,当然主分支是你新功能的基础。
现在,您希望添加在master
分支中实现的新功能。 你可以切换到master
并合并cool-feature
分支:
$ git checkout master
$ git merge cool-feature
但这样一个新的虚拟提交加入,如果你想避免意大利面条的历史,你可以变基 :
$ git checkout cool-feature
$ git rebase master
然后将其合并到master
:
$ git checkout master
$ git merge cool-feature
这一次,由于主题分支具有相同的master提交以及具有新功能的提交,因此合并将只是一个快进。
#16楼
合并意味着:创建一个新的提交,将我的更改合并到目标中。
Rebase意味着:使用我当前的提交集作为提示创建一系列全新的提交。 换句话说,如果我已经开始从我重新定位的角度开始制作它们,那么计算我的变化会是什么样子。 因此,在rebase之后,您可能需要重新测试您的更改,并且在rebase期间,您可能会遇到一些冲突。
鉴于此,你为什么要变基? 只是为了保持发展历史的清晰。 假设您正在使用功能X,当您完成后,您将合并您的更改。目标将现在有一个提交,可以说“添加功能X”。 现在,如果您重新定位然后合并,则目标开发历史记录将包含单个逻辑进程中的所有单个提交,而不是合并。 这使得稍后审查更改变得更加容易。 想象一下,如果50位开发人员一直在合并各种功能,你会发现它有多难以回顾开发历史。
也就是说,如果你已经推动了你正在上游工作的分支机构,那么你不应该改变,而应该合并。 对于尚未被推送到上游的分支,rebase,test和merge。
你可能想要重新定义的另一个时间是你想要在推送上游之前从你的分支中删除提交。 例如:承诺尽早引入一些调试代码,并进一步提交清除代码的其他提交。 执行此操作的唯一方法是执行交互式rebase: git rebase -i <branch/commit/tag>
更新:当您使用Git连接到不支持非线性历史记录的版本控制系统(例如subversion)时,您还想使用rebase。 使用git-svn桥时,非常重要的是,您合并回subversion的更改是在trunk中最新更改之上的连续更改列表。 只有两种方法可以做到这一点:(1)手动重新创建更改和(2)使用rebase命令,这要快得多。
UPDATE2:另一种思考rebase的方法是,它允许从您的开发样式到您承诺的存储库中接受的样式的一种映射。 假设你喜欢小而小的块。 你有一个提交修复错误,一个提交摆脱未使用的代码,等等。 当你完成了你需要做的事情时,你需要进行一系列的提交。 现在让我们说你承诺鼓励大量提交的存储库,所以对于你正在做的工作,人们会期望一个或两个提交。 你如何处理你的提交并将它们压缩到预期的位置? 您将使用交互式rebase并将您的小提交压缩为更少的更大块。 如果需要反向,情况也是如此 - 如果你的风格是一些大型提交,但是repo需要长串的小提交。 您也可以使用rebase来做到这一点。 如果您已合并,则现在已将提交样式移植到主存储库中。 如果有很多开发人员,你可以想象在一段时间之后跟踪具有几种不同提交样式的历史记录是多么困难。
更新3: Does one still need to merge after a successful rebase?
是的你是。 原因在于,基础改变主要涉及提交的“转移”。 正如我上面所说,这些提交是计算出来的,但是如果从分支开始就有14次提交,那么假设你的rebase没有出错,那么你将会提前14次提交(在你重新调整之后)。 rebase已经完成。 在rebase之前你有一个分支。 之后你会有一个相同长度的分支。 在发布更改之前,您仍需要合并。 换句话说,可以根据需要多次使用rebase(同样,只有在没有将更改推送到上游时)。 只有在你变基之后合并。