Esempio n. 1
0
        private void WriteGeneralTab(GitDataCollector data, string path)
        {
            var f      = new StreamWriter(path + "\\index.html");
            var format = "yyyy-MM-dd H:mm:ss";

            PrintHeader(f);
            f.Write($"<h1>GitStatistics - {data.ProjectName}</h1>");
            PrintNav(f);
            f.Write("<dl>");
            f.Write($"<dt>Project name</dt><dd>{data.ProjectName}</dd>");
            var runTime = DateTime.Now - data.FirstCommitStamp;

            f.Write(
                $"<dt>Generated</dt><dd>{DateTime.Now.ToString(format)} (in {runTime.Seconds:f2} seconds)</dd>");
            f.Write(
                "<dt>Generator</dt><dd><a href=\"https://github.com/hegsie/GitStatistics\">GitStatistics</a> (version {0})</dd>",
                version);
            f.Write(
                $"<dt>Report Period</dt><dd>{data.FirstCommitStamp.ToString(format)} to {data.LastCommitStamp.ToString(format)}</dd>");
            f.Write("<dt>Age</dt><dd>{0} days, {1} active days ({2:f2}%)</dd>", data.GetCommitDeltaDays(),
                    data.ActiveDays.Count, 100.0 * data.ActiveDays.Count / data.GetCommitDeltaDays());
            f.Write($"<dt>Total Files</dt><dd>{data.TotalFiles}</dd>");
            f.Write("<dt>Total Lines of Code</dt><dd>{0} ({1} added, {2} removed)</dd>", data.TotalLines,
                    data.TotalLinesAdded, data.TotalLinesRemoved);
            f.Write("<dt>Total Commits</dt><dd>{0} (average {1:F1} commits per active day, {2:F1} per all days)</dd>",
                    data.TotalCommits, data.TotalCommits / data.ActiveDays.Count,
                    data.TotalCommits / data.GetCommitDeltaDays());
            f.Write($"<dt>Authors</dt><dd>{data.TotalAuthors}</dd>");
            f.Write("</dl>");
            f.Write("</body>\n</html>");
            f.Close();
        }
Esempio n. 2
0
        public void Create(GitDataCollector data, string path)
        {
            Data = data;
            Path = path;

            _title = data.ProjectName;
            // copy static files. Looks in the binary directory, ../share/gitstats and /usr/share/gitstats
            var binaryPath    = Directory.GetCurrentDirectory();
            var secondaryPath = AssemblyDirectory;
            var baseDirs      = new List <object>
            {
                binaryPath,
                secondaryPath,
                "/usr/share/gitstats"
            };

            foreach (var file in new List <string>
            {
                "gitstats.css", "sortable.js", "arrow-up.gif", "arrow-down.gif", "arrow-none.gif"
            })
            {
                foreach (var @base in baseDirs)
                {
                    var src = @base + "\\" + file;
                    if (File.Exists(src))
                    {
                        File.Copy(src, path + "\\" + file, true);
                        break;
                    }
                }
            }

            WriteGeneralTab(data, path);
            //##
            // Activity
            var totalCommits = WriteActivityTab(data, path);

            //##
            // Authors
            WriteAuthorsTab(data, path, totalCommits);
            //##
            // Files
            WriteFilesTab(data, path);
            //##
            // Lines
            WriteLinesTab(data, path);
            //##
            // tags.html
            WriteTagsTab(data, path);

            CreateGraphs(path);
        }
Esempio n. 3
0
        private void WriteFilesTab(GitDataCollector data, string path)
        {
            var f = new StreamWriter(path + "\\files.html");

            PrintHeader(f);
            f.Write("<h1>Files</h1>");
            PrintNav(f);
            f.Write("<dl>\n");
            f.Write($"<dt>Total files</dt><dd>{data.TotalFiles}</dd>");
            f.Write($"<dt>Total lines</dt><dd>{data.TotalLines}</dd>");
            f.Write("<dt>Average file size</dt><dd>{0} bytes</dd>", 100.0 * data.TotalLines / data.TotalFiles);
            f.Write("</dl>\n");
            // Files :: File count by date
            f.Write(html_header(2, "File count by date"));
            // use set to get rid of duplicate/unnecessary entries
            var filesByDate = new List <string>();

            foreach (var stamp in data.FilesByStamp.Keys.OrderBy(p8 => p8).ToList())
            {
                filesByDate.Add(
                    $"{stamp:yyyy-MM-dd} {data.FilesByStamp[stamp]}");
            }
            var fg = new StreamWriter(path + "\\files_by_date.dat");

            foreach (var line in filesByDate.ToList().OrderBy(p9 => p9).ToList())
            {
                fg.Write($"{line}\n");
            }

            fg.Close();
            f.Write($"<img src=\"files_by_date.{ImageType}\" alt=\"Files by Date\" />");

            f.Write(html_header(2, "Extensions"));
            f.Write(
                "<table class=\"sortable\" id=\"ext\"><tr><th>Extension</th><th>Files (%)</th><th>Lines (%)</th><th>Lines/file</th></tr>");
            foreach (var ext in data.Extensions.Keys.OrderBy(p10 => p10).ToList())
            {
                var files = data.Extensions[ext].Files;
                var lines = data.Extensions[ext].Lines;
                f.Write("<tr><td>{0}</td><td>{1} ({2}%)</td><td>{3} ({4:F2}%)</td><td>{5}</td></tr>", ext, files,
                        100.0 * files / data.TotalFiles, lines,
                        100.0 * lines / data.TotalLines, lines / files);
            }

            f.Write("</table>");
            f.Write("</body></html>");
            f.Close();
        }
Esempio n. 4
0
        public void Run(string[] argsOrig)
        {
            Parser.Default.ParseArguments <Configuration>(argsOrig)
            .WithParsed(o =>
            {
                var gitPath    = Path.GetFullPath(o.RepositoryPath);
                var outputPath = Path.GetFullPath(o.OutputPath);
                var runDir     = Directory.GetCurrentDirectory();
                try
                {
                    Directory.CreateDirectory(outputPath);
                }
                catch
                {
                }

                if (!Directory.Exists(outputPath))
                {
                    Console.WriteLine($"FATAL: Output Path {outputPath} is not a directory or does not exist");
                    Environment.Exit(1);
                }

                Console.WriteLine($"Git Path: {gitPath}");
                Console.WriteLine("Output Path: {0}", outputPath);
                Directory.SetCurrentDirectory(gitPath);
                var cacheFile = Path.Join(outputPath, "gitstats.cache");
                Console.WriteLine("Collecting Data...");
                var data = new GitDataCollector();
                data.LoadCache(cacheFile);
                data.Collect(gitPath, o);
                Console.WriteLine("Refining Data...");
                data.SaveCache(cacheFile);
                data.Refine();
                Directory.SetCurrentDirectory(runDir);
                Console.WriteLine("Generating report...");
                var report = new HtmlReportCreator(o);
                report.Create(data, outputPath);
                var timeEnd       = DateTime.Now;
                ExecTimeInternal += timeEnd - TimeStart;

                Console.WriteLine(
                    $"Execution time {ExecTimeInternal.Seconds} secs, {ExecTimeExternal.Seconds} secs ({(100.0 * ExecTimeExternal / ExecTimeInternal):F2} %) in external commands)");
            });
        }
Esempio n. 5
0
        private void WriteLinesTab(GitDataCollector data, string path)
        {
            var f = new StreamWriter(path + "\\lines.html");

            PrintHeader(f);
            f.Write("<h1>Lines</h1>");
            PrintNav(f);
            f.Write("<dl>\n");
            f.Write("<dt>Total lines</dt><dd>{0}</dd>", data.TotalLines);
            f.Write("</dl>\n");
            f.Write(html_header(2, "Lines of Code"));
            f.Write($"<img src=\"lines_of_code.{ImageType}\" />");
            var fg = new StreamWriter(path + "\\lines_of_code.dat");

            foreach (var stamp in data.ChangesByDate.Keys.OrderBy(p11 => p11).ToList())
            {
                fg.Write(
                    $"{(int) (stamp.Subtract(new DateTime(1970, 1, 1))).TotalSeconds} {data.ChangesByDate[stamp].TotalLines}\n");
            }
            fg.Close();
            f.Write("</body></html>");
            f.Close();
        }
Esempio n. 6
0
        private void WriteTagsTab(GitDataCollector data, string path)
        {
            var f = new StreamWriter(path + "\\tags.html");

            PrintHeader(f);
            f.Write("<h1>Tags</h1>");
            PrintNav(f);
            f.Write("<dl>");
            f.Write("<dt>Total tags</dt><dd>{0}</dd>", data.Tags.Count);
            if (data.Tags.Count > 0)
            {
                f.Write("<dt>Average commits per tag</dt><dd>{0}</dd>", 1.0 * data.TotalCommits / data.Tags.Count);
            }
            f.Write("</dl>");
            f.Write("<table class=\"tags\">");
            f.Write("<tr><th>Name</th><th>Date</th><th>Commits</th><th>Authors</th></tr>");
            // sort the tags by date desc
            var tagsSortedByDateDesc = data.Tags.Select(el => (el.Value.Date, el.Key)).OrderBy(p12 => p12)
                                       .Reverse().Select(el => el.Key);

            foreach (var tag in tagsSortedByDateDesc)
            {
                var authorInfo       = new List <string>();
                var authorsByCommits = data.Tags[tag].Authors.OrderByDescending(pair => pair.Value).Select(a => a.Key);
                foreach (var i in authorsByCommits.Reverse().ToList())
                {
                    authorInfo.Add($"{i} ({data.Tags[tag].Authors[i]})");
                }

                f.Write("<tr><td>{0}</td><td>{1}</td><td>{2}2</td><td>{3}</td></tr>", tag, data.Tags[tag].Date,
                        data.Tags[tag].Commits, string.Join(", ", authorInfo));
            }

            f.Write("</table>");
            f.Write("</body></html>");
            f.Close();
        }
Esempio n. 7
0
        private void WriteAuthorsTab(GitDataCollector data, string path, int totalCommits)
        {
            object next;

            string[] authors;
            int      commits;
            var      f = new StreamWriter(path + "\\authors.html");

            PrintHeader(f);
            f.Write("<h1>Authors</h1>");
            PrintNav(f);
            // Authors :: List of authors
            f.Write(html_header(2, "List of Authors"));
            f.Write("<table class=\"authors sortable\" id=\"authors\">");
            f.Write(
                "<tr><th>Author</th><th>Commits (%)</th><th>+ lines</th><th>- lines</th><th>First commit</th><th>Last commit</th><th class=\"unsortable\">Age</th><th>Active days</th><th># by commits</th></tr>");
            foreach (var author in data.GetAuthors(_configuration.MaxAuthors))
            {
                var info = data.GetAuthorInfo(author);
                f.Write(
                    "<tr><td>{0}</td><td>{1} ({2:F2}%)</td><td>{3}</td><td>{4}</td><td>{5:yyyy-MM-dd}</td><td>{6:yyyy-MM-dd}</td><td>{7}</td><td>{8}</td><td>{9}</td></tr>",
                    author, info.Commits, info.CommitsFrac, info.LinesAdded, info.LinesRemoved,
                    info.DateFirst, info.DateLast, info.TimeDelta, info.ActiveDays,
                    info.PlaceByCommits);
            }

            f.Write("</table>");
            var allAuthors = data.GetAuthors(_configuration.MaxAuthors).ToArray();

            if (allAuthors.Count() > _configuration.MaxAuthors)
            {
                var rest = allAuthors.Take(_configuration.MaxAuthors);
                f.Write("<p class=\"moreauthors\">These didn\'t make it to the top: {0}</p>", string.Join(", ", rest));
            }

            // Authors :: Author of Month
            f.Write(html_header(2, "Author of Month"));
            f.Write("<table class=\"sortable\" id=\"aom\">");
            f.Write(
                "<tr><th>Month</th><th>Author</th><th>Commits (%)</th><th class=\"unsortable\">Next top 5</th></tr>");
            var authMonthRev = data.AuthorOfMonth.Keys.OrderBy(p6 => p6).Reverse().ToList();

            foreach (var yymm in authMonthRev)
            {
                var authorDict = data.AuthorOfMonth[yymm];
                authors = authorDict.OrderByDescending(pair => pair.Value).Select(a => a.Key).ToArray();
                commits = data.AuthorOfMonth[yymm][authors[0]];
                var top5 = new List <string>(authors.ToList());
                top5.RemoveAt(0);
                next = string.Join(", ", top5.Take(5));
                f.Write("<tr><td>{0}</td><td>{1}</td><td>{2} ({3:F2}% of {4})</td><td>{5}</td></tr>", yymm, authors[0],
                        commits, 100.0 * commits / data.CommitsByMonth[yymm], data.CommitsByMonth[yymm], next);
            }

            f.Write("</table>");
            f.Write(html_header(2, "Author of Year"));
            f.Write(
                "<table class=\"sortable\" id=\"aoy\"><tr><th>Year</th><th>Author</th><th>Commits (%)</th><th class=\"unsortable\">Next top 5</th></tr>");
            foreach (var yy in data.AuthorOfYear.Keys.OrderBy(p7 => p7).Reverse().ToList())
            {
                var authorDict = data.AuthorOfYear[yy];
                authors = authorDict.OrderByDescending(pair => pair.Value).Select(a => a.Key).ToArray();
                commits = data.AuthorOfYear[yy][authors[0]];
                var top5 = new List <string>(authors.ToList());
                top5.RemoveAt(0);
                next = string.Join(", ", top5.Take(5));
                f.Write("<tr><td>{0}</td><td>{1}</td><td>{2} ({3:F2}% of {4})</td><td>{5}</td></tr>", yy, authors[0],
                        commits, 100.0 * commits / data.CommitsByYear[yy], data.CommitsByYear[yy], next);
            }

            f.Write("</table>");
            // Domains
            f.Write(html_header(2, "Commits by Domains"));
            var domainsByCommits = data.Domains.Keys.Select(outerKey => (data.Domains[outerKey].Commits, outerKey))
                                   .OrderBy(p1 => p1).Select(tuple => tuple.outerKey);

            domainsByCommits.Reverse();
            f.Write("<div class=\"vtable\"><table>");
            f.Write("<tr><th>Domains</th><th>Total (%)</th></tr>");
            var fp = new StreamWriter(path + "\\domains.dat");
            var n  = 0;

            foreach (var domain in domainsByCommits)
            {
                if (n == _configuration.MaxDomains)
                {
                    break;
                }
                commits = 0;
                n      += 1;
                var info = data.GetDomainInfo(domain);
                fp.Write("{0} {1} {2}\n", domain, n, info.Commits);
                f.Write("<tr><th>{0}</th><td>{1} ({2:F2}%)</td></tr>", domain, info.Commits,
                        100.0 * info.Commits / totalCommits);
            }

            f.Write("</table></div>");
            f.Write($"<img src=\"domains.{ImageType}\" alt=\"Commits by Domains\" />");
            fp.Close();
            f.Write("</body></html>");
            f.Close();
        }
Esempio n. 8
0
        private int WriteActivityTab(GitDataCollector data, string path)
        {
            object r;
            int    commits;
            var    f = new StreamWriter(path + "\\activity.html");

            PrintHeader(f);
            f.Write("<h1>Activity</h1>");
            PrintNav(f);
            // Weekly activity
            const int weeks = 32;

            f.Write(html_header(2, "Weekly activity"));
            f.Write("<p>Last {0} weeks</p>", weeks);
            // generate weeks to show (previous N weeks from now)
            var         now         = DateTime.Now;
            var         weeksList   = new List <string>();
            var         stampCur    = now;
            CultureInfo cultureInfo = new CultureInfo("en-US");
            Calendar    calendar    = cultureInfo.Calendar;

            foreach (var i in Enumerable.Range(0, weeks - 0))
            {
                var weekOfYear = calendar.GetWeekOfYear(stampCur, cultureInfo.DateTimeFormat.CalendarWeekRule,
                                                        cultureInfo.DateTimeFormat.FirstDayOfWeek);
                weeksList.Insert(0,
                                 stampCur.ToString(
                                     $"yyyy-{weekOfYear}"));
                stampCur = stampCur.AddDays(-7);
            }

            // top row: commits & bar
            f.Write("<table class=\"noborders\"><tr>");
            foreach (var i in Enumerable.Range(0, weeks - 0))
            {
                commits = 0;
                if (data.ActivityByYearWeek.ContainsKey(weeksList[i]))
                {
                    commits = data.ActivityByYearWeek[weeksList[i]];
                }

                decimal percentage = 0;
                if (data.ActivityByYearWeek.ContainsKey(weeksList[i]))
                {
                    percentage = data.ActivityByYearWeek[weeksList[i]] / data.ActivityByYearWeekPeak;
                }

                var height = Math.Max(1, Convert.ToInt32(200 * percentage));
                f.Write(
                    "<td style=\"text-align: center; vertical-align: bottom\">{0}<div style=\"display: block; background-color: red; width: 20px; height: {1}px\"></div></td>",
                    commits, height);
            }

            // bottom row: year/week
            f.Write("</tr><tr>");
            foreach (var i in Enumerable.Range(0, weeks - 0))
            {
                f.Write("<td>{0}</td>", weeks - i);
            }
            f.Write("</tr></table>");
            // Hour of Day
            f.Write(html_header(2, "Hour of Day"));
            var hourOfDay = data.ActivityByHourOfDay;

            f.Write("<table><tr><th>Hour</th>");
            foreach (var i in Enumerable.Range(0, 24 - 0))
            {
                f.Write("<th>{0}</th>", i);
            }
            f.Write("</tr>\n<tr><th>Commits</th>");
            var fp = new StreamWriter(path + "\\hour_of_day.dat");

            foreach (var i in Enumerable.Range(0, 24 - 0))
            {
                if (hourOfDay.ContainsKey(i))
                {
                    r = 127 + Convert.ToInt32(hourOfDay[i] / data.ActivityByHourOfDayBusiest * 128);
                    f.Write("<td style=\"background-color: rgb({0}, 0, 0)\">{1}</td>", r, hourOfDay[i]);
                    fp.Write("{0} {1}\n", i, hourOfDay[i]);
                }
                else
                {
                    f.Write("<td>0</td>");
                    fp.Write("{0} 0\n", i);
                }
            }

            fp.Close();
            f.Write("</tr>\n<tr><th>%</th>");
            var totalCommits = data.TotalCommits;

            foreach (var i in Enumerable.Range(0, 24 - 0))
            {
                if (hourOfDay.ContainsKey(i))
                {
                    r = 127 + Convert.ToInt32(hourOfDay[i] / data.ActivityByHourOfDayBusiest * 128);
                    f.Write("<td style=\"background-color: rgb({0}, 0, 0)\">{1:F2}</td>", r,
                            100 * hourOfDay[i] / totalCommits);
                }
                else
                {
                    f.Write("<td>0.00</td>");
                }
            }

            f.Write("</tr></table>");
            f.Write($"<img src=\"hour_of_day.{ImageType}\" alt=\"Hour of Day\" />");
            var fg = new StreamWriter(path + "\\hour_of_day.dat");

            foreach (var i in Enumerable.Range(0, 24 - 0))
            {
                fg.Write(hourOfDay.ContainsKey(i) ? $"{i + 1} {hourOfDay[i]}\n" : $"{i + 1} 0\n");
            }
            fg.Close();
            // Day of Week
            f.Write(html_header(2, "Day of Week"));
            var dayOfWeek = data.ActivityByDayOfWeek;

            f.Write("<div class=\"vtable\"><table>");
            f.Write("<tr><th>Day</th><th>Total (%)</th></tr>");
            fp = new StreamWriter(path + "\\day_of_week.dat");
            foreach (var d in Enumerable.Range(0, 7 - 0))
            {
                commits = 0;
                if (dayOfWeek.ContainsKey(d))
                {
                    commits = dayOfWeek[d];
                }
                fp.Write("{0} {1}\n", d + 1, commits);
                f.Write("<tr>");
                f.Write("<th>{0}</th>", d + 1);
                if (dayOfWeek.ContainsKey(d))
                {
                    f.Write("<td>{0} ({1:F2}%)</td>", dayOfWeek[d], 100.0 * dayOfWeek[d] / totalCommits);
                }
                else
                {
                    f.Write("<td>0</td>");
                }
                f.Write("</tr>");
            }

            f.Write("</table></div>");
            f.Write($"<img src=\"day_of_week.{ImageType}\" alt=\"Day of Week\" />");
            fp.Close();
            // Hour of Week
            f.Write(html_header(2, "Hour of Week"));
            f.Write("<table>");
            f.Write("<tr><th>Weekday</th>");
            foreach (var hour in Enumerable.Range(0, 24 - 0))
            {
                f.Write("<th>{0}</th>", hour);
            }
            f.Write("</tr>");
            foreach (var weekday in Enumerable.Range(0, 7 - 0))
            {
                f.Write("<tr><th>{0}</th>", weekday + 1);
                foreach (var hour in Enumerable.Range(0, 24 - 0))
                {
                    commits = 0;
                    if (data.ActivityByHourOfWeek.TryGetValue(weekday, out var weekdayDict))
                    {
                        if (weekdayDict.TryGetValue(hour, out var hourOutput))
                        {
                            commits = hourOutput;
                        }
                    }

                    if (commits != 0)
                    {
                        f.Write("<td");
                        r = 127 + Convert.ToInt32(commits / data.ActivityByHourOfWeekBusiest * 128);
                        f.Write(" style=\"background-color: rgb({0}, 0, 0)\"", r);
                        f.Write(">{0}</td>", commits);
                    }
                    else
                    {
                        f.Write("<td></td>");
                    }
                }

                f.Write("</tr>");
            }

            f.Write("</table>");
            // Month of Year
            f.Write(html_header(2, "Month of Year"));
            f.Write("<div class=\"vtable\"><table>");
            f.Write("<tr><th>Month</th><th>Commits (%)</th></tr>");
            fp = new StreamWriter(path + "\\month_of_year.dat");
            foreach (var mm in Enumerable.Range(1, 13 - 1))
            {
                commits = 0;
                if (data.ActivityByMonthOfYear.ContainsKey(mm))
                {
                    commits = data.ActivityByMonthOfYear[mm];
                }
                f.Write("<tr><td>{0}</td><td>{1} ({2:F2} %)</td></tr>", mm, commits,
                        100.0 * commits / data.TotalCommits);
                fp.Write("{0} {1}\n", mm, commits);
            }

            fp.Close();
            f.Write("</table></div>");
            f.Write($"<img src=\"month_of_year.{ImageType}\" alt=\"Month of Year\" />");
            // Commits by year/month
            f.Write(html_header(2, "Commits by year/month"));
            f.Write("<div class=\"vtable\"><table><tr><th>Month</th><th>Commits</th></tr>");
            foreach (var yymm in data.CommitsByMonth.Keys.OrderBy(p1 => p1).Reverse().ToList())
            {
                f.Write("<tr><td>{0}</td><td>{1}</td></tr>", yymm, data.CommitsByMonth[yymm]);
            }
            f.Write("</table></div>");
            f.Write($"<img src=\"commits_by_year_month.{ImageType}\" alt=\"Commits by year/month\" />");
            fg = new StreamWriter(path + "\\commits_by_year_month.dat");
            foreach (var yymm in data.CommitsByMonth.Keys.OrderBy(p2 => p2).ToList())
            {
                fg.Write("{0} {1}s\n", yymm, data.CommitsByMonth[yymm]);
            }
            fg.Close();
            // Commits by year
            f.Write(html_header(2, "Commits by Year"));
            f.Write("<div class=\"vtable\"><table><tr><th>Year</th><th>Commits (% of all)</th></tr>");
            foreach (var yy in data.CommitsByYear.Keys.OrderBy(p3 => p3).Reverse().ToList())
            {
                f.Write("<tr><td>{0}</td><td>{1} ({2:F2}%)</td></tr>", yy, data.CommitsByYear[yy],
                        100.0 * data.CommitsByYear[yy] / data.TotalCommits);
            }
            f.Write("</table></div>");
            f.Write($"<img src=\"commits_by_year.{ImageType}\" alt=\"Commits by Year\" />");
            fg = new StreamWriter(path + "\\commits_by_year.dat");
            foreach (var yy in data.CommitsByYear.Keys.OrderBy(p4 => p4).ToList())
            {
                fg.Write($"{yy} {data.CommitsByYear[yy]}\n");
            }
            fg.Close();
            // Commits by timezone
            f.Write(html_header(2, "Commits by Timezone"));
            f.Write("<table><tr>");
            f.Write("<th>Timezone</th><th>Commits</th>");
            var maxCommitsOnTz = data.CommitsByTimezone.Values.Max();

            foreach (var i in data.CommitsByTimezone.Keys.OrderBy(Convert.ToInt32).ToList())
            {
                commits = data.CommitsByTimezone[i];
                r       = 127 + Convert.ToInt32(commits / maxCommitsOnTz * 128);
                f.Write("<tr><th>{0}</th><td style=\"background-color: rgb({1}, 0, 0)\">{2}</td></tr>", i, r, commits);
            }

            f.Write("</tr></table>");
            f.Write("</body></html>");
            f.Close();
            return(totalCommits);
        }