我们正在为一个大型团队(> 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
这将有助于仅检索一次数据,因此没有查找问题。但它会增加序列化 - 反序列化数据的额外开销,这对我来说不合适。 请分享您的想法
答案 0 :(得分:2)
访问Commit
对象中的注释使libgit2在循环的每次迭代中访问注释树。更有效的方法是:
push-date
命名空间关联的所有注释 注意:这会从内存的角度增加一些压力,但它应该更快。
这可以使用以下代码在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 };
答案 1 :(得分:0)
您可以随时考虑使用'git notes'的替代品。 请参阅:https://www.tikalk.com/posts/2015/11/12/yet-another-way-to-implement-commit-metadata/