git rebase 和 git reset 之间的区别

Difference between git rebase and git reset

假设我有这个:

A - B - C - E - F    [integration]
 \
  G - H - I          [feature]

提交 I 后,我们通过集成重新设置基准:

git fetch origin
git rebase integration

所以现在我们有:

A - B - C - E - F    [integration]
 \
  B - C - E - F - G - H - I          [feature]

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

A - B - C - E - F - G - H - I   [integration]
 \
  B - C - E - F - G - H - I          [feature]

(我认为这是对的),但我看不出这与根本不进行变基有何不同?

基本上,我所了解和发现的是,如果您想删除提交历史记录,则只能执行变基。 Git 重置将更改参考文件中的参考历史记录。

我找到了关于堆栈溢出两者之间区别的很好的解释:here

我希望这有助于阐明在何种情况下哪个选项是工作的最佳工具。

你的画误导了你。

记住这些事情:

  • 提交的 "true name" 是其哈希 ID。
  • 每个提交都存储其父提交的哈希 ID,或者对于合并提交,存储其所有父提交的哈希 ID。
  • 任何提交都不能更改,但提交可以复制到新的替换项。

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

另一方面,

Branch names 是非常轻的项目。它们就像便利贴,您可以在提交上贴上标签,然后撕下并贴在不同的提交上。

所以如果你有这个:

A <-B <-C <-E <-F   <-- integration
 \
  G <-H <-I   <-- feature

您无法获取第二张图片,因为现有提交 G 记录了 A 的哈希 ID。您不能 拥有一个以 F 作为父级的 G。您还不应该 如果可能的话,不要绘制两次提交:提交是独一无二的,只有一个提交 G.

git cherry-pick复制 提交

的基石

通常,我们发现自己处于这样一种情况,即我们有一个像 G 这样的提交,它是好的,但如果它有点不同,我们会更喜欢它。我们想要一个 类似于 G 的新副本,但其父级为 F,并且具有与原始 [=14= 不同的源树快照] 也。让我们将新提交称为 G' 以将其与 G 区分开来,但提醒我们它有很多 G。我们希望 FG' 之间的 差异与 A 之间的差异相同G,因此也考虑了由于提交 B-C-E-F 而需要的任何更改。所以我们想要的是一个看起来像这样的提交图:

              G'  <-- new-and-improved-feature
             /
A--B--C--E--F   <-- integration
 \
  G--H--I   <-- feature

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

              G'-H'-I'  <-- new-and-improved-feature
             /
A--B--C--E--F   <-- integration
 \
  G--H--I   <-- feature

git reset 移动标签

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

有一张贴纸,上面写着feature。该粘性标签现在已附加到提交 I 上。但我们只是使用了 git cherry-pick 三次,以 G-H-I 序列复制到 G'-H'-I' 序列,在我们新的和改进的设置中。

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

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

为了实现这一目标,我们 运行:git checkout feature; git reset new-and-improved-feature

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

(我们不再需要 "new and improved feature" 标签,所以我们现在可以删除它。)

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

如果我们使用名称 feature,但是,我们 找到新的副本 而不是原始提交。这使得 看起来 提交已经改变,只要我们不密切注意并注意到实际上,这些是 new 提交.

底线是这样的:在我们复制提交后,如果我们使用git reset放弃原件以支持新的和改进的副本,我们只会看到新的和改进的副本,我们可以表现得像 提交已更改。提交没有改变,如果有人仔细观察,他们会发现我们的秘密,但是 如果其他人不知道原件,他们就不会发现这些是 廉价仿冒品 改良版。

git rebase = cherry-pick 加重置

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

任何仍然拥有原件的人都不会被愚弄!如果其他人 - 其他 Git 存储库 - 仍然拥有原始提交,我们必须说服 他们 也切换到新的改进提交。但如果我们是唯一有权访问提交的人,我们只需要自欺欺人,这可能更容易。