更新的答案
我有一个简单的想法,既包含了原始答案相对于FelipeC的 ~20% 的性能优势(避免了对 git log
(一次)和 git show
(每个提交)的调用),又具备了FelipeC更好的页面处理能力(找到的提交一个接一个地处理,而不是在处理整个输出后再进行处理,这是我的原始答案所不具备的)。
这通过不再使用GNU sed的子shell执行模式来实现,而是逐行读取输出,在“分支:8ba14...”一行中替换Git哈希为git branch --contains 8ba14...
的结果。在这个简单的解决方案中,我甚至不再使用sed或awk,只使用Bash内置的子字符串功能:
#!/usr/bin/bash
git --no-pager log --quiet --pretty=format:"commit %H%nAuthor: %an <%ae>%nDate: %ad%nBranches: %H%n%n%w(76,4,4)%B" "$@" | while read -r LINE; do
if [ "${LINE:0:10}" = "Branches: " ]; then
echo "Branches: $(git branch --contains ${LINE:10} --format='%(refname:short)' | tr '\n' ' ')"
else
echo "$LINE"
fi
done
根据您自己的喜好取消注释less
分页。感谢FelipeC与我讨论并比较我们各自的原始解决方案,帮助我提出了这个改进版本。
原始回答
默认情况下,Git无法在其漂亮的格式字符串中嵌入shell脚本。这是一个快速且简单的解决方案。它有点像您想要的格式,但没有颜色,也没有Git在某些情况下可能打印的一些额外信息。但我希望它能给您提供继续和完善它的线索。
前提条件:
- 您使用类UNIX的shell,我在Windows上尝试了Git Bash。
- 您使用GNU sed。
在您的配置文件或直接在控制台中定义此shell函数:
git_log_branches() {
git log --pretty=format:"commit %H%nAuthor: %an <%ae>%nDate: %ad%nBranches: %H%n%n%w(76,4,4)%B" "$@" |
sed -E "s/^(Branches: )(.*)/echo -n '\1'; git branch --contains \2 --format='%(refname:short)' | tr '\n' ' '/e"
}
日志输出将类似于以下内容:
$ git_log_branches -2 HEAD~100
commit d81a845b61f5b98b217722122c6005cb51f9e160
Author: Alexander Kriegisch <aaaa@bbb.xy>
Date: Sun Jun 6 13:27:03 2021 +0700
Branches: main openj9-jit openj9-jit-exclude
Integration test POM (group ID) + UML whitespace cosmetics
commit 25eafcc93340ee2ee6ce05d0ec1a2139e20d45d8
Author: Florian Lasinger <ggg@hhh.yz>
Date: Fri Feb 19 13:57:05 2021 +0100
Branches: main openj9-jit openj9-jit-exclude
[#92] Dependency artifacts have higher precedence than reactor artifacts
(cherry picked from commit f32367b3 + additional comment)
输出结果不会自动分页。我不太喜欢这个解决方案,但是在玩了一会儿后,这是我能想到的最好的解决方案。
更新:像FelipeC在他的答案中提到的那样,你当然也可以将shell函数转换为独立的shell脚本,命名为git-log-branches并将其放置在PATH
中的任何位置,这样Git就可以找到它:
#!/usr/bin/bash
git log --pretty=format:"commit %H%nAuthor: %an <%ae>%nDate: %ad%nBranches: %H%n%n%w(76,4,4)%B" "$@" |
sed -E "s/^(Branches: )(.*)/echo -n '\1'; git branch --contains \2 --format='%(refname:short)' | tr '\n' ' '/e"
然后,您可以使用git log-branches -2 HEAD~100
进行调用,以获取与调用相应的shell函数时完全相同的日志输出。
我还比较了日志调用的时间:
# OK, we have a history of 577 commits
$ git log --oneline | wc -l
577
# Generating a standard Git log is really quick!
$ time (git log | wc -l)
5471
real 0m0.100s
# As expected, kriegaex's solution is way slower. This is the
# price you pay if you want the branches for each commit.
$ time (git log-branches | wc -l)
6591
real 0m56.953s
# The performance of FelipeC's solution is in the same order of magnitude,
# just slightly slower. No big deal. Both solutions could be tweaked
# here or there.
$ time (git log-branches2 | wc -l)
6592
real 1m8.982s
关于 -G foo
的更新:当使用 -G
时,Git 中的筛选过程占用了大部分时间,结果要处理的提交较少。因此,衡量整体性能 - 不是在这里讨论显示第一个匹配项所需的时间 - 并没有真正帮助。我的上面的测量更有意义,因为它们影响到将在具有更多条目的日志中处理的所有提交。但是,值得一提的是,现在数字看起来像这样:
$ time (git log -G foo --oneline | wc -l)
19
real 0m0.401s
$ time (git log-branches -G foo | wc -l)
216
real 0m2.683s
$ time (git log-branches2 -G foo | wc -l)
217
real 0m2.691s
再次强调,这两种解决方案所需时间相近,但比普通的git log
更多。这并不意外。
现在我们切换到一个更大的开源项目 - 我选择了Eclipse AspectJ - 并在其中搜索-G foo
。幸运的是,在主分支的8607个提交中,有707个包含“foo”,即不仅仅是一小部分。因此,性能测试实际上可以对比两个脚本与Git本身的性能:
# 8,607 commits in total
$ time (git log --oneline | wc -l)
8607
real 0m0.464s
# 707 commits contain "foo"
$ time (git log -G foo --oneline | wc -l)
707
real 0m55.196s
# Script by kriegaex
$ time (git log-branches -G foo | wc -l)
5450
real 1m15.749s
# Script by FelipeC
$ time (git log-branches2 -G foo | wc -l)
5451
real 1m19.283s
在这里,我们可以看到处理所有707个提交所需的75(kriegaex)或79(FelipeC)秒中,实际上几乎相同,有55秒仅用于Git过滤包含8607个提交的大型存储库。也就是说,55/75或73.3%的时间被Git使用,我们不再仅比较实际脚本性能。当通过-G
找到更少的提交时,该比率将渐近地提高到100%。这就是为什么最初我没有使用昂贵的-G
选项进行测量,因为它只会污染结果。
git branch --contains <hash>
。你目前有什么进展? - Romain Valeri