是否可以使用交互式git rebase从历史记录中删除文件?

时间:2015-09-08 09:59:18

标签: git git-rebase git-filter-branch

我的本​​地存储库中有一个旧提交,它添加了一些文件,包括一个名为" unwanted.txt"的文件。在后续提交中,该文件与其他文件一起被修改。是否可以完全删除文件" unwanted.txt"从历史使用交互式git rebase? 我知道使用" git filter-branch"可以实现这一点,但是因为我正在学习git而且我想要了解" git rebase -i"的全部潜力。 ,我想知道这个命令是否可以用于这样的操作。

2 个答案:

答案 0 :(得分:2)

您应该可以通过编辑违规提交(在rebase待办事项列表中提交前面的eedit)来执行此操作,然后只删除文件像这样:

git rm unwanted.txt
git commit --amend
git rebase --continue

这可能会在以后的提交中为您提供更改文件的冲突,但是应该通过再次删除文件并继续使用rebase来解决这个问题。

修改: 您很可能还必须确保没有分支指向任何不需要的文件仍然存在的提交,并运行git gc以清除repo中的未连接blob。如果它不是与其他任何人共享的纯粹私人回购,那么这不应该是一个问题。

答案 1 :(得分:2)

理论上这是可能的,但在实践中它通常太痛苦了。

rebase和filter-branch中的方法相同。如果您意识到交互式变基的所有内容,git cherry-pick类固醇,就可能会有所帮助;而git filter-branch只是一个跨多个分支的自动化的复杂变基,并且具有合并保留。

与git一样,它主要归结为操作提交图,并添加看起来像现有提交但有更改的新提交 - 在这种情况下,树附加到这些提交。 (只要一个提交不同,它就会得到一个不同的SHA-1,这意味着所有后续提交也必须改变,以列出作为新图形弹出的不同SHA-1增长。)

要了解它的工作原理,请先绘制提交图。您需要一个相当完整的图表,具体取决于您必须停止查看unwanted.txt文件的距离。但我只是绘制一个简单的图形,只有一个命名分支,master

I - A - B - C - F   <-- master
      \       /
        D - E

这里I是初始提交;为简单起见,我们说它有不需要的文件。让我们说这个文件是在提交A中引入的,并在CE中进行了修改。

我们需要做的是:

  1. 复制所有提交I(保留提交作者和提交者,以及日期戳等),同时删除不需要的文件,即根据需要更改附加到I的源树。这只是让我们提交I,所以我们保留原来的SHA-1。
  2. 在删除不需要的文件时复制所有提交A。这会导致新的不同提交A',因为我们将A树更改为已删除文件的新树。我们得到一个新的SHA-1加密校验和,因为新的提交与旧的不同。因此,我们在地图中保存一个条目,说明旧提交A替换为新提交A'
  3. 在删除不需要的文件时复制所有提交B。这会更改树(请记住,每次提交都有完整的整个源快照,因此不需要的文件位于原始B)中。创建一个新的提交B',其中包含已更改的树已将提交A'作为其父ID。
  4. 在删除不需要的文件时复制所有提交C,从而生成C'
  5. 使用我们的更改复制所有提交D,生成D'。 (请注意,我们无法复制F,直到我们复制了图表中的所有前任内容,在本例中为CE。)
  6. 使用我们的更改复制所有提交E
  7. 使用我们的更改复制所有提交F。新提交F'C'E'作为其双亲;我们使用我们一直在构建的SHA-1映射找到这些。
  8. 最后,将master更改为指向提交F',放弃原始提交F
  9. 这会生成如下图:

        A - B - C - F    [abandoned]
       /  \       /
      /     D - E
     /
    I - A' - B' - C' - F'   <-- master
           \         /
             D' - E'
    

    --preserve-merges的交互式rebase可以处理这种特殊情况。但是,如果有多个分支,则必须根据需要使用--onto小心地重新添加其他分支以使用新提交,这些提交必须与旧提交相匹配,最有可能使用您手动构建的SHA-1映射文件。

    还有一个额外的皱纹,默认情况下git commit拒绝让#34;空的&#34;提交,其中&#34;空&#34;被定义为&#34;具有与前一次提交相同的树&#34; (并不是合并)。 filter-branch脚本会自动为您处理,如果您选择删除空提交,则会将多个新提交映射到单个旧提交(只有 修改不需要的文件的提交在前一个和新的时候变为空提交都放弃不需要的文件)。在保留合并时,交互式rebase不能很好地处理这个问题,因此会带来更多痛苦。

    还有一些其他微妙的差异:例如,当rebase&#34;放弃&#34;一连串的提交,他们仍然在&#34; reflog&#34;对于已经重新命名的分支,以及HEAD的reflog。 filter-branch脚本使用不同的方法:它将所有引用复制到子名称空间refs/original/。当你想要清除旧的,废弃的提交时,这一切都很重要:通过rebase,你&#34; expire&#34;旧的引用,但使用filter-branch,你强行删除原件。