git查找重复提交(通过patch-id)

时间:2012-07-23 02:17:59

标签: git duplicates commit

我想要找到重复更改的方法。 patch-id可能是相同的,但提交属性可能不是。

这似乎是patch-id的预期用途:

  

git patch-id --help

     

IOW,你可以用这个东西来寻找可能的重复提交。

我想把“git log”,“git patch-id”和uniq串在一起 可以做得很糟糕,但如果有人有一个命令,那么 工作得很好,我很感激。

7 个答案:

答案 0 :(得分:11)

由于重复更改可能不在同一分支上(除非它们之间存在还原),您可以使用git cherry

git cherry [-v] [<upstream> [<head> [<limit>]]]

upstream将成为head中检查重复更改的分支。

答案 1 :(得分:9)

为了查找特定提交的重复项,这可能对您有用。

首先,确定目标提交的补丁ID:

$ THE_COMMIT_REF_OR_SHA_YOURE_SEEKING_DUPES_OF='7a3e67c'
$ git show $THE_COMMIT_REF_OR_SHA_YOURE_SEEKING_DUPES_OF | git patch-id
f6ea51cd6acd30cd627ce1a56e2733c1777d5b52 7a3e67ce38dbef471889d9f706b9161da7dc5cf3

第一个SHA是patch-id。接下来,列出每个提交的补丁ID并过滤掉任何匹配:

$ for c in $(git rev-list --all); do git show $c | git patch-id; done | grep 'f6ea51cd6acd30cd627ce1a56e2733c1777d5b52'
f6ea51cd6acd30cd627ce1a56e2733c1777d5b52 5028e2b5500bd5f4637531e337e17b73f5d0c0b1
f6ea51cd6acd30cd627ce1a56e2733c1777d5b52 7a3e67ce38dbef471889d9f706b9161da7dc5cf3
f6ea51cd6acd30cd627ce1a56e2733c1777d5b52 929c66b5783a0127a7689020d70d398f095b9e00

所有这些,一些额外的标志,以及utility script

的形式
test ! -z "$1" && TARGET_COMMIT_SHA="$1" || TARGET_COMMIT_SHA="HEAD"

TARGET_COMMIT_PATCHID=$(
git show --patch-with-raw "$TARGET_COMMIT_SHA" |
    git patch-id |
    cut -d' ' -f1
)
MATCHING_COMMIT_SHAS=$(
for c in $(git rev-list --all); do
    git show --patch-with-raw "$c" |
        git patch-id
done |
    fgrep "$TARGET_COMMIT_PATCHID" |
    cut -d' ' -f2
)

echo "$MATCHING_COMMIT_SHAS"

用法:

$ git list-dupe-commits 7a3e67c
5028e2b5500bd5f4637531e337e17b73f5d0c0b1
7a3e67ce38dbef471889d9f706b9161da7dc5cf3
929c66b5783a0127a7689020d70d398f095b9e00

它并不是非常快速,但是对于大多数repos来说应该完成工作(对于一个拥有826次提交的repo只测量36秒,在2.4GHz Core 2 Duo上测量158MB .git目录)

答案 2 :(得分:3)

我有一个适用于玩具回购的草稿,但它保留了 patch-&gt;在内存中提交映射,它可能在大型存储库上有问题:

# print commit pairs with the same patch-id
for c in $(git rev-list HEAD); do \
    git show $c | git patch-id; done \
| perl -anle '($p,$c)=@F;print "$c $s{$p}" if $s{$p};$s{$p}=$c'

输出应该是具有相同patch-id的提交对 (3个副本A B C表示为“A B”,然后是“B C”)。

更改git rev-list命令以限制检查的提交:

git log --format=%H HEAD somefile

追加“| xargs git show”以详细查看提交, 或“| xargs git show -s --oneline”获取摘要:

0569473 add 6-8
5e56314 add 6-8 again
bece3c3 comment
e037ed6 add comment again

事实证明,patch-id在我原来的情况下不起作用 后来的提交还有其他变化。 “git log -S”更有用。

答案 3 :(得分:2)

bsb建议的漂亮命令需要进行一些小的调整:

(1)命令应该使用

而不是运行git show的{​​{1}}。
git diff-tree --cc

否则 git diff-tree -p 会生成虚假的空SHA1哈希值。

(2)当使用git patch-id管道时,xargs应该有xargs参数。否则,三重提交将不会与等效提交配对。

这是-L 1中的别名:

~/.gitconfig

答案 4 :(得分:1)

要搜索提交$hash的重复提交,请排除合并提交:

git rev-list --no-merges --all | xargs -r git show | git patch-id \
    | grep ^$(git show $hash|git patch-id|cut -c1-40) | cut -c42-80 \
    | xargs -r git show -s --oneline

要搜索合并提交$mergehash的副本,请将$(git show $hash|git patch-id|cut -c1-40)替换为git diff-tree -m -p $mergehash | git patch-id给出的两个修补程序ID(第1列)之一。它们与其两个父项中的每一个对应于合并提交的差异。

要查找所有提交的重复项,请排除合并提交:

git rev-list --no-merges --all | xargs -r git show | git patch-id \
    | sort | uniq -w40 -D | cut -c42-80 \
    | xargs -r git log --no-walk --pretty=format:"%h %ad %an (%cn) %s" --date-order --date=iso

可以通过将参数更改为git rev-list来扩展或限制对重复提交的搜索,--all接受多个选项。例如,要将搜索限制为特定分支,请指定其名称而不是选项HEAD ^HEAD~100;或者在最近的100次提交中搜索传递参数--no-merges

请注意,这些命令很快,因为它们不使用shell循环和批处理进程提交。

要包含合并提交,请移除选项xargs -r git show,然后将xargs -r -L1 git diff-tree -m -p替换为git diff-tree。这个速度要慢得多,因为每次提交都会执行{{1}}次。

说明:

  • 第一行生成带有提交哈希的修补程序ID的映射(2列数据,每个40个字符)。

  • 第二行仅保留与重复的修补程序ID(第1列)对应的提交哈希值(第2列)。

  • 最后一行打印有关重复提交的自定义信息。

答案 5 :(得分:0)

对于任何想在Windows powershell上执行此操作的人,unagi的答案的等效命令是:

git rev-list --no-merges --all  | %{&git.exe show $_} | 
  git patch-id | ConvertFrom-String -PropertyNames PatchId, Commit | 
  Group-Object PatchId | Where-Object count -gt 1 | 
  %{$_.group.Commit + " "}

给出如下输出:

1605e0e1e13d7b3f456c20432d8edec664ca7117
1e8efa8f2f01962a2c08fd25caf687d330383428

b45b6db084b27ae420ac8e9cf6511110ebb46513
4a2e1e3ba5a9a1d5db1d00343813e1404f6124e2

将重复的提交哈希分组在一起。

注意:在我的仓库中,这是一个缓慢的命令,因此请确保适当地过滤对rev-list的调用!

答案 6 :(得分:0)

确保使用最新版本的Git

OP bsbanswer中提到的diff_flush_patch_id()并不总是唯一的。

这是因为,在Git 2.29(2020年第四季度)之前,patch-id计算并未忽略空格等“不完整的最后一行”标记。

请参见commit 82a6201René Scharfe (rscharfe)(2020年8月19日)。
(由Junio C Hamano -- gitster --commit 5122614中合并,2020年8月24日)

patch-id:忽略1-2000中文件末尾的换行符

报告人:蒂尔曼·沃格尔
初始测试者:蒂尔曼·沃格尔
签名人:RenéScharfe

计算补丁程序ID时将忽略空格。
这是通过在对diff行进行散列之前将所有空格删除(包括文件末尾的换行符)来除去的。
但是,如果缺少该换行符,diff将在单独的行中报告该事实,该行包含“ \\文件末尾没有换行符\ n”,并且此标记像上下文行一样散列。

这违背了使补丁ID与空白无关的目标。

使用与2485eab55cc ({{3}中添加的git patch-id(git-patch-id:不要跳过“ no newline”标记,2011-02-17)相同的启发式方法}})而是跳过以反斜杠和空格开头且长度超过十二个字符的差异行。