![](/img/trans.png)
[英]Using git filter-branch to remove commits by their commit message
[英]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)
所以HEAD
在F
master
那里; 并且有一个分支(显然)是从A
创建A
(在6f7fda9
之后但在HEAD
之前)。
现在的问题是,给定该图, 6f7fda9..HEAD
是什么意思? 不幸的是,答案并不是很多人凭直觉想到的。
6f7fda9..HEAD
是短期的HEAD ^6f7fda9
-意思是“一切从到达HEAD
但从不可达6f7fda9
”。 “可达到”是指“提交本身,以及通过遵循父指针找到的所有提交”。 因此,在这种情况下,它表示A
, B
, C
和F
; 但不是x
或6f7fda9
(因为它们可以从6f7fda9
到达),也不能是D
或E
(因为它们不能从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.