据我所知http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html,重新分支的分支被“移动”到另一个分支。
但是,我在测试中看到的情况表明,重新分支的提交仍然保留在历史记录中,因此它们实际上是重复的。
也许我遗漏了某些东西,或者完全不了解rebase的目的或两者兼而有之。
如果我看到的是预期的行为,那为什么会这样呢?
答案 0 :(得分:5)
简而言之,rebase
是一种将提交从树的一部分应用到不同起点的方法。它可以复制这些更改,但不会移动它们。
请记住,git提交是不可变的 - 一旦有东西有哈希值,它就永远不会改变。这意味着当你在另一个变化之上重新定义一些变化时,散列必然是不同的,所以git将保持旧的和新的变化。
但是,如果没有分支名称指向旧提交(在您的示例中为“add file2”),那么几周后git的自动垃圾收集器将从您的存储库中删除旧提交。 (为什么两周?这样,如果你改变主意,你可以从git reflog
检索旧的提交。)一般来说,这是一件好事 - 它会使意外丢失数据变得更加困难 - 但如果该文件非常庞大,您可以使用git prune
和git gc
的组合来删除冗余数据。
答案 1 :(得分:5)
这里有两种不同的现象。
您发布的屏幕截图,来自gitk,仍显示旧提交。这就是gitk的工作方式;如果你通过点击 Ctrl + F5 重新加载,而不仅仅是 F5 (那是文件>重新加载而不是文件>为鼠标用户更新)你会看到旧的提交消失了,因为它不再相关。
Git中有很多的操作可以创建提交。甚至更多的是在文件存储中创建文件或树对象。许多这些对象不再使用这一事实无关紧要。
这有很多优点。在您的示例中,这意味着如果您认为您的rebase是一个坏主意,您的旧提交仍然存在并且可以恢复。甚至还有一个方便的语法:topic@{1}
指的是topic
在上一次移动之前指向的提交;在这里,这将是在rebase之前。
Git对象模型对这类事情很聪明。像这样的额外提交会占用非常一点额外的空间。对于像你所描述的那样的rebase,我希望保留旧分支最多花费几百个字节。
当然,随着时间的推移,这确实会增加。所以git gc
(每隔一段时间由某些命令自动运行)运行git prune
。并且git prune
会查找旧的且不再相关的提交和对象,并为您清除它们。
这并不意味着你的rebase没有起作用,只是因为rebase“移动”提交的想法是简化。 rebase实际上做的是将每个提交及其父级之间的差异应用于新分支,并为旧分支上的每个提交创建一个具有这些差异的新提交。然后它会更新分支,这样,如果查看分支历史记录,就好像这些提交已被移动一样。
答案 2 :(得分:1)
Rebase是一个重写历史记录的命令。但是感谢git,你的历史不会丢失。你可以回滚,直到git垃圾收集器清除那些悬空提交。
答案 3 :(得分:1)
......重新分支的分支被“移动”到另一个分支。
这是摆弄它的一种方式,但不完全准确。
考虑git repo的最佳方式是将其视为两件事的组合:一个有向的,不可变的提交的非循环图,每个代表一个版本的软件(或者repo中的任何东西),以及分支指针变量集(主等)。
假设您从一个包含三个提交的repo开始,如下所示:
a--> b
\-> c
其中 origin / master 分支指针指向b
, master 分支指针指向c
。您实际上在这里有三个不同版本的软件,a
,b
和c
。
如果您决定将c
重新定位到b
,那么您最终会得到一个如下所示的回购:
a--> b--> c'
\-> c
将主分支指针更改为指向c'
。 “提升此提交”将导致提交c'
被发送到原始仓库,原始仓库的主分支指针被更改为指向c'
,并且您的 origin / master 分支指针被更改为匹配它。
您会注意到c'
是与c
不同的提交,它仍然存在,您现在有四个版本的软件。 c'
提交在道德上对b
对c
所做的a
进行了相同的更改(或者有人希望,假设您已恰当地编辑了任何冲突)。
c
不再有任何指向它的分支指针(实际上,在reflog之外),因此在正常的git操作期间稍后会被垃圾收集。
(Git还会执行一些花哨的压缩技巧来存储所有这些不同[和完整]版本的软件,而不是单独检查它们,但这不是你需要的,甚至不应该,打扰想着。)
在随意的谈话中,我们将此操作称为“更改主分支”,但实际上,您正在做的是创建新分支并更改 master 指从旧分支到新分支。