public string RevToDate(string rev) { var stamp = Convert.ToInt32(GitStats.GetPipeOutput(new[] { $"git log --pretty=format:%%at \"{rev}\" -n 1" })); return(DateTimeOffset.FromUnixTimeSeconds(stamp).DateTime.ToString("%Y-%m-%d")); }
private void GetExtensions() { Extensions = new DictionaryWithDefault <string, Extension>(); var lines = GitStats.GetPipeOutput(new[] { "git ls-tree -r -z HEAD" }).Split("\0").ToList().Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); ; TotalFiles = lines.Length; Console.WriteLine($"Processing {TotalFiles} files in repo"); foreach (var line in lines) { if (line.Length == 0) { continue; } var parts = Regex.Split(line, "\\s+", RegexOptions.None); var sha1 = parts[2]; var filename = parts[3]; string ext; if (filename.IndexOf(".", StringComparison.Ordinal) == -1 || filename.IndexOf(".", StringComparison.Ordinal) == 0) { ext = ""; } else { ext = filename.Substring(filename.LastIndexOf(".", StringComparison.Ordinal) + 1); } if (ext.Length > _configuration.MaxExtensionLength) { ext = ""; } if (!Extensions.ContainsKey(ext)) { Extensions[ext] = new Extension(); } Extensions[ext].Files += 1; try { Extensions[ext].Lines += GetLinesInBlob(sha1); } catch { Console.WriteLine("Warning: Could not count lines for file \"%s\"", line); } } }
private void GetTotalAuthors() { try { TotalAuthors = Convert.ToInt32(GitStats.GetPipeOutput(new[] { "git shortlog -s", "wc -l" })); } catch (Exception) { TotalAuthors = 0; } }
public int GetLinesInBlob(string sha1) { if (Cache.LinesInBlob.TryGetValue(sha1, out var res)) { return(res); } res = Convert.ToInt32(GitStats.GetPipeOutput(new[] { $"git cat-file blob {sha1}", "wc -l" }, PipingLevel.Minimal).Split()[0]); Cache.LinesInBlob[sha1] = res; return(res); }
public int GetFilesInCommit(string rev) { if (Cache.FilesInTree.TryGetValue(rev, out var res)) { return(res); } res = Convert.ToInt32(GitStats.GetPipeOutput(new[] { $"git ls-tree -r --name-only \"{rev}\"", "wc -l" }).Split("\n")[0]); Cache.FilesInTree[rev] = res; return(res); }
private void GetFilesByStampAndTotalCommits(List <string> lines) { // TODO Optimize this, it's the worst bottleneck // outputs "<stamp> <files>" for each revision lines.Clear(); FilesByStamp = new DictionaryWithDefault <DateTime, int>(); var revLines = GitStats.GetPipeOutput(new[] { "git rev-list --remotes --pretty=format:\"%at %T\" HEAD", "grep -v ^commit" }).Trim().Split("\n"); foreach (var revLine in revLines) { var tup2 = revLine.Split(" "); var time = tup2[0]; var rev = tup2[1]; var lineCount = GetFilesInCommit(rev); lines.Add($"{Convert.ToInt32(time)} {lineCount}"); } TotalCommits = lines.Count; foreach (var line in lines) { var parts = line.Split(" "); if (parts.Length != 2) { continue; } try { FilesByStamp[DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt32(parts[0])).DateTime] = Convert.ToInt32(parts[1]); } catch (FormatException) { Console.WriteLine("Warning: failed to parse line \"%s\"", line); } } }
private void GetChangesByDateAndTotalLines() { // line statistics // outputs: // N files changed, N insertions (+), N deletions(-) // <stamp> <author> ChangesByDate = new DictionaryWithDefault <DateTime, Change>(); var lines = GitStats.GetPipeOutput(new[] { "git log --shortstat --pretty=format:\"%at %an\"" }).Split("\n") .ToList(); lines.Reverse(); var files = 0; var inserted = 0; var deleted = 0; var totalLines = 0; foreach (var line in lines) { if (line.Length == 0) { files = 0; inserted = 0; deleted = 0; continue; } if (line.IndexOf(" changed,", StringComparison.CurrentCultureIgnoreCase) == -1) { var pos = line.IndexOf(" ", StringComparison.CurrentCultureIgnoreCase); if (pos != -1) { try { var datetime = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt32(line.Substring(0, pos))).DateTime; var author = line.Substring(pos + 1); ChangesByDate[datetime] = new Change { Files = files, Inserted = inserted, Deleted = deleted, TotalLines = totalLines }; if (!Authors.ContainsKey(author)) { Authors[author] = new Author(); } Authors[author].LinesAdded += inserted; Authors[author].LinesRemoved += deleted; } catch (Exception) { Console.WriteLine($"Warning: unexpected line \"{line}\""); } } else { Console.WriteLine($"Warning: unexpected line \"{line}\""); } } else { files = GetIntFromStartOfRegex(line, "\\d+ file"); inserted = GetIntFromStartOfRegex(line, "\\d+ insertion"); deleted = GetIntFromStartOfRegex(line, "\\d+ delet"); totalLines += inserted; totalLines -= deleted; TotalLinesAdded += inserted; TotalLinesRemoved += deleted; } } TotalLines = totalLines; }
// Collect revision statistics // Outputs "<stamp> <date> <time> <timezone> <author> '<' <mail> '>'" private List <string> GetActivityDataAndAuthors() { var lines = GitStats.GetPipeOutput(new[] { "git rev-list --remotes --pretty=format:\"%at %ai %an <%aE>\" HEAD", "grep -v ^commit" }, PipingLevel.Full, true).Split("\n").ToList(); foreach (var line in lines) { var parts = Regex.Split(line, "([01-9-:+]+ )").Where(x => !string.IsNullOrEmpty(x)).Select(s => s.Trim()) .ToArray(); DateTime stamp; try { stamp = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt32(parts[0])).DateTime; } catch (FormatException) { stamp = DateTime.MinValue; } var timezone = parts[3]; var tup1 = parts[4].Split("<"); var author = tup1[0]; var mail = tup1[1]; author = author.TrimEnd(); mail = mail.TrimEnd('>'); var domain = "?"; if (mail.IndexOf("@", StringComparison.CurrentCultureIgnoreCase) != -1) { domain = mail.Split("@")[1]; } var date = stamp; // First and last commit stamp if (LastCommitStamp == DateTime.MinValue) { LastCommitStamp = stamp; } FirstCommitStamp = stamp; // activity // hour var hour = date.Hour; ActivityByHourOfDay[hour] = ActivityByHourOfDay[hour] + 1; // most active hour? if (ActivityByHourOfDay[hour] > ActivityByHourOfDayBusiest) { ActivityByHourOfDayBusiest = ActivityByHourOfDay[hour]; } // day of week var day = (int)date.DayOfWeek - 1; ActivityByDayOfWeek[day] = ActivityByDayOfWeek[day] + 1; // domain stats if (!Domains.ContainsKey(domain)) { Domains[domain] = new Domain(); } // commits Domains[domain].Commits = Domains[domain].Commits + 1; // hour of week if (!ActivityByHourOfWeek.ContainsKey(day)) { ActivityByHourOfWeek[day] = new DictionaryWithDefault <int, int>(); } ActivityByHourOfWeek[day][hour] = ActivityByHourOfWeek[day][hour] + 1; // most active hour? if (ActivityByHourOfWeek[day][hour] > ActivityByHourOfWeekBusiest) { ActivityByHourOfWeekBusiest = ActivityByHourOfWeek[day][hour]; } // month of year var month = date.Month; ActivityByMonthOfYear[month] = ActivityByMonthOfYear[month] + 1; // yearly/weekly activity var yyw = $"{date.Year}-{_calendar.GetWeekOfYear(date, _cultureInfo.DateTimeFormat.CalendarWeekRule, _cultureInfo.DateTimeFormat.FirstDayOfWeek)}"; ActivityByYearWeek[yyw] = ActivityByYearWeek[yyw] + 1; if (ActivityByYearWeekPeak < ActivityByYearWeek[yyw]) { ActivityByYearWeekPeak = ActivityByYearWeek[yyw]; } // author stats if (!Authors.ContainsKey(author)) { Authors[author] = new Author(); } // commits if (Authors[author].LastCommitStamp == DateTime.MinValue) { Authors[author].LastCommitStamp = stamp; } Authors[author].FirstCommitStamp = stamp; Authors[author].Commits = Authors[author].Commits + 1; // author of the month/year var yymm = $"{date.Year}-{date.Month:D2}"; if (AuthorOfMonth.ContainsKey(yymm)) { AuthorOfMonth[yymm][author] = AuthorOfMonth[yymm][author] + 1; } else { AuthorOfMonth[yymm] = new DictionaryWithDefault <string, int> { [author] = 1 } }; CommitsByMonth[yymm] = CommitsByMonth[yymm] + 1; var yy = date.Year; if (AuthorOfYear.ContainsKey(yy)) { AuthorOfYear[yy][author] = AuthorOfYear[yy][author] + 1; } else { AuthorOfYear[yy] = new DictionaryWithDefault <string, int> { [author] = 1 } }; CommitsByYear[yy] = CommitsByYear[yy] + 1; // authors: active days var yymmdd = date; if (Authors[author].LastActiveDay == DateTime.MinValue) { Authors[author].LastActiveDay = yymmdd; Authors[author].ActiveDays = 1; } else if (yymmdd != Authors[author].LastActiveDay) { Authors[author].LastActiveDay = yymmdd; Authors[author].ActiveDays += 1; } // project: active days if (yymmdd != LastActiveDay) { LastActiveDay = yymmdd; ActiveDays.Add(yymmdd); } // timezone CommitsByTimezone[timezone] = CommitsByTimezone[timezone] + 1; } return(lines); }
private void GetTagData() { string[] parts; string output; var lines = GitStats.GetPipeOutput(new[] { "git show-ref --tags" }).Split("\n").ToList(); foreach (var line in lines) { if (line.Length == 0) { continue; } var split = line.Split(" "); var hash = split[0]; var tag = split[1]; tag = tag.Replace("refs/tags/", ""); output = GitStats.GetPipeOutput(new[] { $"git log \"{hash}\" --pretty=format:\"%at %an\" -n 1" }); if (output.Length <= 0) { continue; } parts = output.Split(" "); DateTime stamp; try { stamp = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt32(parts[0])).DateTime; } catch (FormatException) { stamp = DateTime.MinValue; } Tags[tag] = new Tag { Stamp = stamp, Hash = hash, Date = stamp, Commits = 0, Authors = new Dictionary <string, int>() }; } // Collect info on tags, starting from latest var tagsSortedByDateDesc = Tags.Select(el => (el.Value.Date, el.Key)).OrderBy(p1 => p1).Reverse() .Select(el => el.Key); foreach (var tag in tagsSortedByDateDesc.Reverse()) { var cmd = string.Format($"git shortlog -s \"{tag}\""); output = GitStats.GetPipeOutput(new[] { cmd }); if (output.Length == 0) { continue; } var prev = tag; foreach (var line in output.Split("\n")) { parts = Regex.Split(line, "\t", RegexOptions.None); var commits = Convert.ToInt32(parts[0].Trim()); var author = parts[1]; Tags[tag].Commits += commits; Tags[tag].Authors[author] = commits; } } }
public void CreateGraphs(string path) { Console.WriteLine("Generating graphs..."); // hour of day var f = new StreamWriter(path + "\\hour_of_day.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'hour_of_day.{ImageType}' unset key set xrange [0.5:24.5] set xtics 4 set grid y set ylabel ""Commits"" plot 'hour_of_day.dat' using 1:2:(0.5) w boxes fs solid "); f.Close(); // day of week f = new StreamWriter(path + "\\day_of_week.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'day_of_week.{ImageType}' unset key set xrange [0.5:7.5] set xtics 1 set grid y set ylabel ""Commits"" plot 'day_of_week.dat' using 1:2:(0.5) w boxes fs solid "); f.Close(); // Domains f = new StreamWriter(path + "\\domains.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'domains.{ImageType}' unset key unset xtics set grid y set ylabel ""Commits"" plot 'domains.dat' using 2:3:(0.5) with boxes fs solid, '' using 2:3:1 with labels rotate by 45 offset 0,1 "); f.Close(); // Month of Year f = new StreamWriter(path + "\\month_of_year.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'month_of_year.{ImageType}' unset key set xrange [0.5:12.5] set xtics 1 set grid y set ylabel ""Commits"" plot 'month_of_year.dat' using 1:2:(0.5) w boxes fs solid "); f.Close(); // commits_by_year_month f = new StreamWriter(path + "\\commits_by_year_month.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'commits_by_year_month.{ImageType}' unset key set xdata time set timefmt ""%Y-%m"" set format x ""%Y-%m"" set xtics rotate by 90 15768000 set bmargin 5 set grid y set ylabel ""Commits"" plot 'commits_by_year_month.dat' using 1:2:(0.5) w boxes fs solid "); f.Close(); // commits_by_year f = new StreamWriter(path + "\\commits_by_year.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'commits_by_year.{ImageType}' unset key set xtics 1 rotate by 90 set grid y set ylabel ""Commits"" set yrange [0:] plot 'commits_by_year.dat' using 1:2:(0.5) w boxes fs solid "); f.Close(); // Files by date f = new StreamWriter(path + "\\files_by_date.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'files_by_date.{ImageType}' unset key set xdata time set timefmt ""%Y-%m-%d"" set format x ""%Y-%m-%d"" set grid y set ylabel ""Files"" set xtics rotate by 90 set ytics autofreq set bmargin 6 plot 'files_by_date.dat' using 1:2 w steps "); f.Close(); // Lines of Code f = new StreamWriter(path + "\\lines_of_code.plot"); f.Write(GnuplotCommon); f.Write($@" set output 'lines_of_code.{ImageType}' unset key set xdata time set timefmt ""%s"" set format x ""%Y-%m-%d"" set grid y set ylabel ""Lines"" set xtics rotate by 90 set bmargin 6 plot 'lines_of_code.dat' using 1:2 w lines "); f.Close(); Directory.SetCurrentDirectory(path); var matchingFiles = Glob.Files(path, "*.plot").ToArray(); foreach (var file in matchingFiles) { var output = GitStats.GetPipeOutput(new[] { Program.GnuPlotCmd + $" \"{file}\"" }); if (output.Length > 0) { Console.WriteLine(output); } } }