建议何时使用Git rebase与Git merge?

成功重新创建数据库后,是否仍需要合并?


当前回答

当您正在处理分支并在其间合并了一些其他工作时,Rebase是有用的-合并将创建更改,从而使diff受到污染,因此更难阅读。

如果您重新设置分支的基值,那么您的提交将应用于要重新设置基值的分支的顶部,这使得查看更容易,并且diff输出更干净。

其他回答

当您正在处理分支并在其间合并了一些其他工作时,Rebase是有用的-合并将创建更改,从而使diff受到污染,因此更难阅读。

如果您重新设置分支的基值,那么您的提交将应用于要重新设置基值的分支的顶部,这使得查看更容易,并且diff输出更干净。

这里的很多答案都说,合并会将所有提交转换为一个,因此建议使用rebase来保存提交。这是不正确的。如果你已经提交了,那是个坏主意。

合并不会消除提交。合并保留历史!(看看gitk)热巴改写了历史,这是你推后的坏事。

使用merge,而不是在已经推送的情况下重新设置基址。

这是Linus(Git的作者)对它的看法(现在托管在我自己的博客上,由Wayback Machine恢复)。这真是一本好书。

或者你可以在下面阅读我自己的版本。

在主节点上重新分配分支:

提供了如何创建提交的错误想法用一堆可能没有经过良好测试的中间提交来污染master实际上可能会在这些中间提交上引入构建中断,因为在创建原始主题分支和重新创建主题分支之间对master进行了更改。这使得在master中找到好地方结账变得困难。导致提交的时间戳与其在树中的时间顺序不一致。因此,您将看到提交A在主提交B之前,但提交B是先编写的。(什么?!)产生更多的冲突,因为主题分支中的每个提交都可能涉及合并冲突,这些冲突必须单独解决(关于每个提交中发生的情况,请进一步查看历史)。是对历史的改写。如果正在重新创建的分支被推到任何地方(与除您之外的任何人共享),那么自您改写历史以来,您已经将拥有该分支的所有其他人都搞砸了。

相反,将主题分支合并为主分支:

保留主题分支创建位置的历史记录,包括从主分支到主题分支的任何合并,以帮助保持其最新状态。您可以准确地了解开发人员在构建时使用的代码。master是一个主要由合并组成的分支,而这些合并提交中的每一个都是历史上可以安全检出的“好点”,因为这就是主题分支可以集成的地方。主题分支的所有单独提交都会被保留,包括它们位于主题分支中的事实,因此隔离这些更改是很自然的,您可以在需要时钻取。合并冲突只需解决一次(在合并时),因此在主题分支中进行的中间提交更改不必单独解决。可以平滑地进行多次。如果您定期将主题分支集成到master,人们可以继续在主题分支上进行构建,并且可以继续独立地进行合并。

信息图形总是有用的:)

合并:将一个分支重叠到另一个分支上

Assume the following history exists and the current branch is "master":

                     A---B---C topic
                    /
               D---E---F---G master

       Then "git merge topic" will replay the changes made on the topic branch since it
       diverged from master (i.e., E) until its current commit (C) on top of master, and
       record the result in a new commit along with the names of the two parent commits
       and a log message from the user describing the changes.

                     A---B---C topic
                    /         \
               D---E---F---G---H master

Rebase:将一个分支的更改移动到另一个分支末端

Assume the following history exists and the current branch is "topic":

                     A---B---C topic
                    /
               D---E---F---G master

       From this point, the result of either of the following commands:

           git rebase master
           git rebase master topic

       would be:

                             A'--B'--C' topic
                            /
               D---E---F---G master

       NOTE: The latter form is just a short-hand of git checkout topic followed by git
       rebase master. When rebase exits topic will remain the checked-out branch.

因此,我们基本上可以得出结论,合并是一个安全的选项,它可以保存存储库的整个历史,而重基化通过将特性分支移动到main的顶端来创建线性历史。

Credit:帮助页面gitmerge--help和gitrebase--help

简短版本

合并在一个分支中接受所有更改,并在一次提交中将它们合并到另一个分支。Rebase说我希望我的分支点转移到一个新的起点

那你什么时候用这两种?

合并

假设您创建了一个分支来开发单个功能。当您想将这些更改带回master时,可能需要合并。

回扣

第二种情况是,如果您开始进行一些开发,然后另一个开发人员进行了不相关的更改。您可能希望从存储库中提取并重新创建基础,以将当前版本的更改作为基础。

挤压:在这两种情况下都会保留所有提交(例如:“add feature”,然后是“typ”,然后“oops typ again”…)。通过挤压,可以将提交合并为单个提交。挤压可以作为合并或重基操作(--squash标志)的一部分完成,在这种情况下,通常称为挤压合并或挤压重基。

拉取请求:流行的git服务器(Bitbucket、GitLab、GitHub等)允许配置如何在每个回购基础上合并拉取请求。按照惯例,UI可能会显示“合并”按钮,但该按钮可以使用任何标志(关键字:合并、重基、挤压、快进)执行任何操作。

这个答案广泛围绕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

吉特重读

再次,这里很好地解释了一下。但简单地说,如果您启用了它,您就不再需要多次解决同一冲突。