merge --squash 和 rebase 有什么区别?
我试图理解壁球和变基之间的区别。据我了解,在进行变基时会执行挤压。
I'm trying to understand the difference between a squash and a rebase. As I understand it, one performs a squash when doing a rebase.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
合并提交:保留分支中的所有提交,并将它们与基础分支上的提交交错
合并挤压:保留更改但忽略历史记录中的各个提交
变基:这会将整个功能分支移至 master 分支的顶端,有效地将所有新提交合并到 master
有关 此处
前两张图来自 关于 GitHub 文档上的拉取请求合并
Merge commits: retains all of the commits in your branch and interleaves them with commits on the base branch
Merge Squash: retains the changes but omits the individual commits from history
Rebase: This moves the entire feature branch to begin on the tip of the master branch, effectively incorporating all of the new commits in master
More on here
The first two diagrams come from About pull request merges on the GitHub Docs
git merge --squash 和 git rebase --interactive 都可以产生“压缩”提交。但它们有不同的目的。
git merge --squash abranch
将生成一个压缩目标分支上的提交,不标记任何合并关系。
(注意:它不会立即生成提交:您需要一个额外的 git commit -m "squashbranch")
如果您想完全丢弃源分支,从 (schema取自SO问题):
到:
然后删除
tmp< /代码> 分支。
注意:
git merge
有一个--commit
选项,但不能与--squash
一起使用。 永远不可能同时使用--commit
和--squash
。自 Git 2.22.1(2019 年第 3 季度)起,这种不兼容性变得明确:
请参阅 commit 1d14d0c(2019 年 5 月 24 日)作者:Vishal Verma (
reloadbrain
)。(由 Junio C Hamano --
gitster
-- 合并于 提交 33f2790,2019 年 7 月 25 日)git/git
builtin/merge.c#cmd_merge()
现在包括:git rebase --interactive
在新的基础上重放您的部分或全部提交,允许您压缩(或者最近“修复”,请参阅此 SO问题),直接转到:
如果您选择压缩
tmp
的所有提交(但是,与merge --squash
相反,您可以选择重播某些内容,并压缩其他内容)。因此,区别在于:
squash
不会触及您的源分支(此处为tmp
),并在您想要的位置创建单个提交。rebase
允许您在同一源分支上继续(仍然是tmp
):Both
git merge --squash
andgit rebase --interactive
can produce a "squashed" commit. But they serve different purposes.git merge --squash abranch
will produce a squashed commit on the destination branch, without marking any merge relationship.
(Note: it does not produce a commit right away: you need an additional
git commit -m "squash branch"
)This is useful if you want to throw away the source branch completely, going from (schema taken from SO question):
to:
and then deleting
tmp
branch.Note:
git merge
has a--commit
option, but it cannot be used with--squash
. It was never possible to use--commit
and--squash
together.Since Git 2.22.1 (Q3 2019), this incompatibility is made explicit:
See commit 1d14d0c (24 May 2019) by Vishal Verma (
reloadbrain
).(Merged by Junio C Hamano --
gitster
-- in commit 33f2790, 25 Jul 2019)git/git
builtin/merge.c#cmd_merge()
now includes:git rebase --interactive
replays some or all of your commits on a new base, allowing you to squash (or more recently "fix up", see this SO question), going directly to:
If you choose to squash all commits of
tmp
(but, contrary tomerge --squash
, you can choose to replay some, and squashing others).So the differences are:
squash
does not touch your source branch (tmp
here) and creates a single commit where you want.rebase
allows you to go on on the same source branch (stilltmp
) with:让我们从以下示例开始:
现在我们有 3 个选项将功能分支的更改合并到主分支:
合并提交
将保留功能分支的所有提交历史记录并将它们移动到主分支
将添加额外的虚拟提交。
变基和合并
会将功能分支的所有提交历史记录附加到主分支的前面
不会添加额外的虚拟提交。
挤压并合并
将所有功能分支提交分组为一个提交,然后将其附加到主分支的前面
将添加额外的虚拟提交。
您可以在下面找到主分支将如何照顾它们中的每一个。
在所有情况下:
我们可以安全地删除功能分支。
Let's start by the following example:
Now we have 3 options to merge changes of feature branch into master branch:
Merge commits
Will keep all commits history of the feature branch and move them into the master branch
Will add extra dummy commit.
Rebase and merge
Will append all commits history of the feature branch in the front of the master branch
Will NOT add extra dummy commit.
Squash and merge
Will group all feature branch commits into one commit then append it in the front of the master branch
Will add extra dummy commit.
You can find below how the master branch will look after each one of them.
In all cases:
We can safely DELETE the feature branch.
合并挤压将树(一系列提交)合并为单个提交。也就是说,它将 n 次提交中所做的所有更改压缩为单个提交。
Rebasing就是重新定基,即为树选择一个新的基础(父提交)。也许这个多变的术语更清楚:他们称之为移植,因为它只是:为一棵树选择一个新的基础(父提交,根)。
在进行交互式变基时,您可以选择压缩、选择、编辑或跳过要变基的提交。
希望这是清楚的!
Merge squash merges a tree (a sequence of commits) into a single commit. That is, it squashes all changes made in n commits into a single commit.
Rebasing is re-basing, that is, choosing a new base (parent commit) for a tree. Maybe the mercurial term for this is more clear: they call it transplant because it's just that: picking a new ground (parent commit, root) for a tree.
When doing an interactive rebase, you're given the option to either squash, pick, edit or skip the commits you are going to rebase.
Hope that was clear!
我学会理解 squash、merge 等之间的价值和区别的方法是为自己编写这个简短的教程。
如果你遵循这个,它将解释你想了解的关于挤压、合并、快进和变基的所有内容。
功能分支
假设您在原点有 Main 分支,具有提交历史记录,A。
您从 Main 中提取内容,并创建一个新的分支,FeatureA,这样您就有提交历史记录:
您想要将您的更改推送到 Origin 并合并到 Main 中。由于 Origin 上的 Main 没有改变,您也可以快进。
快进合并 将
FeatureA 分支的所有提交历史记录应用到 Main 提交历史记录之上。
与 Squash 合并
当您的分支有多个提交时,可用的选项就是压缩。与 Main 合并时,压缩会将 FeatureA 分支的历史记录合并为单个提交。
如果 Main 上有其他更改,则无法快进。
如果您对 FeatureA 分支进行了更改,同时其他人已推送进入 Main,这样提交的起源历史现在是:A、B、C
您希望将更改推送到 Main 分支Origin,您有几个不同的选项:
合并(也称为不快进合并)
如果您要获取 Origin 的 Main 分支的本地副本并将其合并到本地 FeatureA 分支中,您将创建一个提交对于该合并,M。FeatureA 上的本地历史记录将是:A、F1、F2、F3、M,其中 M 是合并提交。
然后你可以合并到Main(如果你愿意的话可以压缩)。请注意,如果您不压缩,您将在 Main 提交历史记录中引入合并提交:
您可以通过压缩< /em> 合并到 Main 时的分支,如上面 Squash 部分所述,结果为:ABCF,如下所示:
Rebase
Rebase 是避免引入将提交合并到历史记录中。它本质上会接受您的更改,并使其就像您已开始使用最新的 Main,而不是使用过时的 Main 版本。例如,在变基后,您的功能分支将从 A、B、C(而不是最初的 A)分支出来。结果:
注意,没有合并 Git 实现此目的
的方式是系统地(逐次提交)将更改从源的 Main 应用于您的分支。最终结果就好像您过去没有基于提交 A 启动您的 FeatureA 分支,但就好像您在最新的 Main 之上开始了所有更改。
问题是,这样做的方式。对于必须应用于 FeatureA 分支的每个提交,git 都会检查合并冲突,并且您必须解决它们。如果自您的 FeatureA 分支以来,对 Origin 上的 Main 进行了多次更改,则您可以一遍又一遍地解决合并冲突
。拉比较容易?
存储和拉取
存储您的 FeatureA,从 Origin 获取/拉取 Main 的最新本地副本,然后创建一个新分支并应用您的存储。 (或者将新的 Main 分支合并到您的分支中,并应用您的存储。如果执行后者,请压缩以避免在提交历史记录中合并。)然后您可以推送到 Origin / 创建拉取请求。
The way I have learned to understand the value and difference between squash, merge, etc. is to put together this short tutorial for myself.
If you follow this it will explain everything you want to know about squash, merge as well as fast forward, and rebase.
Feature Branches
Say you have Main branch at the Origin, with a history of Commits, A.
You pull from Main, and create a new branch, FeatureA, such that you have commit history:
You want to push you changes into Origin and merge into Main. Since Main on Origin hasn’t changed you are also able to Fast Forward.
Merge with Fast Forward
Apply all the commit history of FeatureA branch on top of Main’s commit history.
Merge with Squash
An option available whenever your branch has multiple commits is to squash. Squashing combines the history of FeatureA branch into a single commit when merging with Main.
Fast forward isn’t possible if there have been other changes on Main.
If you made changes to the FeatureA branch, meanwhile others had pushed into Main, such that the Origin history of Commits is now: A, B, C
You want to push you changes into Main branch on Origin, and you have a few different options:
Merge (aka. Merge without fast-forward)
If you were to fetch a local copy of Origin’s Main branch and merge it into your local FeatureA branch, you would create a commit for that merge, M. You local history on FeatureA would be: A, F1, F2, F3, M, where M is the merge commit.
You could then merge to Main (squashing if you wanted). Note that if you don’t squash, you will introduce a Merge commit into the Main commit history:
You could avoid this by squashing your branch when merging to Main, as described above in the Squash section, resulting in: A B C F, like so:
Rebase
Rebase is another option to avoid introducing a merge commit into the history. It essentially takes your changes, and makes it as if you had begun on the latest Main, rather than on an outdated version of Main. For example, after rebasing your Feature branch would branch off from A, B, C, (instead of A, as it was originally). The result:
Note, there is no Merge commit, M
The way Git achieves this is to systematically--commit-by-commit--apply the changes from origin’s Main to your branch. The end result is as if you had not started your FeatureA branch in the past based off commit A, but as if you started all your changes on top of the latest Main.
The problem is, in the way this is done. For each commit that has to be applied into your FeatureA branch, git checks for merge conflicts, and you have to resolve them. If there has been more than one change to Main on Origin since your FeatureA branch, you can be resolving merge conflicts over and over again.
Perhaps a stash and pull is easier?
Stash and Pull
Stash your FeatureA, Fetch/Pull an up to date local copy of Main from Origin, and either create a new branch and apply your stash. (Or merge your new Main branch into your branch, and apply your stash. If doing the latter, squash to avoid a merge in the commit history.) Then you can push to Origin / create pull request.
合并和变基都保留提交历史记录,而压缩则不保留。保留提交历史记录会产生巨大的影响,我在以下场景中经历了惨痛的教训:
我们有 master、develop 和 feature 分支。功能是在开发过程中创建的,并在发布时合并到主版本中。 Hotfix 分支是从 master 创建并合并的(因此开发人员不知道这一点)。 Master 在发布后没有合并回开发,因此它们不同步,从 master 合并回开发显示更改,而从开发向前合并到 master 也显示更改(即使开发中没有任何更改)。
造成这种情况的原因是当 master 合并回开发时压缩了提交。进行定期合并以保留提交历史记录,解决了问题。从开发合并到主控现在没有显示出任何预期的变化。
Merge and rebase both retain commit history vs squash doesn't. Retaining commit history has a huge impact and I learned it the hard way in the following scenario:
We have master, develop and feature branches. Feature is created off of develop and merged to master upon release. Hotfix branch was created off of master and merged (so develop doesn't know about it). Master was not merged back to develop after release so they got out of sync where merge from master back into develop shows changes and forward merge from develop to master also shows changes (even when nothing in develop has changed).
The cause of this was squashing commits when master is merged back to develop. Doing a regular merge which keeps the commit history, fixed the problem. Merge from develop to master doesn't show any changes now as expected.