我们正在为一个大型团队(> 100个开发人员)使用git,而我正在编写不同的脚本来向管理层提供git统计信息。
管理层想知道的统计数据之一是将提交实际推送到存储库的时间。他们并不真正在乎作者日期或提交者日期,因为重要的是何时推送提交并因此被CI服务器接收。因此,我必须实现推送日期之类的东西。出于完整性考虑(不做自我宣传:))这是我的博客文章,描述了详细信息http://mnaoumov.wordpress.com/2013/01/31/git-get-push-date/
基本上,我将使用自定义git注释将提交实际推送到远程存储库时存储细节。
让我们考虑一个简单的任务:提供A(唯一)和B(包括)之间的所有提交的列表,并输出提交哈希,提交消息和推送日期
好吧,我可以做类似的事情
git log A..B --notes=push-date --format=<begin>%H<separator>%s<separator>%N<end>
然后相应地解析事物。嗯,无论如何这都是很慢的。而且我也不喜欢进行字符串解析,我更喜欢强类型方法。
因此,为了解决性能问题并摆脱解析,我决定使用LibGit2Sharp库。
好吧,如果我们不触摸笔记,它的运行速度很快,但是一旦我尝试检索笔记,它就会变得非常慢
# PowerShell script
$pushDateNote = $commit.Notes | Where-Object -FilterScript { $_.Namespace -eq "push-date" }
$pushDate = [DateTime]::Parse($pushDateNote.Message)
作为比较,如果我不包含注释,则大约2秒内返回200次提交的结果。如果我加上笔记-时间最多2分钟。
而且我检查了这里的瓶颈是提交的搜索记录。似乎git本身在commit和note之间没有映射,因此它需要一直在所有笔记中进行查找。
我刚刚检查了存储库中有188921个提交,因此最可能的情况甚至会更糟。因此,我的解决方案根本无法扩展。
所以我的问题是我做错了吗?也许git不是有效存储自己的元数据的正确工具吗?我现在正在考虑将所有元数据移动到外部数据库(例如MSSQL)中。但我宁愿将所有内容都放在一个地方。或者,我在考虑将提交及其推送日期之间的整个映射序列化为一次提交中的注释
例如使用魔术哈希 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (Is git's semi-secret empty tree object reliable, and why is there not a symbolic name for it?)
git notes add 4b825dc642cb6eb9a060e54bf8d69288fbee4904 -m serialized-data
$serializedData = git notes show 4b825dc642cb6eb9a060e54bf8d69288fbee4904
这将仅一次检索数据,因此不会出现查找问题。但这会增加序列化-反序列化数据的额外开销,这在我看来并不正确。
请分享您的想法
最佳答案
从Commit
对象访问注释使libgit2在循环的每次迭代中访问注释树。一种更有效的方法是:
push-date
命名空间相关的所有注释 请注意:从内存的 Angular 来看,这会增加一些压力,但是应该更快。
可以使用以下代码在C#中完成:
using (var repo = new Repository("your_repo_path"))
{
var notes = repo.Notes["push-date"];
var commits = repo.Commits.QueryBy(
new CommitFilter {Since = "1234567", Until = "89abcde"});
var pairs = from commit in commits
from note in notes
where note.TargetObjectId == commit.Id
select new {Commit = commit, Note = note};
foreach (var pair in pairs)
{
Debug.Write(pair.Commit.Sha + " : " + pair.Note);
}
}
这将输出在
push-date
命名空间中具有注释的提交。请注意:如果您使用
QueryBy
语法来检索提交列表,请注意,指定为Until
的提交将从列表中排除(例如:如git log A ... B中所示)为了也显示在
push-date
命名空间中没有注释的提交,可以使用以下linq查询:var pairs2 = from commit in commits
join note in notes on commit.Id equals note.TargetObjectId into gj
from subnote in gj.DefaultIfEmpty()
select new { Commit = commit, Note = subnote };
关于performance - Git注意到性能和替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22468146/