建议何时使用Git rebase与Git merge?
成功重新创建数据库后,是否仍需要合并?
建议何时使用Git rebase与Git merge?
成功重新创建数据库后,是否仍需要合并?
当前回答
这里的很多答案都说,合并会将所有提交转换为一个,因此建议使用rebase来保存提交。这是不正确的。如果你已经提交了,那是个坏主意。
合并不会消除提交。合并保留历史!(看看gitk)热巴改写了历史,这是你推后的坏事。
使用merge,而不是在已经推送的情况下重新设置基址。
这是Linus(Git的作者)对它的看法(现在托管在我自己的博客上,由Wayback Machine恢复)。这真是一本好书。
或者你可以在下面阅读我自己的版本。
在主节点上重新分配分支:
提供了如何创建提交的错误想法用一堆可能没有经过良好测试的中间提交来污染master实际上可能会在这些中间提交上引入构建中断,因为在创建原始主题分支和重新创建主题分支之间对master进行了更改。这使得在master中找到好地方结账变得困难。导致提交的时间戳与其在树中的时间顺序不一致。因此,您将看到提交A在主提交B之前,但提交B是先编写的。(什么?!)产生更多的冲突,因为主题分支中的每个提交都可能涉及合并冲突,这些冲突必须单独解决(关于每个提交中发生的情况,请进一步查看历史)。是对历史的改写。如果正在重新创建的分支被推到任何地方(与除您之外的任何人共享),那么自您改写历史以来,您已经将拥有该分支的所有其他人都搞砸了。
相反,将主题分支合并为主分支:
保留主题分支创建位置的历史记录,包括从主分支到主题分支的任何合并,以帮助保持其最新状态。您可以准确地了解开发人员在构建时使用的代码。master是一个主要由合并组成的分支,而这些合并提交中的每一个都是历史上可以安全检出的“好点”,因为这就是主题分支可以集成的地方。主题分支的所有单独提交都会被保留,包括它们位于主题分支中的事实,因此隔离这些更改是很自然的,您可以在需要时钻取。合并冲突只需解决一次(在合并时),因此在主题分支中进行的中间提交更改不必单独解决。可以平滑地进行多次。如果您定期将主题分支集成到master,人们可以继续在主题分支上进行构建,并且可以继续独立地进行合并。
其他回答
这个答案广泛围绕GitFlow。这些表是用漂亮的ASCII表生成器生成的,历史树是用这个奇妙的命令(别名为gitlg)生成的:
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,因为它使您的历史看起来更接近现实):
合并分支
命令:
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--无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合并vs git重基
第一点:总是将特性合并到开发中,而不是从特性重新基础开发。这是回扣黄金法则的结果:
gitrebase的黄金法则是永远不要在公共分支机构上使用它。
换句话说:
永远不要把你推到某个地方的东西重新放在底座上。
我个人会补充一句:除非这是一个功能分支,并且您和您的团队意识到其后果。
因此,git merge与git rebase的问题几乎只适用于功能分支(在下面的示例中,合并时始终不使用ff)。注意,由于我不确定是否有更好的解决方案(存在争论),所以我只提供两个命令的行为方式。在我的例子中,我更喜欢使用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 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重基
命令:
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
从开发到功能分支
合并分支
命令:
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 develop
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重基
命令:
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 develop
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是一个很好的解决方案(-x选项在原始提交消息正文中附加一行“(cherry-picked from commit…)”,因此通常最好使用它-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--重新基数
我不确定我能比德里克·古尔利解释得更好。。。基本上,使用git pull--rebase而不是git pull:)但本文中缺少的是,您可以默认启用它:
git config --global pull.rebase true
吉特重读
再次,这里很好地解释了一下。但简单地说,如果您启用了它,您就不再需要多次解决同一冲突。
简短版本
合并在一个分支中接受所有更改,并在一次提交中将它们合并到另一个分支。Rebase说我希望我的分支点转移到一个新的起点
那你什么时候用这两种?
合并
假设您创建了一个分支来开发单个功能。当您想将这些更改带回master时,可能需要合并。
回扣
第二种情况是,如果您开始进行一些开发,然后另一个开发人员进行了不相关的更改。您可能希望从存储库中提取并重新创建基础,以将当前版本的更改作为基础。
挤压:在这两种情况下都会保留所有提交(例如:“add feature”,然后是“typ”,然后“oops typ again”…)。通过挤压,可以将提交合并为单个提交。挤压可以作为合并或重基操作(--squash标志)的一部分完成,在这种情况下,通常称为挤压合并或挤压重基。
拉取请求:流行的git服务器(Bitbucket、GitLab、GitHub等)允许配置如何在每个回购基础上合并拉取请求。按照惯例,UI可能会显示“合并”按钮,但该按钮可以使用任何标志(关键字:合并、重基、挤压、快进)执行任何操作。
Pro Git的书在重新设置基础页面上有一个很好的解释。
基本上,合并将进行两次提交并合并它们。
一个重基将转到这两者的共同祖先,并在彼此之上逐步应用更改。这使得历史更加“清晰”和线性。
但当您重新创建基准时,您将放弃以前的提交并创建新的提交。因此,您永远不应该重新设置公共存储库的基础。其他在存储库中工作的人会恨你。
仅出于这个原因,我几乎完全合并了。99%的时候,我的分支机构没有太大的差异,所以如果有冲突,只会发生在一两个地方。
在合并/重新基础之前:
A <- B <- C [master]
^
\
D <- E [branch]
git合并主机后:
A <- B <- C
^ ^
\ \
D <- E <- F
git rebase master之后:
A <- B <- C <- D' <- E'
(A、B、C、D、E和F为提交)
在Git基础教程中可以找到这个例子以及更多关于Git的详细信息。
我什么时候使用git rebase?几乎不会,因为它改写了历史。gitmerge几乎总是首选,因为它尊重项目中实际发生的事情。