git rebase 和 git reset 的区别

Ale*_*lls 0 git git-rebase

说我有这个:

A - B - C - E - F    [integration]
 \
  G - H - I          [feature]
Run Code Online (Sandbox Code Playgroud)

在 commit 之后I,我们通过集成进行变基:

git fetch origin
git rebase integration
Run Code Online (Sandbox Code Playgroud)

所以现在我们有:

A - B - C - E - F    [integration]
 \
  B - C - E - F - G - H - I          [feature]
Run Code Online (Sandbox Code Playgroud)

然后说我们将功能分支合并到集成中,然后我们有:

A - B - C - E - F - G - H - I   [integration]
 \
  B - C - E - F - G - H - I          [feature]
Run Code Online (Sandbox Code Playgroud)

(我认为这是对的),但我看不出这与根本不重新定位有什么不同?

tor*_*rek 6

你的图误导了你。

记住以下几点:

  • 提交的“真实名称”是其哈希 ID。
  • 每个提交都存储其父提交的哈希 ID,或者对于合并提交,存储其所有父提交的哈希 ID。
  • 根本无法更改提交,但可以将提交复制到新的替换中。

如果有帮助,请将提交视为大而坚固的东西:例如,构成建筑物的砖块和横梁。(就像超大的乐高积木一样,每个积木都有一些连接到其他积木的连接器,我们将积木连接在一起形成链。这些连接通过哈希 ID:它们来自子提交并指向父提交。 )

另一方面,分支名称是非常轻量级的项目。它们就像粘滞便笺,您可以在提交上贴上,然后再剥离并贴在不同的提交上。

所以如果你有这个:

A <-B <-C <-E <-F   <-- integration
 \
  G <-H <-I   <-- feature
Run Code Online (Sandbox Code Playgroud)

您无法获得第二张图片,因为现有提交G记录A的哈希 ID。你不能有一个GF作为其父。如果可能的话,您也不应该绘制两次提交:提交是独一无二的,只有一次提交G

git cherry-pick复制提交的构建块

通常,我们发现自己处于这样的情况下,我们有这样的提交是G可以的,但如果它有点不同,我们会更喜欢它。我们希望能有新的副本的喜欢 G,但F作为其母公司,并有不同的源代码树的快照比原来的G太。让我们称新提交G'以区别于它,G但提醒我们它很像 G. 我们希望之间的差异 F,并G'以相同的区别 AG,从而占因提交的需要作出改变B-C-E-F了。所以我们想要的是一个看起来像这样的提交图:

              G'  <-- new-and-improved-feature
             /
A--B--C--E--F   <-- integration
 \
  G--H--I   <-- feature
Run Code Online (Sandbox Code Playgroud)

如果我们然后将提交复制H新的和改进的 H',并复制I新的和改进的 I',我们会得到:

              G'-H'-I'  <-- new-and-improved-feature
             /
A--B--C--E--F   <-- integration
 \
  G--H--I   <-- feature
Run Code Online (Sandbox Code Playgroud)

git reset 移动标签

什么git reset——嗯,它可以做的几件事之一,但这就是我们现在用它做的——是移动分支名称粘性标签。

有一个粘性标签,我们在上面写了字feature。该粘性标签立即附加到提交I。但是,我们只用git cherry-pick3次,复制G-H-I序列的G'-H'-I'序列,在我们的新的和改进的设置。

如果我们现在让 Git 将标签feature从 commit 上剥离I,并将其粘贴到 commit 上I',我们会得到:

              G'-H'-I'  <-- feature (HEAD), new-and-improved-feature
             /
A--B--C--E--F   <-- integration
 \
  G--H--I   <-- ORIG_HEAD
Run Code Online (Sandbox Code Playgroud)

要做到这一点,我们运行:git checkout feature; git reset new-and-improved-feature

git reset命令设置此特殊名称ORIG_HEAD以记住feature曾经去过的地方。现在标签feature附加到 commit I',但有多种方法可以找到I,包括这个ORIG_HEAD技巧。

(我们不再需要“新的和改进的功能”标签,所以我们现在可以删除它。)

请注意,没有提交已更改。原件G仍在存储库中。运行git log ORIG_HEAD,我们仍然可以看到它,至少在我们执行另一个ORIG_HEAD用于记住其他提交的Git 命令之前。我们会看到I,然后H,然后G。我们也可以使用引用日志feature寻找提交的哈希ID GHI。只要我们有哈希 ID 或哈希 ID 的名称,我们就可以找到提交。(这些 reflog 条目最终会过期——它们有一个日期戳,一三个月后,Git 删除了 reflog 条目。)

feature但是,如果我们使用 name ,我们会找到新的副本而不是原始提交。这使得提交看起来好像发生了变化,只要我们不密切注意并注意到实际上这些都是新的提交。

底线是这样的:在我们复制 commits 之后,如果我们git reset习惯于放弃原件而支持新的和改进的副本,我们将只看到新的和改进的副本,我们可以提交一样行事已经变化。提交没有改变,如果有人真的仔细观察,他们会发现我们的秘密,但如果其他人从来不知道原件,他们就不会发现这些是 廉价的仿冒品 改进副本。

git rebase = 精选加重置

这使我们得出结论:git rebase从根本上说git cherry-pick是一些提交,然后是 git reset. 也就是说,我们首先提交复制到新的和——我们希望——改进的版本;然后我们git reset试图欺骗每个人使用改进的提交来代替原始提交。

谁还有原版就不会被忽悠了! 如果其他人——某个其他 Git 存储库——仍然拥有原始提交,我们必须说服他们也切换到新的改进提交。但是如果我们是唯一可以访问提交的人,我们只需要自欺欺人,这可能更容易。