繁体   English   中英

使用git filter-branch进行特定的提交

[英]Using git filter-branch for specific commits

我正在尝试使用git filter-branch功能来删除最近更新并提交的文件。 我尝试运行以下命令:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- 6f7fda9..HEAD

但是,这只会从master分支中删除文件,而我希望将其从所有分支中删除。

从将6f7fda9提交到HEAD开始,我希望删除文件。 我执行的命令是否错误?

git filter-branch -- --all all在所有分支上运行过滤器。 所以:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- --all

我想从所有分支中删除[文件]

重要的是要意识到分支几乎(但不是完全)无关。 重要的是提交

您实际上无法更改任何现有的提交,并且Git不会尝试。 git filter-branch所做的是复制提交。 也就是说,对于每个要过滤的提交,Git都会将其提取到一个临时工作区中,应用您的过滤器,然后从结果中进行一个新的提交。

如果新提交与原始提交逐位相同,则它将重新使用存储库数据库中的实际基础对象。 如果不是(目的是导致结果为“ not”),则将保留原始提交,而新副本将获得新的,不同的哈希ID。 如果我们使用大写字母代替提交哈希ID,并记住每个提交都存储其父提交的哈希ID,则可以通过以下方式绘制原始文档:

... <-F <-G <-H <-I   <-- master

master这样的分支名称会记住上一次提交的哈希ID。 该提交会记住其父级的哈希ID,并记住另一个父级的另一个哈希ID,依此类推: master让Git 找到提交I ,然后找到提交H ,然后找到提交G ,依此类推。

使用git filter-branch我们告诉Git: 提取commit F并对其进行一些更改,然后重新提交。 如果F没有任何变化,我们将保留实际的哈希ID。 然后,我们将Git提取为commit G并进行一些更改。 这次,也许我们删除了一个敏感文件。 因此,我们进行了一个类似于G的新提交,但有所不同:它获得了一个新的,不同的哈希ID,我们可以将其称为G' 提交G'仍将提交F作为其父级:

...--F--G--H--I   <-- master
      \
       G'

然后,我们提取H并应用过滤器。 即使没有其他改变,我们也需要新的提交指向G' ,所以filter-branch确保了这种情况的发生,因此我们得到了指向G'的提交H' G' 我们为I重复I ,结果是:

...--F--G--H--I   <-- master
      \
       G'-H'-I'

最后一步是git filter-branch重写每个分支名称 该名master现在必须指向犯I' ,其新的和不同的哈希,不破旧恶心I

git filter-branch在处理结束时重写的名称是您在命令行上肯定标识的所有名称。 这部分有些棘手: git filter-branch将适合git rev-list字符串用作其某些参数。 这些可以是正引用(例如master ,也可以是负引用(例如^develop^6f7fda9

否定引用告诉Git: 不要为这些提交打扰 如果使用^6f7fda9跳过提交6f7fda9以及提交之前(按图形显示)的任何内容,则git filter-branch不必花费任何计算机时间来执行该提交。

表达式6f7fda9..HEAD^6f7fda9 HEAD简写,并且HEAD表示当前分支名称 因此,这是对一个分支名称(例如master )的正向引用 ,而对哈希ID的一个负向引用。

您可以使用--branches来命名所有分支名称。 您可以使用--all来命名所有引用 (包括不是分支名称的内容)。 Filter-branch将只重写肯定引用,但会重写所有引用。 请注意这一点,因为这可能会重写refs/stash

重写任何分支,标签,或其他指的是一些名称提交, 包含你希望有这个文件,你会得到的东西,如:

                    tip2   [abandoned]
                   /
...--good--bad--...--tip   [abandoned]
       \
        copied--...--tip'   <-- branch1
                   \
                    tip2'   <-- branch2

如果您重写某些名称,该名称从bad到下(向右)指向任何提交,则这些名称仍将指向具有您要删除的文件的“坏”提交。 (请记住,在我在StackOverflow上执行的这些特定图形绘图中,较早的/父提交位于左侧,稍后的/子提交位于右侧。)

所述的要求是矛盾的。 特别

我希望将其从所有分支中删除。

从将6f7fda9提交到HEAD开始,我希望删除文件。

需要和解。 我怀疑这归结为对提交范围的不正确理解-只是git中的一种。

考虑以下提交图:

x -- 6f7fda9 -- A -- B -- C -- F <--(master)
                 \                        ^(HEAD)
                  D -- E <--(branch)

所以HEADF master那里; 并且有一个分支(显然)是从A创建A (在6f7fda9之后但在HEAD之前)。

现在的问题是,给定该图, 6f7fda9..HEAD是什么意思? 不幸的是,答案并不是很多人凭直觉想到的。

6f7fda9..HEAD是短期的HEAD ^6f7fda9 -意思是“一切从到达HEAD但从可达6f7fda9 ”。 “可达到”是指“提交本身,以及通过遵循父指针找到的所有提交”。 因此,在这种情况下,它表示ABCF 但不是x6f7fda9 (因为它们可以从6f7fda9到达),也不能是DE (因为它们不能从HEAD到达)。

有几种方法可以使filter-branch处理所有分支。 例如你可以

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- --all

但这将包括所有引用(不仅是所有分支); 如果有问题

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- --branches

另一项警告-如果您特别希望在重写6f7fda9之前提交,那么您需要包括一个或多个否定提交引用。 但是,如果您确实打算包括6f7fda9本身, 6f7fda9排除其父项(而不是其自身)。

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- ^6f7fda9^ --branches

如果6f7fda9是合并,则必须为其每个父项列出否定提交引用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM