git rebase
命令在幕后使用合并,所以我想知道git rebase
如何/为什么避免合并提交,而git merge通常会在分支之间存在差异时创建新的提交。
答案 0 :(得分:4)
rebase
不是像合并分支那样的合并。相反,rebase
是一系列git cherry-pick
。樱桃镐选择一个提交并将其复制,就好像它是在其他某个提交之上编写的一样。就像您将提交的差异应用到其他地方一样。
考虑这样的存储库。 A,B,C等代表提交ID。
A - B - C - D [master]
\
E - F - G [feature]
如果master
需要E
,也许这是一个关键的错误修复程序,但不是feature
的其余部分,它可以git cherry-pick E
。 Git将以C为原点在D和E之间进行合并,从而得到E1。这就是为什么在重新设置基准时可能会发生合并冲突的原因。但是与普通合并不同,E1只有一个父级。
A - B - C - D - E1 [master]
\
E - F - G [feature]
重新设置就像是否对您的所有提交都执行了。并且移动master
而不是放在feature
上方。这是git rebase master
的逐步说明。
再次考虑您的存储库。
A - B - C - D [master]
\
E - F - G [feature]
首先,如前所述,它在E上执行cherry-pick
,并将其放在master
的顶部。但是,它没有移动master
,而是创建了一个没有标签的新分支。
E1
/
A - B - C - D [master]
\
E - F - G [feature]
然后将其插入F并置于E1之上。
E1 - F1
/
A - B - C - D [master]
\
E - F - G [feature]
然后樱桃选择G并将其放在F1上作为G1。
E1 - F1 - G1
/
A - B - C - D [master]
\
E - F - G [feature]
完成摘樱桃后,它将feature
分支标签移至G1。
E1 - F1 - G1 [feature]
/
A - B - C - D [master]
\
E - F - G
是的,旧的提交仍然存在。如果没有引用它们的信息(例如标签或其他分支),则将在几周内将其回收。同时,您仍然可以使用git reflog
访问它们。有关更多信息,请参见Maintenance and Data Recovery chapter of Pro Git。
答案 1 :(得分:3)
我喜欢在Git中将合并的概念分为两部分:
动词要合并表示要合并更改。许多命令都使用此命令。
名词 a merge ,源自形容词形式 merge commit ,其中形容词“ merge”修饰名词“ commit”,表示 a与两个或更多父母在一起。只有git merge
会生成此类提交(嗯,git merge
或运行git merge
的事物,包括git pull
)。
这意味着git rebase
使用动词形式 merge 操作,该操作通常通过重复的git cherry-pick
发生。 1 Git通过执行以提交的parent 2 作为合并基础的合并,将该提交本身作为“其他”提交,以及当前的HEAD
提交发挥其正常作用的方式,樱桃提交。 zzxyz's comment因此是正确的:每次执行摘樱桃操作都可能导致合并冲突,这是人类必须解决的。
每个Cherry-pick操作的结果是每个提交的副本,如Schwern noted in his answer。复制感兴趣的提交后,Git移动分支名称,使其指向最终复制的提交-新分支的尖端在提交图中的新位置。由于ORIG_HEAD
(在移动分支名称之前设置了rebase
)以及由于各种reflog(对于HEAD
本身和/或分支,原始提交至少保留了一段时间) git rebase
刚刚更改的名称。
最后需要注意的是上一段中的利益承诺。当rebase对将要提交的提交进行布局时,默认情况下故意放弃合并。在此过程的任一侧都有论点(即“保持合并”与“丢弃合并”),但这就是Git今天要做的。因此,在重新定基后,即使复制提交的过程涉及 merge 动词,副本也不会包含任何合并提交。
1 交互式git rebase -i
或与git rebase
或-m
一起运行的-s strategy
实际上使用git cherry-pick
。没有-m
或-s
的非交互式rebase使用git format-patch
和git am -3
,这在技术上在几个方面都较差,但向后兼容古老的Git 1.5。
2 这就是很难进行挑选的原因:合并-合并提交,根据定义是具有两个或多个父级的提交-没有 a 父级,它有两个或更多父级。 git cherry-pick
命令可以让您随意选择它,但要这样做,您必须指定哪个父命令,该命令应将其作为“动词合并”过程中的合并基础。
答案 2 :(得分:2)
您可以这样考虑:git rebase
从分支中提取新提交并将其放在一边,然后倒回分支以匹配更新的原始提交,最后在结果之上添加您的提交; git merge
仅使用分支之间的差异创建一个新的(合并)提交。请注意,在重新部署原始分支上不存在的所有提交时,它们的哈希值都会改变,因此您将不得不强行推入新分支(如果有多个人在同一分支上进行协作,这并不总是那么容易科)。但是,通过重定基础,您可以避免合并提交并保持更清晰的历史记录。
答案 3 :(得分:1)
您是正确的,即基本是在后台进行合并。实际上,您可以使用merge命令创建自己的rebase命令,而您所需要做的就是随后修改一些提交元数据。
让我们说这是您的历史记录
A - B - C (master)
\
D - E (dev)
如果您希望将dev改编为master,可以先合并D然后合并E,
A - B - C - D'- E'
\ / /
+ - - - D - E
然后您修改提交以删除D和E中的链接,
A - B - C - D"- E" (master)
\
D - E (dev)
因此,您可以看到git rebase仅仅是合并之上的一些简单的历史操作(仅更改元数据)。您看不到合并提交的原因仅仅是一个元数据问题-每个提交都有指向其父提交的指针,如果删除除其中一个指针以外的所有指针,则会删除该提交与那些父提交的连接,从而将合并提交变成非合并提交。
也可以用这种方法完成更复杂的变基。请注意,这并不是重新设置实际的工作方式,它只是您可以手动重新设置提交内容而无需使用rebase或cherry-pick命令的一种方式。重要的是,合并提交无非就是两个或多个父级的提交,您可以通过修改元数据来更改任何提交的父级。
答案 4 :(得分:1)
很多人回答了如何 rebase
避免合并提交,但是我认为为什么有点有趣。您可以用它做很多事情,所以这只是我对最重要的观点。 (其他具有不同工作流程的人自然会不同意:)
在集中式存储库系统(例如svn
或perforce
)中,提交直接完成到服务器。因此,如果Bob和Jim都在主分支的本地副本上工作,那么他们都在主分支上工作。故事结局。在Bob和Jim做出承诺之后,我们可以按顺序在一个分支中看到它们。
git
的潜在极少缺点之一是,远程存储库实际上是 隐式分支。它的行为就像一个,它像在git日志中一样显示,等等。因此,如果Bob和Jim都对其本地副本进行了更改,则要推送的最后一个必须拉出...并从远程进行合并。这样看来,鲍勃和吉姆都在各自的分支机构工作。有时,这种行为是完美的。有时这是不可取的。
git pull --rebase
/ git fetch;git rebase
是告诉git
的方式:“我知道您认为这是一个分支,但我不知道。我只想像正常人一样,在最后一个承诺的基础上做我的承诺,并拥有我认为的准确分支历史。”我个人不太关心干净。