在过滤器分支之后删除无用的合并(没有任何'non-mainline'提交的合并)



我已经执行了一个git filter-branch --index-filter 'git rm --cached --ignore-unmatched badfiles/ badfiles2/' --prune-empty(根据这里)删除一堆文件,以准备将剩余的文件移动到另一个存储库。--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(+)

所以我想摆脱"无用"合并,它们都是"空"合并(没有合并更改),但我想保留与顶部的"空">KEEP 合并相关的历史记录/分组,它将这些提交分组到一个"变更集"中。

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

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

我已经尝试了删除"空"合并的解决方案(像这样),但这些删除了所有空合并,我想保留示例中显示的"有用"空合并......

据我所知,"无用"的空合并不包含任何不在历史记录中左侧/顶部的提交。有没有办法干净地过滤掉这些?我想我什至不知道如何描述/定义这些......

请注意,给定的示例有意简单。对于它的价值,在历史的后面,这个回购看起来像这样,所有这些都我想修剪:

*   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
| |     
| |/ / / /  

这是问题的核心:

我想我什至不知道如何描述/定义这些......

Git 的核心是一个图形处理程序,旨在构建 DAG(有向无环图),其中图中的每个节点都是一个提交。 每个提交都携带源快照作为一种数据有效负载这一事实与此过程无关。 (当然,这与 Git 最终有用高度相关。

你想要采用现有的(筛选后)DAG 并生成不同的 DAG。 需要定义一种算法,用于将不需要的 DAG 转换为所需的 DAG。 您不一定必须使用git filter-branch来实现转换,但如果您打算这样做,则必须将此转换进一步细化为一种与"到目前为止"知识一起工作的算法:它可以看到当前提交哈希 ID,即 filter-branch 建议复制的提交。 那是在$GIT_COMMIT. 它可以读取该提交(使用 Git 管道命令),并且可以使用 shell 函数map从其他已复制的提交中找到映射,如git filter-branch文档中所述。

我也不知道如何定义"有用的合并"。 不过,我认为最明显的算法是(至少直接)不适合过滤器分支的算法:它是一种迭代松弛算法,在这种算法中,你从完整的图开始,反复提取合并节点,将它们的父节点连接到他们的子节点,只要这些节点没有用。 (定义无用仍然由您决定。 最后,您有一个要保留的节点和要删除的节点的列表。该列表对于您为 filter-branch 编写的筛选器很有用:您现在将使用像往常一样运行git commit-tree--commit-filter运行git filter-branch,或者按照文档中的说明提供skip_commit函数运行。 "保留"或"跳过"的决定基于您使用放松算法生成的列表。

好的,我不认为这是完美的,但它确实解决了这种特殊情况下的问题; 在某些情况下,它并没有尽可能多地清理,但如果有人感兴趣,这是一个步骤:

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) 第二个父项 ($GIT_COMMIT^2) 包含第一个父项 ($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(+)

它通过SQUASHED-PR保留所有内容(请注意,提交 ID4182dac和父项被保留,因为他们的历史记录没有改变)。它决定USELESS-A应该坚持使用b/c,它的第二个父级(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",否则这些分支将包含在"$@"中。如果您有多个父项(或者更确切地说,如果您的提交有多个父项),则必须对此进行调整以考虑这一点;应该不会太难,但我现在不是为了一个假设而修复它 - 你可能想选择特定的父母来放弃,idk。
  • 如果在合并到USELESS-B之前USELESS-A之后有一个"有用"的提交(可以说这不会是无用的),USELESS-A不会被修剪/删除,所以你仍然会有一些丑陋。
  • 可能还有其他情况,这不起作用或可以改进。如果您发现任何建议,请在评论中添加建议(像往常一样)!

最新更新