在filter-branch之后删除无用的合并(没有任何'非主线'提交的合并)

问题描述 投票:0回答:2

我已经执行了一个git filter-branch --index-filter 'git rm --cached --ignore-unmatched badfiles/ badfiles2/' --prune-empty(每个here)来删除一堆文件,准备将剩余的文件移动到另一个存储库。 --prune-empty摆脱任何产生的空提交,但它不会对合并起作用,这是有道理的。

现在这个特定仓库的历史看起来非常丑陋,有一堆实际上并没有添加任何东西的合并,有些合并只是其他合并的合并,实际上没有添加任何变化(在重写的历史记录中;它们可能是在filter-branch之前'有用')。

考虑这个带注释的片段(使用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'合并,这些合并都是'空'合并(没有合并更改),但我想保留与'-empty'KEEP合并相关的历史/分组top,将那些提交组合成一个'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
| |\ \ \ \  
| |/ / / /  
git git-filter-branch git-history-graph
2个回答
1
投票

这是问题的核心:

我想我甚至不知道如何描述/定义那些......

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

您希望采用现有(后过滤)DAG并构建不同的DAG。您需要定义一个算法,用于将不需要的DAG转换为所需的DAG。你不一定要使用git filter-branch来实现转换,但是如果你打算这样做,你将不得不进一步改进这个转换为一个与“迄今为止”知识一起工作的算法:它可以看到当前的提交过滤分支提议复制的提交的哈希ID。这是在$GIT_COMMIT。它可以读取该提交(使用Git管道命令),它可以使用shell函数map从其他已经复制的提交中找到映射,如the git filter-branch documentation中所述。

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


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)第二个父级($GIT_COMMIT^2)包含第一个父级($GIT_COMMIT^1)不提交的提交(请参阅here),保留提交(如果它不是空的;如果你想保留空,请使用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(注意提交id 4182dac和父母保留,因为他们的历史没有改变)。它决定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(+)

Important Caveats

  • 这仅适用于只有两个分支的简单历史,因为我们在这种情况下明确传递"$1" "$2" "$3"而不使用"$4" "$5",否则将包含在"$@"中。如果您有多个父母(或者更确切地说,如果您的提交有多个父母),您必须对此进行调整以解决这个问题;不应该太难,但我现在不是为了假设而修理它 - 你可能想选择特定的父母来放弃,idk。
  • 如果在USELESS-A合并到USELESS-B之后有一个“有用的”提交(当然这可能不会没用),USELESS-A将不会被修剪/丢弃,所以你可能仍然会有一些丑陋。
  • 可能还有其他情况,这些情况不起作用或无法改进。如果您发现任何建议,请在评论中添加建议(像往常一样)!
© www.soinside.com 2019 - 2024. All rights reserved.