0

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

现在这个特定 repo 的历史看起来很丑陋,有一堆实际上没有添加任何东西的合并,还有一些只是合并了其他没有实际添加任何更改的合并(在重写的历史中;他们可能已经在过滤器分支之前“有用”)。

考虑这个带注释的片段(用 生成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
| |\ \ \ \  
| |/ / / /  
4

2 回答 2

1

这是问题的核心:

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

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

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

我也不太清楚如何定义“有用的合并”。不过,我认为最明显的算法是不(至少直接)不适合过滤分支的算法:它是一种迭代松弛算法,您从完整图开始并反复提取合并节点,将它们的父节点连接到它们的子节点,只要这些节点没用。(您仍然可以定义not有用。)最后,您有一个要保留的节点列表和要删除的节点。 该列表对您为 filter-branch 编写的过滤器很有用:您现在可以git filter-branch使用 a运行,--commit-filter它要么git commit-tree照常运行,要么提供skip_commit功能如文档中所述。“保留”或“跳过”的决定取决于您使用松弛算法生成的列表。

于 2019-03-27T18:46:05.807 回答
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则返回错误)或 2) 第二个父项 ( ) 包含第一个父项 ( ) 没有的提交(参见此处),保留提交(如果它不是空的;如果你想保留空的,请使用);如果第二个父级存在并且没有添加任何有用的东西,我们会跳过提交,并故意只传递第一个父级-我不确定这是“合法的”,但它会从历史记录中删除第二个父级,并且它有效在我的情况下......(见下面的警告)$GIT_COMMIT^2$GIT_COMMIT^2$GIT_COMMIT^1git 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"了 off "$4" "$5",否则将包含在"$@". 如果您有多个父母(或者更确切地说,如果您的提交有多个父母),则必须对此进行调整以解决此问题;应该不会太难,但我现在不是为了假设而修复它 - 你可能想要选择特定的父母放弃,idk。
  • 如果在USELESS-A合并之前有一个“有用的”提交USELESS-B(可以说这不会是无用的),USELESS-A不会被修剪/删除,所以你可能仍然会有一些丑陋。
  • 在其他情况下,这可能不起作用或可以改进。如果您发现任何建议,请在评论中添加建议(像往常一样)!
于 2019-03-29T15:08:52.280 回答