简体   繁体   English

包含所有重命名文件提交的筛选器分支

[英]filter-branch that includes all commits of renamed files

Using filter-branch like explained here it's possible to split out some subdirectory to a new repository. 使用此处说明的filter-branch ,可以将某些子目录拆分到新的存储库中。 However the suggested filter will only keep commits where the files in the directory had the same name/path as they do now. 但是,建议的过滤器将仅保留目录中文件具有与现在相同名称/路径的提交。

git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME

I need a filter that will keep the same commits for each file as when doing gitk --follow FILE-NAME . 我需要一个过滤器,该过滤器将使每个文件的提交与执行gitk --follow FILE-NAME时的提交相同。

Essentially I need a filter that will keep commits for both the current filename/path and older filenames/paths for each file in the directory. 本质上,我需要一个过滤器,该过滤器将保留目录中每个文件的当前文件名/路径和旧文件名/路径的提交。

I tried: 我试过了:

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- FOLDER-NAME' --prune-empty -- --all

but it did the same as --subdirectory-filter . 但是它和--subdirectory-filter

You will have to write your own, fancy (perhaps very fancy), filter. 您将必须编写自己的,花哨的(也许非常花哨的)过滤器。

By this I don't mean modify git filter-branch to add a new --EJS-filter . 我的意思不是修改git filter-branch来添加一个新的--EJS-filter That would be one way to do it, but would require that you get ridiculously fancy. 是做到这一点的一种方式,但会要求你可笑幻想。 :-) Instead, what I mean is this: :-)相反,我的意思是:

  • Find the name(s) you want to preserve in particular commit range(s). 找到您要保留的特定提交范围内的名称。

    This might be relatively easy: perhaps from commits "after" a123456 onward, you keep all files named newdirname/* , and from commits "before" a123456 , including a123456 itself, you keep all files named olddirname/* . 这可能相对容易:从a123456之后的“提交”开始,保留所有名为newdirname/*文件,从a123456 “ before” a123456 (包括a123456本身),则保留所有名为olddirname/*文件。

    It might even be extremely easy: perhaps in commits after a123456 , there are no files named olddirname/* , and in commits up to that point, there are no files named newdirname/* . 这甚至可能非常容易:也许在a123456之后的a123456没有名为olddirname/*文件 ,并且在此之前的提交中,没有名为newdirname/*文件。 In this case, your filter devolves to: Retain all files named olddirname/* or newdirname/* ; 在这种情况下,您的过滤器olddirname/*为: 保留所有名为olddirname/*newdirname/* remove all other files. 删除所有其他文件。

    If it's not that easy, well, then it's not that easy. 如果不是那么容易,那么那就不是那么容易。

  • Now that you have identified which files to keep, write a --tree-filter (slow, but very easy to write) or --index-filter (much faster, but harder to write) that retains the files you want and deletes the files you don't want. 现在,您已经确定了要保留的文件,编写一个--tree-filter (很慢,但是很容易编写)或--index-filter (要快得多,但是很难编写),以保留您想要的文件并删除不需要的文件。

Here, you can (and maybe must) make use of the way that git filter-branch operates. 在这里,您可以(也许必须)利用git filter-branch操作方式。 When you run git filter-branch , what Git does is: 当您运行git filter-branch ,Git的作用是:

  1. Enumerate every commit reachable from the branch(es) you're filtering, according to whatever gitrevisions style criteria you specify. 根据您指定的任何gitrevisions样式标准,枚举您要过滤的分支中可到达的每个提交。 With just --all at the end, enumerate every commit reachable from every branch. 最后,仅--all枚举每个分支可到达的每个提交。

  2. Put that list of commits into "reverse topological order", ie, start with the root commit (the very first commit ever made) and then list its immediate children, then their children, and so on. 将提交列表放入“反向拓扑顺序”,即从提交(有史以来的第一个提交)开始,然后列出其直接子代,然后是其子代,依此类推。 Hence for a commit chain that looks in part like this: 因此,对于部分看起来像这样的提交链:

      G--H <-- branch1 / ...--E--F \\ I--J <-- branch2 

    the list would end with EF followed by either GHIJ or IJGH . 该列表将以EF结尾,然后是GHIJIJGH

  3. For every commit in the list, do these steps (more or less): 对于列表中的每个提交,执行以下步骤(或多或少):

    • check out the commit by its hash ID, saved temporarily in the variable $GIT_COMMIT ; 通过其哈希ID来检查提交,该哈希ID临时保存在变量$GIT_COMMIT
    • run each -- whatever -filter ; 每次运行-- whatever -filter ;
    • make a new commit from whatever's left; 从剩下的任何东西重新提交;
    • update a map: "old $GIT_COMMIT becomes new hash ID just made". 更新地图:“旧的$GIT_COMMIT变成刚创建的新哈希ID”。

    In other words, git filter-branch simply copies every commit. 换句话说, git filter-branch复制每个提交。 With --prune-empty it skips the "make a new commit" step if the new commit would match its parent commit. 如果新提交与它的父提交匹配,则使用--prune-empty可以跳过“提交新提交”步骤。

    In any case, the new commit's parent commit(s) is/are the mapped IDs. 在任何情况下,新提交的父提交都是映射的 ID。 That is, if we start from original commit A , which is a root commit and has no parents, we make a new commit A' : 也就是说,如果我们从原始提交A开始,它是一个根提交并且没有父母,我们将创建一个新的提交A'

     A--B--C--... A' <-- (just made) 

    Then we copy B , since that's the only child of A . 然后我们复制B ,因为那是A的唯一孩子。 When we—through git filter-branch —make the new commit B' we make B' 's parent be A' rather than A : 当我们通过git filter-branch进行新的提交B'我们使B'的父对象为A'而不是A

     A--B--C--... A'-B' <-- (just made) 

    When we make C' from C , we'll have B' as its parent. 当我们从C制作C'时,我们将B'作为其父代。 Or, if—due to the --prune-empty rule—we don't actually make B' after all, we'll set C' 's parent to A' . 或者,如果-由于--prune-empty规则,我们实际上并不 B'毕竟,我们将设置C'的家长A'

What this means is that you can use $GIT_COMMIT , if necessary, to decide which file name(s) you wish to keep. 这意味着您可以使用$GIT_COMMIT来确定要保留的文件名。 You can test it against all the existing commits in the repository, or build your own map of names to keep based on commit hashes, or whatever you like. 您可以针对存储库中所有现有的提交对其进行测试,也可以构建自己的名称映射以基于提交哈希或任何您喜欢的名称进行保留。

Note that in general, since you are dealing with a directed acyclic graph (DAG) of commit hashes, the test you usually want to use to implement these things is "is ancestor". 请注意,通常,由于您要处理提交哈希的有向无环图(DAG),因此通常要用于实现这些目的的测试是“祖先”。 If commit A is an ancestor of commit B —more precisely, if A ≼ B , so that you allow for A = B —then you have a "before" situation. 如果提交A是提交B的祖先(更确切地说,如果A≼B ,那么您允许A = B) ,那么您将处于“之前”的情况。 This is what I described above: commits "before and including" a123456 use an older name, while subsequent commits use a newer one. 这就是我在上面描述的内容:“在a123456之前和包含”的a123456使用较旧的名称,而后续的提交使用较新的名称。 The git merge-base --is-ancestor AB command performs this A ≼ B test, with its result delivered as a shell command exit status: git merge-base --is-ancestor AB命令执行此A≼B测试,其结果作为shell命令退出状态传递:

if git-merge-base --is-ancestor $GIT_COMMIT a123456; then ...; else ...; fi

tests whether $GIT_COMMITa123456 . 测试是否$GIT_COMMITa123456

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

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