两种 git 变基方法之间的区别
Difference between two git rebasing methods
我想从 master 执行更新到我的本地分支,该分支在时间轴上较早地从 master 分支出来(从 M2 更改)。
Master 表示有 M
变化,我的本地分支有 L
变化
新分支是从 master 的 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
假设there is only one remote repository,这两个会产生相同的效果。
在第一种情况下,您正在更新 master
的本地副本,然后进行变基。
在第二种情况下,您是直接从远程 存储库变基。
当您可能不想费心更新要变基的分支的本地副本时,请使用第二个选项。
例如,我们有一个主要的 develop
分支,我们可以从中创建主题分支,例如 feature/0001
。工作时,我会检查 feature/0001
并时不时简单地 git pull -r origin develop
。在这种情况下,拥有 develop
的本地最新副本是无关紧要的。
我的功能分支合并后,我检出并拉取 develop
,然后从更新的副本创建一个新的 feature/0002
分支。
此外,请注意它实际上会创建 this 作为结果:
M1 -> M2 -> M3 -> M4 -> L1' -> L2'
L1'
是什么意思?粗略地说,它将创建一个具有相同内容的新提交——具有新的 SHA 标识符。所以这不是同一个提交 本身 .
git checkout branch_to_update
git rebase master
或
git checkout branch_to_update
git pull --rebase origin master
这些是相同的结果,但方式不同
pull --rebase
参数简要使用 rebase
与其他两个答案一样,效果大致相同。 但还有更多。要了解是什么以及为什么,我们应该将 git pull
分解成它的成分。
所有挑剔的细节(警告:长)
除了一些小的例外(例如 运行将它放在一个完全空的存储库中),git pull
意味着:
- 运行
git fetch
带有各种选项和参数;然后
- 运行 第二个 Git 命令,在步骤 1 运行 之前选择,也有各种选项和参数。
第二个命令通常是git merge
,但你可以告诉Git使用git rebase
。传递给这两个命令的选项和参数取决于传递给 git pull
的选项和其他配置设置,以及第 1 步中获取的一个或多个结果。
不过,作为一种通用规则,传递给 git pull
的参数会传递给 git fetch
,因此这意味着您的第二个命令序列——将 origin master
传递给 git pull
—也将 origin master
传递给 git fetch
。如果你 运行 git pull
没有 这些参数,就像在你的第一个命令序列中一样,Git 提取 remote(通常是 origin
)和 上游 b运行ch 名称(通常与当前 b运行ch 名称相同),具体从这两个命令的结果来看:1
git config --get branch.$branch.remote
git config --get branch.$branch.merge
(其中 $branch
是当前 b运行ch)。如果当前 b运行ch 是 master
,这将使用 branch.master.remote
作为远程。这就是我们假设只有一个遥控器的意思。 merge
名称可能是 master
,但如果不是,那是我们必须做出的另一个假设,然后我们才能声称它们做同样的事情。
1如果你的 Git 够老了,git pull
是一个 shell 脚本,字面意思是 运行s各种其他 Git 命令。如果是更新的,git pull
已经转换为 C 语言程序,并且直接内置了这些。
Rebase 复制提交,然后切换到新副本
如果我们深入研究所有细节,git rebase
的作用会变得复杂,但在较高层次上,它的工作是 复制 提交。要查看它将复制哪些提交,您应该绘制提交图,或使用 git log --graph
让 Git 为您绘制。 (有的GUI总是画出来,有的web界面*咳*GitHub*咳* 永远不要让你看到它!)通过图形绘制,很容易——好吧,有时很容易——分辨出哪些提交被复制了:
...--A--B--C--D <-- master
\
E--F--G <-- br
将你的 b运行ch br
变基到你的主副本三个提交,这里 E
到 G
,将副本放在提交 D
之后。这和你画的差不多。
假设我们添加 origin/
远程跟踪名称,并显示您自己的 master
当前指向提交 B
而 origin/master
当前指向提交 D
,像这样:
C--D <-- origin/master
/
...--A--B <-- master
\
E--F--G <-- br
现在我们可以看到,我们必须将 br
变基到 origin/master
上,以便在提交 D
后保留副本。变基到 master
会将副本放在 B
之后,这是原件所在的位置,所以根本不需要复制。 (rebase 是否实际复制,或者只是重新使用原件,是挑剔的细节之一:例如,它取决于 -f
选项。)
复制完成后,git rebase
简单地重新指向 b运行ch 名称以指向最终复制(或重新使用)的提交,我们可以称之为 G'
这里要注意,它是 G
的副本。原始提交被有效地放弃,尽管 HEAD 和原始 b运行ch 的 reflog 条目,以及名称 ORIG_HEAD
,暂时保留它们:
E'-F'-G' <-- br
/
C--D <-- origin/master
/
...--A--B <-- master
\
E--F--G [abandoned, but see ORIG_HEAD and reflogs]
默认情况下,reflog 条目会将原件保留至少 30 天。最终 ORIG_HEAD
由于其他操作而移到别处,reflog 条目过期,原始提交被垃圾收集。
现在我们可以查看您的原始命令序列
让我们假设,为了论证,我们有上面的图表(和你的一样,但在 b运行ch br
上多了一个提交,我们已经 运行 git fetch
以获得 origin/master
更新)。然后 Atlassian 命令序列以这两个命令开始:
git checkout master
git pull --rebase
这会将我们的 HEAD
附加到我们的 master
,检查提交 B
;然后,假设上游是 origin/master
,运行 git fetch origin master
来更新我们的 origin/master
,在这种情况下,origin/master
指向 D
。如果我们没有 运行 git fetch
但这将 获得 提交 C
和 D
并将我们的 origin/master
指向 D
.
最后,这将运行 git rebase <hash-of-commit-D>
。 rebase 操作使用哈希 ID,因为它使用 git fetch
在 .git/FETCH_HEAD
中留下的痕迹,并且根据确切的 Git 版本和我们将在此处忽略的更多细节,还使用 git merge-base --fork-point
找到一个提交哈希,以便从上游 rebases 中恢复。 (此过程有时会出错,具体取决于您自己的工作流程,我不确定我是否喜欢默认行为。)
一旦这一切都完成了,我们得到最后两个命令:
git checkout br
git rebase master
第一个将 HEAD
附加到名称 br
,检查提交 G
。然后,rebase 将 E-F-G
提交序列复制到 master
现在指向的提交之后。因此,忽略所有 reflog 条目,我们得到图表:
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
运行s git fetch origin master
,确保我们有提交 C-D
(如果我们还没有获取它们)和更新 origin/master
(如果我们的 Git 至少是 1.8.4),然后 运行s git rebase <hash-of-D>
复制 E-F-G
链,给出:
E'-F'-G' <-- br
/
C--D <-- origin/master
/
...--A--B <-- master
\
E--F--G [abandoned]
所以关键的区别是你自己的名字,master
,永远不会更新指向提交 D
。
我推荐什么
重要的是要注意(并且知道)如果你 运行 git fetch
你自己——这是我的首选方法——这会告诉你 Git 在遥控器的 URL 上调用另一个 Git,并为您的 Git、all[=227= 设置另一个 Git 列表] 的(来源,我们假设)b运行ches。然后,您的 Git 将获得 所有 他们拥有而您没有的提交,并将它们放入您的 Git 的存储库中,并更新 所有 的远程跟踪名称,例如 origin/master
和 origin/develop
等等。
换句话说,您的远程跟踪名称,即您 Git 记住 他们的 b运行 的方式,将 全部 得到更新。这通常是一件好事。只有当他们有很多 b运行ches 和很多大提交并且你的网络连接很慢时才会糟糕;在这种情况下,您可能需要等待很长时间才能下载所有内容。
当 git pull
运行 为 git fetch
时,它 运行 带有一个限制选项。例如,如果您的 git pull
运行s:
git fetch origin master
告诉你的 Git 在 URL 呼叫 Git origin
并要求他们 运行 转 只有 向他们的master
提交新的。如果他们更新了他们的 develop
和 production
和 feature/tall
等等,你不会得到任何这些 - 你只会得到他们的 master
上的新提交.您的 Git 更新您的 origin/master
以记住新的提交,2 但保持其余的远程跟踪名称不变。
在你的第二个命令序列中,你 运行 一个明确的 git pull origin master
(还有 --rebase
),所以这限制了你的 Git 更新你的 origin/master
.在你的 first 命令序列中,你 运行 git pull
没有参数——但是 git pull
插入 origin
和 master
,假设这些是您的 master
b运行ch 的配置设置,因此这也将您的 Git 限制为仅更新您的 origin/master
.
我提到所有这些是因为我建议根本不要使用 git pull
。 运行 git fetch
你自己——你可以让它默认从 origin
获取所有内容——然后 运行 你想要的任何 git rebase
命令!在 fetch
之后,您拥有所有提交和所有适当的 origin/*
名称;然后你可以 运行:
git checkout <whatever-name>
git rebase origin/<whatever-other-name>
复制任何提交 and/or 调整您想要更新的任何 b运行ch 名称。一次提取让您可以执行任意数量的合并、重置、快进伪合并或变基操作。在决定对 运行!
执行其他 Git 命令之前,您还可以 查看 获取的内容
2这假设您的 Git 版本至少为 1.8.4。如果不是,这种git fetch
连origin/master
更新都失败。您必须 运行 git fetch
或 git fetch origin
才能更新您的远程跟踪名称!
我想从 master 执行更新到我的本地分支,该分支在时间轴上较早地从 master 分支出来(从 M2 更改)。
Master 表示有 M
变化,我的本地分支有 L
变化
新分支是从 master 的 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
假设there is only one remote repository,这两个会产生相同的效果。
在第一种情况下,您正在更新 master
的本地副本,然后进行变基。
在第二种情况下,您是直接从远程 存储库变基。
当您可能不想费心更新要变基的分支的本地副本时,请使用第二个选项。
例如,我们有一个主要的 develop
分支,我们可以从中创建主题分支,例如 feature/0001
。工作时,我会检查 feature/0001
并时不时简单地 git pull -r origin develop
。在这种情况下,拥有 develop
的本地最新副本是无关紧要的。
我的功能分支合并后,我检出并拉取 develop
,然后从更新的副本创建一个新的 feature/0002
分支。
此外,请注意它实际上会创建 this 作为结果:
M1 -> M2 -> M3 -> M4 -> L1' -> L2'
L1'
是什么意思?粗略地说,它将创建一个具有相同内容的新提交——具有新的 SHA 标识符。所以这不是同一个提交 本身 .
git checkout branch_to_update git rebase master
或
git checkout branch_to_update git pull --rebase origin master
这些是相同的结果,但方式不同
pull --rebase
参数简要使用 rebase
与其他两个答案一样,效果大致相同。 git pull
分解成它的成分。
所有挑剔的细节(警告:长)
除了一些小的例外(例如 运行将它放在一个完全空的存储库中),git pull
意味着:
- 运行
git fetch
带有各种选项和参数;然后 - 运行 第二个 Git 命令,在步骤 1 运行 之前选择,也有各种选项和参数。
第二个命令通常是git merge
,但你可以告诉Git使用git rebase
。传递给这两个命令的选项和参数取决于传递给 git pull
的选项和其他配置设置,以及第 1 步中获取的一个或多个结果。
不过,作为一种通用规则,传递给 git pull
的参数会传递给 git fetch
,因此这意味着您的第二个命令序列——将 origin master
传递给 git pull
—也将 origin master
传递给 git fetch
。如果你 运行 git pull
没有 这些参数,就像在你的第一个命令序列中一样,Git 提取 remote(通常是 origin
)和 上游 b运行ch 名称(通常与当前 b运行ch 名称相同),具体从这两个命令的结果来看:1
git config --get branch.$branch.remote
git config --get branch.$branch.merge
(其中 $branch
是当前 b运行ch)。如果当前 b运行ch 是 master
,这将使用 branch.master.remote
作为远程。这就是我们假设只有一个遥控器的意思。 merge
名称可能是 master
,但如果不是,那是我们必须做出的另一个假设,然后我们才能声称它们做同样的事情。
1如果你的 Git 够老了,git pull
是一个 shell 脚本,字面意思是 运行s各种其他 Git 命令。如果是更新的,git pull
已经转换为 C 语言程序,并且直接内置了这些。
Rebase 复制提交,然后切换到新副本
如果我们深入研究所有细节,git rebase
的作用会变得复杂,但在较高层次上,它的工作是 复制 提交。要查看它将复制哪些提交,您应该绘制提交图,或使用 git log --graph
让 Git 为您绘制。 (有的GUI总是画出来,有的web界面*咳*GitHub*咳* 永远不要让你看到它!)通过图形绘制,很容易——好吧,有时很容易——分辨出哪些提交被复制了:
...--A--B--C--D <-- master
\
E--F--G <-- br
将你的 b运行ch br
变基到你的主副本三个提交,这里 E
到 G
,将副本放在提交 D
之后。这和你画的差不多。
假设我们添加 origin/
远程跟踪名称,并显示您自己的 master
当前指向提交 B
而 origin/master
当前指向提交 D
,像这样:
C--D <-- origin/master
/
...--A--B <-- master
\
E--F--G <-- br
现在我们可以看到,我们必须将 br
变基到 origin/master
上,以便在提交 D
后保留副本。变基到 master
会将副本放在 B
之后,这是原件所在的位置,所以根本不需要复制。 (rebase 是否实际复制,或者只是重新使用原件,是挑剔的细节之一:例如,它取决于 -f
选项。)
复制完成后,git rebase
简单地重新指向 b运行ch 名称以指向最终复制(或重新使用)的提交,我们可以称之为 G'
这里要注意,它是 G
的副本。原始提交被有效地放弃,尽管 HEAD 和原始 b运行ch 的 reflog 条目,以及名称 ORIG_HEAD
,暂时保留它们:
E'-F'-G' <-- br
/
C--D <-- origin/master
/
...--A--B <-- master
\
E--F--G [abandoned, but see ORIG_HEAD and reflogs]
默认情况下,reflog 条目会将原件保留至少 30 天。最终 ORIG_HEAD
由于其他操作而移到别处,reflog 条目过期,原始提交被垃圾收集。
现在我们可以查看您的原始命令序列
让我们假设,为了论证,我们有上面的图表(和你的一样,但在 b运行ch br
上多了一个提交,我们已经 运行 git fetch
以获得 origin/master
更新)。然后 Atlassian 命令序列以这两个命令开始:
git checkout master
git pull --rebase
这会将我们的 HEAD
附加到我们的 master
,检查提交 B
;然后,假设上游是 origin/master
,运行 git fetch origin master
来更新我们的 origin/master
,在这种情况下,origin/master
指向 D
。如果我们没有 运行 git fetch
但这将 获得 提交 C
和 D
并将我们的 origin/master
指向 D
.
最后,这将运行 git rebase <hash-of-commit-D>
。 rebase 操作使用哈希 ID,因为它使用 git fetch
在 .git/FETCH_HEAD
中留下的痕迹,并且根据确切的 Git 版本和我们将在此处忽略的更多细节,还使用 git merge-base --fork-point
找到一个提交哈希,以便从上游 rebases 中恢复。 (此过程有时会出错,具体取决于您自己的工作流程,我不确定我是否喜欢默认行为。)
一旦这一切都完成了,我们得到最后两个命令:
git checkout br
git rebase master
第一个将 HEAD
附加到名称 br
,检查提交 G
。然后,rebase 将 E-F-G
提交序列复制到 master
现在指向的提交之后。因此,忽略所有 reflog 条目,我们得到图表:
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
运行s git fetch origin master
,确保我们有提交 C-D
(如果我们还没有获取它们)和更新 origin/master
(如果我们的 Git 至少是 1.8.4),然后 运行s git rebase <hash-of-D>
复制 E-F-G
链,给出:
E'-F'-G' <-- br
/
C--D <-- origin/master
/
...--A--B <-- master
\
E--F--G [abandoned]
所以关键的区别是你自己的名字,master
,永远不会更新指向提交 D
。
我推荐什么
重要的是要注意(并且知道)如果你 运行 git fetch
你自己——这是我的首选方法——这会告诉你 Git 在遥控器的 URL 上调用另一个 Git,并为您的 Git、all[=227= 设置另一个 Git 列表] 的(来源,我们假设)b运行ches。然后,您的 Git 将获得 所有 他们拥有而您没有的提交,并将它们放入您的 Git 的存储库中,并更新 所有 的远程跟踪名称,例如 origin/master
和 origin/develop
等等。
换句话说,您的远程跟踪名称,即您 Git 记住 他们的 b运行 的方式,将 全部 得到更新。这通常是一件好事。只有当他们有很多 b运行ches 和很多大提交并且你的网络连接很慢时才会糟糕;在这种情况下,您可能需要等待很长时间才能下载所有内容。
当 git pull
运行 为 git fetch
时,它 运行 带有一个限制选项。例如,如果您的 git pull
运行s:
git fetch origin master
告诉你的 Git 在 URL 呼叫 Git origin
并要求他们 运行 转 只有 向他们的master
提交新的。如果他们更新了他们的 develop
和 production
和 feature/tall
等等,你不会得到任何这些 - 你只会得到他们的 master
上的新提交.您的 Git 更新您的 origin/master
以记住新的提交,2 但保持其余的远程跟踪名称不变。
在你的第二个命令序列中,你 运行 一个明确的 git pull origin master
(还有 --rebase
),所以这限制了你的 Git 更新你的 origin/master
.在你的 first 命令序列中,你 运行 git pull
没有参数——但是 git pull
插入 origin
和 master
,假设这些是您的 master
b运行ch 的配置设置,因此这也将您的 Git 限制为仅更新您的 origin/master
.
我提到所有这些是因为我建议根本不要使用 git pull
。 运行 git fetch
你自己——你可以让它默认从 origin
获取所有内容——然后 运行 你想要的任何 git rebase
命令!在 fetch
之后,您拥有所有提交和所有适当的 origin/*
名称;然后你可以 运行:
git checkout <whatever-name>
git rebase origin/<whatever-other-name>
复制任何提交 and/or 调整您想要更新的任何 b运行ch 名称。一次提取让您可以执行任意数量的合并、重置、快进伪合并或变基操作。在决定对 运行!
执行其他 Git 命令之前,您还可以 查看 获取的内容2这假设您的 Git 版本至少为 1.8.4。如果不是,这种git fetch
连origin/master
更新都失败。您必须 运行 git fetch
或 git fetch origin
才能更新您的远程跟踪名称!