0

正如我从http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html了解到的,重新定位的分支被“移动”到另一个分支。

但是,我在测试中看到的内容表明,来自重新定位的分支的提交仍保留在历史记录中,因此它们被有效地复制了。

变基之前:

变基后:

也许我遗漏了一些东西,或者完全不理解变基的目的,或者两者兼而有之。

如果我看到的是预期的行为,那为什么会这样?

4

4 回答 4

5

简而言之,rebase这是一种将提交从树的一部分应用到不同起点的方法。它可能会复制这些更改,但不会移动它们。

请记住,git 提交是不可变的——一旦有哈希值,它就永远不会改变。这意味着当您在另一个更改之上重新设置一些更改时,哈希值必然不同,因此 git 将保留旧的和新的。

但是,如果没有分支名称指向旧提交(在您的示例中为“add file2”),那么几周后 git 的自动垃圾收集器将从您的存储库中删除旧提交。(为什么要两周?那样的话,如果你改变主意,你可以从git reflog.您可以使用git prune和的组合git gc来修剪冗余数据。

于 2012-09-17T18:34:53.787 回答
5

这里有两种不同的现象。

  1. 您发布的来自 gitk 的屏幕截图仍然显示旧的提交。这就是 gitk 的工作方式;如果您通过点击Ctrl+F5而不仅仅是F5(对于鼠标用户来说是文件 > 重新加载而不是文件 > 更新)重新加载,您会看到旧的提交消失了,因为它不再相关。

  2. Git 中有很多创建提交的操作。甚至更多的是在文件存储中创建文件或树对象。许多这些对象不再使用的事实是无关紧要的。

    这有很多优点。在您的示例中,这意味着如果您认为 rebase 是一个坏主意,那么您的旧提交仍然存在并且可以恢复。它甚至有一个方便的语法:topic@{1}topic在上次移动之前指向的提交;在这里,这将是在变基之前。

    Git 对象模型在这类事情上很聪明。像这样的额外提交占用很少的额外空间。对于像您所描述的那样的变基,我希望保留旧分支最多会花费几百字节。

    当然,这确实会随着时间的推移而增加。因此git gc(由某些命令每隔一段时间自动运行)运行git prune. 并且git prune会寻找旧的并且不再相关的提交和对象并为你清除它们。

这并不意味着你的变基没有工作,只是变基“移动”提交的想法是一种简化。rebase 实际上所做的是将每个提交与其父级之间的差异应用到新分支,并为旧分支上的每个提交创建一个具有这些差异的新提交。然后它会更新分支,这样,如果您查看分支历史记录,就好像这些提交已被移动。

于 2012-09-18T09:54:42.437 回答
1

Rebase 是一个重写历史的命令。但是多亏了 git,您的历史并没有丢失。您可以回滚,直到 git 垃圾收集器清除那些悬空提交。

于 2012-09-17T18:34:42.570 回答
1

...重新定位的分支被“移动”到另一个分支。

这是一种表达方式,但并不完全准确。

考虑 git repo 的最佳方式是将其视为两件事的组合:一个有向的、非循环的不可变提交图,每个代表您的软件版本(或 repo 中的任何内容),以及一组分支指针变量(master等)。

假设您从一个包含三个提交的 repo 开始,如下所示:

a--> b
 \-> c

其中origin/master分支指针指向,bmaster分支指针指向c。您实际上在这里拥有三个不同版本的软件abc.

如果你决定重新c基于 to b,你最终会得到一个如下所示的 repo:

a--> b--> c'
 \-> c

分支指针更改为指向c'. “推送此提交”将导致提交c'被发送到原始仓库,原始仓库的分支指针被更改为指向c',并且您的原始/主分支指针被更改以匹配它。

你会注意到这c'是一个不同的提交c,它仍然存在,你现在有四个版本的软件。c'提交在道德上对所做的更改进行了相同的更改(或者人们b希望如此,假设您适当地编辑了任何冲突)。ca

c不再有任何指向它的分支指针(实际上,在 reflog 之外),因此在正常 git 操作期间的某个时间点将被垃圾收集。

(与单独检出它们相比,Git 还执行了一些花哨的压缩技巧,以将软件的所有这些不同 [和完整] 版本存储在更少的空间中,但这并不是您真正需要甚至不应该考虑的事情。 )

在闲聊中,我们将此操作称为“更改master分支”,但实际上,您所做的是创建一个新分支并将master所指的内容从旧分支更改为新分支。

于 2012-12-19T06:46:04.613 回答