【问题标题】:Counter-intuitive git rm --cached flag behavior when invoked via git filter-branch (retroactive .gitignore)通过 git filter-branch 调用时反直觉的 git rm --cached 标志行为(追溯 .gitignore)
【发布时间】:2019-12-16 12:12:25
【问题描述】:

问题

--cached 标志在通过git filter-branch --index-filter 运行git rm 时有何不同?

git filter-branch --index-filter 'git rm --cached *.ext'  
git filter-branch --index-filter 'git rm *.ext'

问题

当我运行git rm --cached *.ext 时,所有*.ext 文件都未跟踪但没有从工作目录中删除(据我了解,这是--cached 标志的目的)

但是,当我尝试追溯运行该命令时,就像

git filter-branch --index-filter 'git rm --cached *.ext'

所有历史 *.ext 文件均未跟踪并且现有 *.ext 文件从工作目录中删除。

为什么?

没有--cached有什么区别

理论

由于该命令在所有提交上运行,每次运行都没有“工作目录”,因此它还将 HEAD/当前工作目录视为没有工作目录的独立提交。

【问题讨论】:

    标签: git


    【解决方案1】:

    您的理论大部分是正确的,只是在最后有点偏离。 git filter-branch 不使用标准索引,也不使用您的工作树。当它调用您的每个过滤器时,它会在一个临时目录中运行,该目录在文件系统中的位置取决于您是否提供了 -d <em>directory</em> 选项。1 如果没有可用的工作树,所有索引过滤器git rm 操作应该使用--cached2 Filter-branch 还在临时目录中创建一个临时索引。这个临时索引在过滤的每一步都存在:它用于提取索引过滤器的提交,然后在索引过滤器修改它后进行新的提交。

    完成所有过滤操作后,filter-branch的最后几个步骤是:

    1. 更新所有要修改的引用:过滤的分支和任何标签,如果你使用了--tag-name-filter。例如,master 之前可能已经识别了提交 a123456...。如果现在应该使用新的提交 b789abc... 而不是 a123456,则 Git 必须重写 refs/heads/master 以便它现在识别提交 b789abc...

    2. 如果您在非裸存储库中(过滤器分支 可以 用于裸存储库),则有效地git checkout 新提交而不是旧提交。这会更新您的索引和工作树,以便您的索引和工作树保持“干净”。3 实际操作是 git read-tree

      if [ "$(is_bare_repository)" = false ]; then
              git read-tree -u -m HEAD || exit
      fi
      

    最后一步,即有效签出,会删除您的 *.ext 文件。

    您的当前索引将它们显示为已跟踪,它们是;它们在您当前的commit 中,即(例如)a123456...。您的新目标提交b789abc... 缺少*.ext 文件,因为您使用--index-filter 过滤掉了它们。所以要从当前提交移动到新提交,正确的操作是删除文件。

    解决问题:

    运行git rm --cached '*.ext' 您运行带有--index-filter 选项的filter-branch 命令并提交。这样,您的 current 提交(将不再是 a123456...不会拥有这些文件,并且您的索引不会拥有这些文件,因此它们将只是在当前提交和新提交中都是未跟踪的文件,并且不会被触及。


    1Filter-branch 首先从您的-d 选项设置$tempdir,或默认设置.git-rewrite。然后它创建$tempdir/t,如果需要也创建$tempdir;将位置更改为其中;并将 Git 工作树设置为 .。此$tempdir/t 目录保持为空,除非您使用--tree-filter 选项,或者在任何过滤器中尝试以某种方式操作当前目录。

    2如果没有--cachedgit rm 会尝试从$GIT_WORK_TREE 中删除文件,即$tempdir/t。 Filter-branch 自己的状态文件从这里位于../,即$tempdir,因此它们通常应该是安全的,但尝试删除不应该存在的东西仍然不是一个好主意:如果仅此而已,它浪费了计算时间。使用--cached 可以防止git rm 尝试它。

    3这里的“干净”是一种定义模糊的状态,无论如何您都可能认识它。如果需要,精确的定义在git-sh-setup.sh 中,特别是在函数require_clean_work_tree 中。如果在非裸存储库上运行,Filter-branch 在开始过滤之前调用它。

    【讨论】:

    • 感谢您的出色回答!看来,在我的情况下,(我不希望额外的“提交”来删除历史文件,但我也想将文件保留在工作树中),我应该等到我准备好进行另一个实质性提交,在最终提交之前运行git rm --cached '*.ext',然后运行git filter-tree --index-filter...命令?
    • 可以等待,但这很容易忘记。我建议改为:继续进行额外的提交。执行过滤器分支并确保您对结果感到满意。然后,使用git reset --hard HEAD^ 删除您所做的额外提交(以及 Git 复制的),如果 Git 复制了它。请注意,--filter-branch 有一个 --prune-empty 选项,如果它与前一个匹配,则跳过提交,这对于这些特殊情况很有用,只要您没有想要保留的故意“空”提交。
    • 树过滤器的行为是否与 --cached 不同?在这种情况下,该标志似乎没有任何作用。
    • Here is another question I've asked,如果你有兴趣看看。
    • tree-filter 非常不同,因为它会丢弃您对索引所做的任何更改,并严格根据您留在临时目录中的任何内容构建新的提交。
    猜你喜欢
    • 2016-06-05
    • 2016-01-07
    • 2011-08-13
    • 1970-01-01
    • 2012-09-15
    • 2023-03-16
    • 2020-07-22
    • 2021-04-02
    • 1970-01-01
    相关资源
    最近更新 更多