两个git rebasing方法之间的区别

时间:2018-04-25 15:52:02

标签: git git-rebase

我想从master到我的本地分支执行更新,该分支在时间轴上从早期分支出来(从M2更改)。 Master用M更改表示,我的本地分支L更改

新分支是从主人的M2

创建的
 M1->M2-->M3->M4
      \
       L1->L2

我想我的结果应该是我当地的分支机构如下:

M1->M2->M3->M4->L1->L2

这意味着重新创建我的本地分支以首先进行所有主更改,然后我的本地分支在其上更改,如下所述:
   https://www.atlassian.com/git/tutorials/merging-vs-rebasing (如果我错了,请纠正我)

我的问题是,下列方法之一是否未创建上述所需流程,如果是,为什么?

git checkout master
git pull --rebase 
git checkout branch_to_update
git rebase master` (method mentioned in attlasian tautorial)

VS

git checkout branch_to_update
git pull --rebase origin master

3 个答案:

答案 0 :(得分:3)

假设there is only one remote repository,这两者将具有相同的效果。

在第一种情况下,您要更新master的本地副本,然后重新定位。

在第二种情况下,您直接从远程存储库重新定位

当您可能不想更新您正在变基的分支的本地副本时,请使用第二个选项。

例如,我们有一个主develop分支,我们可以在其中创建主题分支,例如feature/0001。在工作期间,我会不时地feature/0001签出git pull -r origin developdevelop。在这种情况下,拥有develop的本地最新副本是无关紧要的。

合并我的功能分支后,我结帐并拉出feature/0002,然后从该更新的副本中创建一个新的M1 -> M2 -> M3 -> M4 -> L1' -> L2' 分支。

此外,请注意,它实际上会创建 this

L1'

for (int i = 0; i < 12; i++) { cout << "Month number " << i + 1 << endl; array[i] = i+1; } 我的意思是什么?粗略地说,它将使用相同的内容创建一个新的提交 - 使用新的SHA标识符。所以它本身不是提交

答案 1 :(得分:2)

与其他两个答案一样,效果通常几乎相同。 msanford has pointed out one definite and one potential difference但还有更多。为了了解什么和为什么,我们应该将git pull反汇编成其成分。

所有挑剔的细节(警告:长)<​​/ h3>

除了一些小的例外(例如在完全空的存储库中运行它),git pull表示:

  1. 使用各种选项和参数运行git fetch;然后
  2. 运行第二个Git命令,在步骤1运行之前选择,还有各种选项和参数。
  3. 第二个命令通常是git merge,但您可以告诉Git使用git rebase。传递给这两个命令的选项和参数取决于传递给git pull的选项和其他配置设置,以及步骤1中获取的结果或结果。

    但是,作为一种一般规则,传递给git pull的参数将传递给git fetch,这意味着您的第二个命令序列 - 将origin master传递给git pull } - 也将origin master传递给git fetch。如果您在没有这些参数的情况下运行git pull ,就像在第一个命令序列中一样,Git会提取远程(通常是origin)和您的配置中的上游分支名称(通常与当前分支名称相同),特别是来自这两个命令的结果: 1

    git config --get branch.$branch.remote
    git config --get branch.$branch.merge
    

    (其中$branch是当前分支)。如果当前分支为master,则使用branch.master.remote作为远程分支。这就是假设只有一个遥控器的含义。 merge名称可能是master,但如果不是,那么我们必须做出另一个假设,然后我们才能宣称这些做同样的事情。

    1 如果您的Git足够大,git pull是一个shell脚本,它实际上运行各种其他Git命令。如果它更新,git pull已转换为C语言程序,并且它已直接内置。

    Rebase复制提交,然后切换到新副本

    如果我们深入研究所有细节,git rebase会变得复杂,但在高层次上,它的工作是复制提交。要查看它将复制哪些提交,您应该绘制提交图,或使用git log --graph让Git为您绘制它。 (有些GUI总是绘制它,一些web界面 * cough * GitHub * cough * 永远不会让你查看它!)用图形绘制,它很容易,很容易分辨哪些提交被复制:

    ...--A--B--C--D   <-- master
             \
              E--F--G   <-- br
    

    在主服务器上重新分配您的分支br会复制三次提交,此处为EG,在提交D后放置副本。这与你画的类似。

    假设我们添加origin/远程跟踪名称,并显示您自己的master当前指向提交B,而origin/master当前指向提交{{1}像这样:

    D

    现在我们可以看到,我们必须将 C--D <-- origin/master / ...--A--B <-- master \ E--F--G <-- br 重新定位到br,以便在提交origin/master之后复制副本。重新定位到D会将副本放在master之后,这是原件所在的位置,因此根本不需要复制。 (无论rebase实际上是复制还是只是重新使用原件,都是一个挑剔的细节:例如,它取决于B选项。)

    复制完成后,-f只需将分支名称重新指向最终复制(或重复使用)的提交,我们可以在此处调用git rebase来注意它&# 39; G'的副本。虽然HEAD和原始分支的reflog条目以及名称G暂时保留了原始提交,但原始提交已被有效放弃:

    ORIG_HEAD

    默认情况下,reflog条目使原件至少可以保留30天。最终 E'-F'-G' <-- br / C--D <-- origin/master / ...--A--B <-- master \ E--F--G [abandoned, but see ORIG_HEAD and reflogs] 由于其他操作而转移到其他地方,并且reflog条目到期,原始提交被垃圾收集。

    现在我们可以查看原始命令序列

    为了论证,我们假设我们有上面的图表(就像你的一样,但在分支ORIG_HEAD上还有一个提交,我们已经运行br来获取{{1 }} 更新)。然后Atlassian命令序列以这两个命令开始:

    git fetch

    这会将我们的origin/master附加到我们的git checkout master git pull --rebase ,并检查提交HEAD;然后,假设上游为master,请运行B更新我们的origin/master,在这种情况下,git fetch origin master指向origin/master。如果我们尚未运行origin/master,那么获取提交Dgit fetch并将C指向D

    最后,这将运行origin/master。 rebase操作使用哈希ID,因为它使用Dgit rebase <hash-of-commit-D>中留下的跟踪,并且根据确切的Git版本以及我们在这里忽略的更多详细信息,还使用git fetch找到提交哈希以便从上游rebase恢复。 (这个过程有时会出错,取决于你自己的工作流程,我不确定我是否喜欢默认行为。)

    完成所有操作后,我们将完成最后两个命令:

    .git/FETCH_HEAD

    第一个将git merge-base --fork-point附加到名称git checkout br git rebase master ,检出提交HEAD。然后,rebase将br提交序列复制到G现在指向的提交之后。因此,忽略所有reflog条目,我们得到图表:

    E-F-G

    将此与较短的命令序列进行比较:

    master

    结帐会将 E'-F'-G' <-- br (HEAD) / ...--A--B--C--D <-- master, origin/master \ E--F--G [abandoned] 附加到git checkout br git pull --rebase origin master HEAD运行br,确保我们提交pull(如果我们尚未提取)并更新git fetch origin master(如果我们的Git至少为1.8.4) ),然后运行复制C-D链的origin/master,给出:

    git rebase <hash-of-D>

    因此,关键区别在于您自己的名称E-F-G永远不会更新为指向提交 E'-F'-G' <-- br / C--D <-- origin/master / ...--A--B <-- master \ E--F--G [abandoned]

    我建议改为

    重要的是要注意(并且知道)如果你自己运行master - 这是我首选的方法 - 这将告诉你的Git调用另一个Git在远程的URL上,并为你的Git提供了另一个Git列表,所有(我们假设的原点)分支。然后,您的Git将获取所有他们所拥有的提交,并将它们放入您的Git存储库中,并更新您所有的遥控器 - 跟踪Dgit fetch之类的名称,等等。

    换句话说,您的远程跟踪名称(即Git记住 分支的方式)将所有更新。这通常是件好事。如果他们有很多分支机构和大量提交并且你的网络连接很慢,那就很糟糕;在这种情况下,您可能需要等待很长时间才能下载所有内容。

    origin/master运行origin/develop时,它会使用限制选项运行它。例如,如果您的git pull运行:

    git fetch

    告诉您的Git在git pull的网址上调用Git,并要求他们仅将 > 提交给他们的git fetch origin master 。如果他们对origin以及masterdevelop等进行了更新,那么您将无法获得其中的任何内容 - 您只会获得{{1}上的新提交}}。您的Git会更新您的production以记住新提交, 2 ,但不会更改其余的远程跟踪名称。

    在第二个命令序列中,您运行一个明确的feature/tall(同时也带有master),这会限制您的Git更新origin/master。在第一个命令序列中,您运行git pull origin master但没有参数 - 但--rebase插入origin/mastergit pull,假设这些是已配置的设置您的git pull分支,因此这也限制了您的Git只更新了您的origin

    我提到这一切是因为我建议不要使用master。自己运行master - 您可以让它默认从origin/master获取所有内容 - 然后运行您想要的任何git pull命令!在git fetch之后,您拥有所有提交和所有相应的origin名称;然后你可以运行:

    git rebase

    复制任何提交和/或调整您想要更新的自己的分支名称。一次提取允许您执行任意数量的合并,重置,快进伪合并或rebase操作。在决定运行其他Git命令之前,您还可以查看获取的内容!

    2 这假设你的Git至少是版本1.8.4。如果没有,这种fetch甚至无法更新origin/*。您必须运行git checkout <whatever-name> git rebase origin/<whatever-other-name> git fetch才能更新远程跟踪名称!

答案 2 :(得分:0)

git checkout branch_to_update
git rebase master

git checkout branch_to_update
git pull --rebase origin master

这些结果相同但方式不同 pull --rebase参数简要使用rebase