简体   繁体   English

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

[英]Using git filter-branch for specific commits

I'm trying to use the git filter-branch feature to remove a file that was recently updated and committed. 我正在尝试使用git filter-branch功能来删除最近更新并提交的文件。 I tried running the following command: 我尝试运行以下命令:

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

However this only removes the file from the master branch, and I want it removed from all branches. 但是,这只会从master分支中删除文件,而我希望将其从所有分支中删除。

Starting with commit 6f7fda9 to HEAD I want the file removed. 从将6f7fda9提交到HEAD开始,我希望删除文件。 Is the command I'm running wrong? 我执行的命令是否错误?

git filter-branch -- --all runs the filter on all branches. git filter-branch -- --all all在所有分支上运行过滤器。 So: 所以:

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

I want [the file] removed from all branches 我想从所有分支中删除[文件]

It's important to realize that branches are almost (but not quite) irrelevant. 重要的是要意识到分支几乎(但不是完全)无关。 What matters are the commits . 重要的是提交

You literally cannot change any existing commit, and Git does not try. 您实际上无法更改任何现有的提交,并且Git不会尝试。 What git filter-branch does is that it copies commits. git filter-branch所做的是复制提交。 That is, for each commit to be filtered, Git extracts the original into a temporary work area, applies your filter(s), and then makes a new commit from the result. 也就是说,对于每个要过滤的提交,Git都会将其提取到一个临时工作区中,应用您的过滤器,然后从结果中进行一个新的提交。

If the new commit is bit-for-bit identical to the original commit, it re-uses the actual underlying object in the repository database. 如果新提交与原始提交逐位相同,则它将重新使用存储库数据库中的实际基础对象。 If not—and the purpose is to result in "not"—the original commit remains, while the new copy gets a new, different hash ID. 如果不是(目的是导致结果为“ not”),则将保留原始提交,而新副本将获得新的,不同的哈希ID。 If we use uppercase letters to stand in for commit hash IDs, and remember that each commit stores the hash ID of its parent commit, we can draw the originals this way: 如果我们使用大写字母代替提交哈希ID,并记住每个提交都存储其父提交的哈希ID,则可以通过以下方式绘制原始文档:

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

A branch name like master remembers the hash ID of the last commit. master这样的分支名称会记住上一次提交的哈希ID。 That commit remembers the hash ID of its parent, which remembers another hash ID of another parent, and so on: master lets Git find commit I , which finds commit H , which finds commit G , and so on. 该提交会记住其父级的哈希ID,并记住另一个父级的另一个哈希ID,依此类推: master让Git 找到提交I ,然后找到提交H ,然后找到提交G ,依此类推。

With git filter-branch we tell Git: extract commit F and maybe make some change to it and then re-commit. 使用git filter-branch我们告诉Git: 提取commit F并对其进行一些更改,然后重新提交。 If nothing changes in F , we stick with the actual hash ID. 如果F没有任何变化,我们将保留实际的哈希ID。 Then we have Git extract commit G and make some change. 然后,我们将Git提取为commit G并进行一些更改。 This time, perhaps we remove a sensitive file. 这次,也许我们删除了一个敏感文件。 So we make a new commit that's like G but different: it gets a new, different hash ID, which we can call G' . 因此,我们进行了一个类似于G的新提交,但有所不同:它获得了一个新的,不同的哈希ID,我们可以将其称为G' Commit G' still has commit F as its parent: 提交G'仍将提交F作为其父级:

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

We then extract H and apply the filter. 然后,我们提取H并应用过滤器。 Even if nothing else changes, we need our new commit to point back to G' , so filter-branch ensures that this happens, and therefore we get a commit H' that points back to G' . 即使没有其他改变,我们也需要新的提交指向G' ,所以filter-branch确保了这种情况的发生,因此我们得到了指向G'的提交H' G' We repeat for I and the result is: 我们为I重复I ,结果是:

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

The final step is for git filter-branch to rewrite each of the branch names . 最后一步是git filter-branch重写每个分支名称 The name master must now point to commit I' , with its new and different hash, not to shabby old icky I . 该名master现在必须指向犯I' ,其新的和不同的哈希,不破旧恶心I

The names that git filter-branch rewrites at the end of its processing are all the names you identified positively on the command line. git filter-branch在处理结束时重写的名称是您在命令行上肯定标识的所有名称。 This part is a little tricky: git filter-branch takes, as one / some of its arguments, strings that are suitable for git rev-list . 这部分有些棘手: git filter-branch将适合git rev-list字符串用作其某些参数。 These can be positive references like master , or negative references like ^develop or ^6f7fda9 . 这些可以是正引用(例如master ,也可以是负引用(例如^develop^6f7fda9

A negative reference tells Git: don't bother with these commits . 否定引用告诉Git: 不要为这些提交打扰 If you use ^6f7fda9 to skip commit 6f7fda9 and anything "before" (graph-wise) that commit, git filter-branch will not have to spend any computer-time working on that commit. 如果使用^6f7fda9跳过提交6f7fda9以及提交之前(按图形显示)的任何内容,则git filter-branch不必花费任何计算机时间来执行该提交。

The expression 6f7fda9..HEAD is shorthand for ^6f7fda9 HEAD , and HEAD means the current branch name . 表达式6f7fda9..HEAD^6f7fda9 HEAD简写,并且HEAD表示当前分支名称 So this is a positive reference to one branch name (such as master ), and one negative reference by hash ID. 因此,这是对一个分支名称(例如master )的正向引用 ,而对哈希ID的一个负向引用。

You can name all your branch names with --branches . 您可以使用--branches来命名所有分支名称。 You can name all your references (including things that are not branch names) with --all . 您可以使用--all来命名所有引用 (包括不是分支名称的内容)。 Filter-branch will only rewrite the positive references, but it will rewrite all of them. Filter-branch将只重写肯定引用,但会重写所有引用。 Be a bit careful with this as this can rewrite refs/stash for instance. 请注意这一点,因为这可能会重写refs/stash

When you do rewrite any branch, tag, or other name that refers to some commit that does contain the file you don't want to have, you'll get things like: 重写任何分支,标签,或其他指的是一些名称提交, 包含你希望有这个文件,你会得到的东西,如:

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

If you don't rewrite some name that points anywhere to any of the commits from bad on down (rightward), those names will still point to the "bad" commits that have the file you want to be rid of. 如果您重写某些名称,该名称从bad到下(向右)指向任何提交,则这些名称仍将指向具有您要删除的文件的“坏”提交。 (Remember that in these particular graph drawings that I do on StackOverflow, earlier / parent commits are to the left, later / child commits are to the right.) (请记住,在我在StackOverflow上执行的这些特定图形绘图中,较早的/父提交位于左侧,稍后的/子提交位于右侧。)

Your requirements as stated are contradictory. 所述的要求是矛盾的。 Specifically 特别

I want it removed from all branches. 我希望将其从所有分支中删除。

and

Starting with commit 6f7fda9 to HEAD I want the file removed. 从将6f7fda9提交到HEAD开始,我希望删除文件。

need to be reconciled. 需要和解。 I suspect this comes down to an inaccurate understanding of commit ranges - which are only sort-of a thing in git. 我怀疑这归结为对提交范围的不正确理解-只是git中的一种。

Consider this commit graph: 考虑以下提交图:

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

So HEAD is at master which is at F ; 所以HEADF master那里; and there's a branch which was (apparently) created from A (after 6f7fda9 but before HEAD ). 并且有一个分支(显然)是从A创建A (在6f7fda9之后但在HEAD之前)。

Now the question is, given this graph what does 6f7fda9..HEAD mean? 现在的问题是,给定该图, 6f7fda9..HEAD是什么意思? And unfortunately, the answer isn't what a lot of people intuitively think. 不幸的是,答案并不是很多人凭直觉想到的。

6f7fda9..HEAD is short for HEAD ^6f7fda9 - meaning "everything reachable from HEAD but not reachable from 6f7fda9 ". 6f7fda9..HEAD是短期的HEAD ^6f7fda9 -意思是“一切从到达HEAD但从可达6f7fda9 ”。 "Reachable" means "the commit itself, and any commits you find by following parent pointers". “可达到”是指“提交本身,以及通过遵循父指针找到的所有提交”。 So in this case, it means A , B , C , and F ; 因此,在这种情况下,它表示ABCF but not x or 6f7fda9 (because they're reachable from 6f7fda9 ) and also not D , or E (because they aren't reachable from HEAD ). 但不是x6f7fda9 (因为它们可以从6f7fda9到达),也不能是DE (因为它们不能从HEAD到达)。

There are several ways to get filter-branch to process all the branches. 有几种方法可以使filter-branch处理所有分支。 For example you could 例如你可以

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

But this will include all refs (not just all branches); 但这将包括所有引用(不仅是所有分支); if that's a problem 如果有问题

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

One other caveat - if you specifically don't want commits before 6f7fda9 rewritten, then you need to include one or more negative commit references. 另一项警告-如果您特别希望在重写6f7fda9之前提交,那么您需要包括一个或多个否定提交引用。 But assuming you do intend to include 6f7fda9 itself, you'd exclude its parent (not itself). 但是,如果您确实打算包括6f7fda9本身, 6f7fda9排除其父项(而不是其自身)。

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

If 6f7fda9 is a merge, you'd have to list negative commit references for each of its parents. 如果6f7fda9是合并,则必须为其每个父项列出否定提交引用。

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

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