过滤器分支后删除无用的合并(没有任何“非主线”提交的合并)

时间:2019-03-27 16:30:06

标签: git git-filter-branch git-history-graph

我已经执行了git filter-branch --index-filter 'git rm --cached --ignore-unmatched badfiles/ badfiles2/' --prune-empty(每个here)以删除一堆文件,以准备将其余文件移动到另一个存储库。 --prune-empty消除了所有空提交,但对合并无效,这很有意义。

现在,此特定回购的历史记录看起来非常丑陋,其中包含一堆实际上未添加任何内容的合并,而某些合并只是实际上未添加任何更改的其他合并的合并(在重写的历史记录中;可能在过滤器分支之前是“有用的”。

考虑以下带注释的代码段(由git log --graph --oneline --shortstat生成):

*   575e3b5 Merge pull request #68 from chris/feature # KEEP THIS MERGE!
|\  
| * 5dbc3f1 Actual feature changes
| |  2 files changed, 2 insertions(+), 2 deletions(-)
| * 35abc98 Cleanup/prep
|/  
|    2 files changed, 22 insertions(+), 16 deletions(-)
*   c3b3d86 Merge pull request #46 from org/topic_branch-mods # USELESS-C
|\  
* \   892de05 Merge pull request #47 from org/topic_branch # USELESS-B
|\ \  
| |/  
|/|   
| *   e738d4b Merge branch 'master' into topic_branch # USELESS-A
| |\  
| |/  
|/|   
* | 4182dac CommitMsg #40 #SQUASHED-PR
| |  2 files changed, 15 insertions(+), 6 deletions(-)
* | 3b42762 CommitMsg
|/  
|    2 files changed, 29 insertions(+), 14 deletions(-)
* c4e62ba CommitMsg
|  2 files changed, 39 insertions(+), 16 deletions(-)
* c2bb13f CommitMsg
   4 files changed, 241 insertions(+)

我想将其缩短为(显然,适当地使用不同的ID):

*   575e3b5 Merge pull request #68 from chris/feature # KEEP THIS MERGE!
|\  
| * 5dbc3f1 Actual feature changes
| |  2 files changed, 2 insertions(+), 2 deletions(-)
| * 35abc98 Cleanup/prep
|/  
|    2 files changed, 22 insertions(+), 16 deletions(-) 
* 4182dac CommitMsg #40 #SQUASHED-PR
|  2 files changed, 15 insertions(+), 6 deletions(-)
* 3b42762 CommitMsg
|  2 files changed, 29 insertions(+), 14 deletions(-)
* c4e62ba CommitMsg
|  2 files changed, 39 insertions(+), 16 deletions(-)
* c2bb13f CommitMsg
   4 files changed, 241 insertions(+)

因此,我想摆脱所有“ ”合并(无合并更改)的“ USELESS”合并,但我想保留相关的历史记录/分组KEEP合并在顶部,并且 also-'empty'将这些提交组合到一个'changeset'中。

或者看看传统的简化侧面历史中的另一个例子:

A -- B -- C -- D   ==>  A -- B --- D'
 \----\--/   /                \-E-/
       \----E 

我已经尝试了删除“空”合并的解决方案(例如this),但是那些删除了“ 所有”空合并,并且我想保留“有用”的空合并,如下所示例子...

据我所知,“无用”的空合并不包含历史上并非一直到左/上的所有提交。有办法干净地过滤掉那些东西吗?我想我什至不知道如何描述/定义那些东西...

请注意,给定的示例是故意简单的。对于它的价值,在历史的后面,此仓库看起来像这样,我想对它们进行修剪:

*   3d37e42 Merge pull request #239 from jim/topic-dev
|\  
| *   05eaf9e Merge pull request #7 from org/master
| |\  
| |/  
|/|  
* |   1576482 Merge pull request #193 from john/master
|\ \  
| * \   187100e Merge branch 'master' of github.com:org/repo into master
| |\ \  
| * \ \   067cc55 Merge branch 'master' of github.com:org/repo into master
| |\ \ \  
| * \ \ \   a69e3d2 Merge branch 'master' of github.com:org/repo into master
| |\ \ \ \  
| | |/ / /  
* | | | |   0ce6813 Merge pull request #212 from jim/feature
|\ \ \ \ \  
| | |_|_|/  
| |/| | |   
| * | | |   0f5352e Merge pull request #5 from org/master
| |\ \ \ \  
| |/ / / /  

2 个答案:

答案 0 :(得分:1)

这是问题的核心:

  

我想我什至不知道该如何形容/定义那些...

Git本质上是一个图形操作程序,旨在构建DAG(有向无环图),其中图形中的每个节点都是提交。每个提交都将源快照作为一种数据有效负载的事实与该过程无关。 (当然,这与Git最终成为有用密切相关。)

您要采用现有的(后过滤)DAG并构建其他DAG。您需要定义一种算法,用于将不需要的DAG转换为需要的DAG。您不一定必须使用git filter-branch来完成转换,但是如果您打算这样做,则必须进一步将此转换精炼为可以使用“到目前为止”知识的算法:查看filter-branch建议复制的提交的当前提交哈希ID。在$GIT_COMMIT中。它可以读取该提交(使用Git管道命令),并且可以使用the git filter-branch documentation中所述的shell函数map从其他已复制的提交中查找映射。

我也不太了解如何定义“有用的合并”。不过,我认为最明显的算法不(至少直接适用于)过滤分支:这是一种迭代松弛算法,其中您从完整图开始,反复拔出合并节点,将其父节点连接到它们子节点,只要这些节点没有用处。 (定义无用仍由您决定。)最后,您有一个要保留的节点列表和要删除的节点。 该列表对于您为过滤器分支编写的过滤器很有用:您现在可以使用git filter-branch运行--commit-filter,该git commit-tree可以照常运行skip_commit,或者提供了uint16_t I106_CALL_DECL uCalcHeaderChecksum(SuI106Ch10Header * psuHeader) { int iHdrIdx; uint16_t uHdrSum; uint16_t * aHdr = (uint16_t *)psuHeader; uHdrSum = 0; for (iHdrIdx=0; iHdrIdx<(HEADER_SIZE-2)/2; iHdrIdx++) uHdrSum += aHdr[iHdrIdx]; return uHdrSum; } 功能,如文档中所述。决定“保留”还是“跳过”取决于您使用松弛算法生成的列表。

答案 1 :(得分:0)

好的,我认为这并不完美,但是在这种情况下确实可以解决问题。在某些情况下,它可能不会尽最大可能地进行清理,但是如果有人感兴趣,这是一个步骤:

git filter-branch --commit-filter '
if ! git rev-parse --verify "$GIT_COMMIT^2" 1>/dev/null 2>&1 ||
  [ "$(git log --no-merges "$GIT_COMMIT^2" "^$GIT_COMMIT^1" --oneline | wc -l)" -gt 0 ];
then
  #echo take $GIT_COMMIT >&2
  # Pick one:
  git_commit_non_empty_tree "$@" # Drop empty commits
  #git commit-tree "$@" # Keep empty commits
else
  #echo "breakup $GIT_COMMIT ($*)" >&2
  skip_commit "$1" "$2" "$3" # (quietly) only keep the first parent
fi' -f HEAD

如果1)提交没有第二个父级(git rev-parse返回错误,如果引用的提交($GIT_COMMIT^2)不存在)或2)第二个父级({{1 }})包含第一个父项($GIT_COMMIT^2)不提交的提交(请参阅here),该提交被保留(如果不是空的;如果要保留,请使用$GIT_COMMIT^1空); 如果第二个父级存在并且不添加任何有用的信息,我们将跳过提交,并且有意地仅通过第一个父级-我不确定这是否是“合法” ,但它从历史记录中删除了第二个父级,并且在我的情况下有效...(请参见下面的注意事项)

从下至上:

git commit-tree

它通过* 575e3b5 Merge pull request #68 from chris/feature # KEEP THIS MERGE! |\ | * 5dbc3f1 Actual feature changes | | 2 files changed, 2 insertions(+), 2 deletions(-) | * 35abc98 Cleanup/prep |/ | 2 files changed, 22 insertions(+), 16 deletions(-) * c3b3d86 Merge pull request #46 from org/topic_branch-mods # USELESS-C |\ * \ 892de05 Merge pull request #47 from org/topic_branch # USELESS-B |\ \ | |/ |/| | * e738d4b Merge branch 'master' into topic_branch # USELESS-A | |\ | |/ |/| * | 4182dac CommitMsg #40 #SQUASHED-PR | | 2 files changed, 15 insertions(+), 6 deletions(-) * | 3b42762 CommitMsg |/ | 2 files changed, 29 insertions(+), 14 deletions(-) * c4e62ba CommitMsg | 2 files changed, 39 insertions(+), 16 deletions(-) * c2bb13f CommitMsg 4 files changed, 241 insertions(+) 保留了所有内容(请注意,提交ID为SQUASHED-PR,并且保留了父母的身份,因为他们的历史没有改变)。决定4182dac应该坚持在其第二个父级(USELESS-A)包含提交其第一个父级(4182dac)的情况下,包含,但是,然后查看了c4e62ba,它的第二个父级(包括 USELESS-B没有添加任何有用的内容,因此将其删除(再次包括USELESS-A)。然后USELESS-A就没用了,因此被丢弃了,USELESS-C在第二个父对象中有“有用的东西”,因此被保留了。因此,您结束了:

KEEP

重要警告

  • 这仅适用于简单的历史记录,其中只有两个分支,因为我们明确地通过* 63b4d39 Merge pull request #68 from chris/feature # KEEP THIS MERGE! |\ | * 9a5570d Actual feature changes | | 2 files changed, 2 insertions(+), 2 deletions(-) | * a251317 Cleanup/prep |/ | 2 files changed, 22 insertions(+), 16 deletions(-) * 4182dac CommitMsg #40 #SQUASHED-PR | 2 files changed, 15 insertions(+), 6 deletions(-) * 3b42762 CommitMsg | 2 files changed, 29 insertions(+), 14 deletions(-) * c4e62ba CommitMsg | 2 files changed, 39 insertions(+), 16 deletions(-) * c2bb13f CommitMsg 4 files changed, 241 insertions(+) ,在这种情况下忽略了"$1" "$2" "$3",否则将包括在内在"$4" "$5"中。如果您有多个父母(或者您的 commit 有多个父母),则必须对此进行调整以解决此问题;不应该太难,但是我现在不假想解决它-您可能要选择特定的父母,idk。
  • 如果在"$@"之后有一个“有用的”提交,然后才被合并到USELESS-A(这可能毫无用处),USELESS-B不是被修剪/删除,因此您可能还会感到有些丑陋。
  • 在其他情况下,这可能行不通或可以改善。如果有发现,请在评论中添加建议(照常)!