我需要一份查找重复更改的指南。
使用patch-id可能会得到相同的结果,但提交属性可能不同。
这似乎是patch-id的预期用途:
git patch-id --help
也就是说,您可以使用此工具查找可能重复的提交。
我想通过串联“git log”、“git patch-id”和uniq命令来完成这项工作,但如果有人有一条能够很好地完成任务的命令,我会非常感激。
我需要一份查找重复更改的指南。
使用patch-id可能会得到相同的结果,但提交属性可能不同。
这似乎是patch-id的预期用途:
git patch-id --help
也就是说,您可以使用此工具查找可能重复的提交。
我想通过串联“git log”、“git patch-id”和uniq命令来完成这项工作,但如果有人有一条能够很好地完成任务的命令,我会非常感激。
由于重复更改很可能不在同一个分支上(除非它们之间存在还原),您可以使用git cherry命令:
git cherry [-v] [<upstream> [<head> [<limit>]]]
其中upstream
将是检查与head
中的更改是否有重复的分支。
如果你想查找特定提交的重复项,这可能对你有用。
首先,确定目标提交的补丁 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 是补丁 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
通过一些额外的标志,以及以 实用脚本 的形式,将所有内容组合在一起:
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
虽然速度不算太快,但对于大多数存储库来说,应该能完成任务(在2.4GHz Core 2 Duo上测试了一个包含826个提交和大小为158MB的.git文件夹的存储库,仅用时36秒)。
我有一个在玩具仓库上运行的草稿,但是由于它将补丁->提交映射保存在内存中,因此在大型仓库上可能会遇到问题:
# 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'
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
事实证明,在我的原始案例中,补丁ID并没有起作用,因为后来的提交中还有其他更改。"git log -S" 更加有用。
git diff $c〜1 $c | git patch-id
的操作。这在合并提交时会出现问题。跟踪两个合并父级是一个更复杂的问题。 - Christophergit show $c | git patch-id
吗?git show
只会打印元数据,但是 git patch-id
需要一个补丁作为输入... - Daniel Aldershow
而不是diff
,因为这样可以让Perl打印重复的提交(否则我只会得到很多零SHA)。这段代码跳过非差异输入(尽管旧版本可能不支持此功能,你使用的是哪个版本?) - bsbgit show
和git patch-id
似乎很好地合作,但仅适用于普通提交。对于合并,它似乎不显示任何差异,这就是我的问题。在1.7.10.4和1.9.1上进行了测试。 - Daniel Alder要搜索与提交$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(第一列)中的一个。它们对应于合并提交与其两个父提交之间的差异。
要查找所有提交的重复项,但排除合并提交:
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
;或者要搜索最近的100次提交,请传递参数HEAD ^HEAD~100
。--no-merges
,并将xargs -r git show
替换为xargs -r -L1 git diff-tree -m -p
。这样做会慢得多,因为git diff-tree
每个提交执行一次。由bsb提出的巧妙命令需要进行一些小调整:
(1) 命令应该使用git diff-tree --cc
而不是运行git show
。
git diff-tree -p
git patch-id
会生成虚假的空SHA1哈希值。xargs
时,xargs
应该带有 -L 1
参数。否则,一个重复的提交将无法与等效的提交配对。~/.gitconfig
中的别名:dup = "!f() { for c in $(git rev-list HEAD); do git diff-tree -p $c | git patch-id; done | perl -anle '($p,$c)=@F;print \"$c $s{$p}\" if $s{$p};$s{$p}=$c' | xargs -L 1 git show -s --oneline; }; f" # "git dup" lists duplicate commits
对于想在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 的调用!
请确保使用最新版本的Git(2.39或更高版本)
OP bsb的答案中提到的git log --format=%H
并不总是唯一的。
这是因为,在Git 2.29之前(2020年第四季度),补丁ID计算不会忽略“不完整的最后一行”标记,如空格。
请查看 提交 82a6201(2020年8月19日),提交者为René Scharfe (rscharfe
)。
(由Junio C Hamano -- gitster
--于2020年8月24日合并至提交 5122614)
计算补丁ID时会忽略空格。
patch-id
:在diff_flush_patch_id()
中忽略文件末尾的换行符报告者:Tilman Vogel
初始测试者:Tilman Vogel
签名作者:René Scharfe
这与我们使补丁ID独立于空格的目标相违背。
请改用与2485eab55cc(git-patch-id: do not trip over "no newline" markers, 2011-02-17)添加到git patch-id
(man)中的启发式方法,跳过以反斜杠和空格开头且长度大于12个字符的差异行。
git patch-id
将在 Git 2.39(2022 年第四季度)中更新。--include-whitespace
" 选项已添加到 "git patch-id
" 中(man),并且 Git 2.39(2022 年第四季度)中已经纠正了内部 patch-id
逻辑中不匹配 "git patch-id
" 的现有错误。
请查看 提交 0d32ae8, 提交 2871f4d, 提交 93105ab, 提交 0df19eb, 提交 51276c1, 提交 0570be7 (2022年10月24日) 由Jerry Zhang (jerry-skydio
)提交。
(由Taylor Blau -- ttaylorr
--合并于提交 160314e, 2022年10月30日)
builtin
:patch-id: 添加--verbatim
作为命令模式署名:Jerry Zhang
署名:Junio C Hamano
git patch-id
现在在其手册页面中包括:
--verbatim
计算输入的patch-id,不要去掉任何空格。
如果
patchid.verbatim
为true,则这是默认设置。
但不仅如此。
来自OP的内容:
我想要一个查找重复更改的方法。 patch-id可能相同,但提交属性可能不同。
这也在Git 2.39中得到了修复:
目前在rebase和cherry-pick中使用的patch-id如果文件被修改,则不考虑文件模式。
patch-id
:修复针对模式更改的patch-id
已签署:Jerry Zhang
git rebase
"(man)将删除您的本地补丁版本以及您的模式更改。patch-id
与内置的不会产生相同的输出,因为它们是diff输出的一部分,而内置的会考虑到模式更改。builtin
:patch-id:修复二进制差异的 patch-id署名:Jerry Zhang
"git patch-id
"(man) 目前在处理包含二进制文件的差异时无法正确输出。get_one_patchid
中添加逻辑,以处理可能出现的不同二进制差异样式。diff.c
中对应逻辑产生的补丁 ID 相同,即通过依次哈希a
和b
的对象 ID 来生成补丁 ID。
一般情况下,我们通过首先缓存“索引”行中的对象 ID,并在找到差异为二进制的指示后使用这些对象 ID 来处理二进制差异。
输入可能包含使用 "git diff --binary
"(man)" 生成的补丁。
目前这会破坏解析逻辑,并导致一个提交对应多个补丁 ID 的输出。
在这种情况下,我们需要跳过补丁本身的内容,因为它们不会被纳入补丁 ID 中。
--binary
imply --full-index
,所以对象 ID 总是可用的。
当使用--full-index
生成差异时,没有要跳过的补丁内容。
当使用--full-index
或--binary
以外的选项生成差异时,对象 ID 将会被缩写。
虽然在哈希后仍会产生足够唯一的补丁 ID,但与内部补丁 ID 输出不匹配。
目前我们将其视为可接受,因为为了匹配内部补丁 ID,我们已经需要特定的 diff 参数(即-U3
)。
git patch-id
现在应该能够在 Git 2.39 (Q2 2022) 中正确地报告所有差异(属性或二进制)。请参见我下面的更新答案。 - VonC