git-stats

Command line utility showing plots of git commits over time in the terminal
git clone https://noulin.net/git/git-stats.git
Log | Files | Refs | README

commit 4f3c7fe232f50f0ea15a2aeab7ba910f0276eaa7
parent 268b91a3214f8035721190e5fe8016f6f0a0d513
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sat,  5 Jun 2021 15:41:01 +0200

show plot instead of barchart

README.md   | 52 +++++++++++++++++++++++++++++++++++-
git-stats.c | 88 +++++++++++++++++++++++++++++++++++++++++++------------------
package.yml | 37 ++++++++++++++++++++++++++
3 files changed, 150 insertions(+), 27 deletions(-)

Diffstat:
MREADME.md | 52+++++++++++++++++++++++++++++++++++++++++++++++++++-
Mgit-stats.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Apackage.yml | 37+++++++++++++++++++++++++++++++++++++
3 files changed, 150 insertions(+), 27 deletions(-)

diff --git a/README.md b/README.md @@ -1,4 +1,54 @@ -This version creates a barchart: +This version creates a plot: + +50┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + ⡧ + ⡧ + ⡧ + ⡧ + ⡧ + ⡧ + ⡧ ⡧ + ⢠ ⡧ ⡤ ⡧ + ⢸ ⢸ ⡦ ⢸ ⢰⢣ ⡧ ⡧ + ⢠ ⡤ ⡧ ⣸ ⢸ ⡧ ⢸ ⢸⢸ ⡧ ⢸ ⢸⡧ + ⣿ ⡧ ⢠⡧ ⢠ ⢠⢰⢧ ⢰ ⡿⡠⢰ ⢠ ⢸⡤ ⡧ ⢰⢠⡧ ⢠ ⢸ ⢸⢸ ⢸⡼⡤ ⢰⢹ ⢸ ⡧ ⢸⡧ + ⡤⣿ ⡧ ⡧⢸⡧ ⢸ ⡤ ⢸⡼⢸ ⣼ ⡧⡧⣸ ⢸⡤⢸ ⢸⡧ ⢰⡧ ⢸ ⢸⣼⡧ ⢸ ⢸⡤ ⡤ ⢸⢸ ⡤⡧ ⢣ ⢸⢸⢠ ⡧⡧ ⡧ ⢸⡧ + ⢠⣷⠩⡦⣰⡧ ⣧⢧⢸⢱ ⡦⢸ ⣼⢸⡤ ⢠⡧ ⡼⠣⠸⣴⢻ ⢰ ⡧⡧⡿⡠ ⢸⡤ ⡮⡧⢸ ⣸ ⡧⡧ ⢸⡧ ⢸⡤ ⡠ ⡧⡧ ⢠⢣ ⢠ ⡼⣿⢧⡠⢰ ⢰ ⡧ ⡦⡸⡦ ⢸⡧⢸⡧ ⢰⡠⡠⢸⢸⡠ ⣧⠧ ⢸ ⡧ ⣸⢸⢸⣸ ⡧⡧ ⡧ ⢸⢱ + ⢸⢸ ⡧⣿⡧ ⢠⠿⢸⡼⢸ ⣧⢧⡼⡤⣿⢸⡧ ⣿⢸⢸ ⡧ ⠧ ⡧ ⣸ ⢠⠣⢧⠣⡧⡧ ⢸⡧ ⢠ ⡧⢸⣸ ⢠ ⡿⡤ ⡧⡧ ⢸⢸ ⡧⡧⣤ ⣧ ⣷⢣⡧ ⢸⢸ ⢸ ⡧ ⢸⣧⡸⡦ ⡼⡦ ⣷⢣ ⡧ ⡧⡧⡧ ⡮⡧⡸⢱ ⢸⢣⢣⢸⢸⡧ ⢸⠸ ⢷⡧ ⡧⠸⡮⡯⡦ ⡧⡧ ⢠⢠⢠⡧⡤ ⢰⡧ ⡼⢸ + ⡼⠸ ⣿⢸⢸⢠⢸ ⠸⠣⢸⣾⢰⠹⢸⡧⣧⠻⡧⠳⡠ ⡧⡧⢸ ⡸⣤⠧ ⡧ ⢠⡧⡧⢸ ⠨ ⣧⢧ ⡼⡧ ⢸ ⣠⠣⢸⡯⣶⢰⡤⢠⢦⢸ ⡧⡧ ⡧⢱ ⡮⢸⢰⡧⠩⠨⣶⢹⢰⠹⢸⢹ ⢸⠨⡦⡼⡠ ⡧ ⢸⡿⡧⡧⢸⠣⡧ ⡠ ⡯⢸ ⡧ ⣧⡧⢱ ⡧⡧⡧⢸⢰ ⡠ ⡮⠨⢸⢸⢸⣧ ⡿ ⠨⢣⢸ ⡧⠡⡧⡦⢠⠣⢱ ⢸⡾⠣⡧⡧⢠⣸⢸ ⡧⢸ +⡧⡤ ⢠⠧ ⠡ ⢸⢸⢸ ⠸ ⡿ ⠨ ⢻ ⡧ ⢱⢸ ⠨⣦⠣⠨ ⡧ ⣧⠧⠡⡧⢸ ⢻⢸ ⡧⢱ ⡮⡧⡯ ⡧⣿⢸⢧⢸⠸⣿ ⢰⠡⡧ ⡧⠨⡦⡧⢸⡸ ⠹⠨⣾ ⠨⢸ ⡧ ⠿⡧⡧ ⡤⣤⠣ ⢸ ⠡⢱⡧ ⡧ ⢠⢧⢠⠧⢸ ⣼⢸⣤⢠ ⢸⢸⠧⠨⢦ ⡧⡧⡧⢸⢸⡠⢠⢳ ⡧ ⢿⢸⢸⡼⢦ ⢠⠧ ⢸⡧ ⢱⢱⢸ ⢸ ⡮⠡ ⢸⢣⢸⢻⢸⢠⠧⢸ +⢸⢹ ⢰ ⢰ ⢸ ⢸⢸⡯ ⠡ ⠨ ⡧ ⠨⣾ ⠡ ⢱ ⡯ ⠸⡼ ⠨⢸ ⡧⢸⡦⡧⢻ ⠣⡧⡧⢸⡧ ⠫⣶⢰⣼ ⢸⢧⡧ ⡧⡧⠸⡧ ⠸ ⠨⡦⣠⡧ ⠡⣧⠿⠸⠸ ⢸ ⠨ ⢣ ⡸⢸⢸ ⠸⡤⣿⢸⠩⣾ ⢸⢸ ⠸⡠⡧⢱⠡⠨⡧⡧⡸⠨⡾⠡ ⠨⢸⠸ ⠸⡼⢻ ⢸ ⠨⢸⢸ ⠨⣶ ⡧ ⢸⢸⡸⠸⢸⡯ ⢸ + ⡧ ⡮⡧⡯⡦⣤⡠ ⢠⡧ ⡼ ⢸⡧⠡ ⠡ ⠡ ⣿ ⠡ ⠷⠡⠸⠸⠡ ⠧⠧ ⡿⠧⠨ ⠨⠸ ⢱⠣ ⠡ ⠷⠡ ⠨ ⠨ ⢸ ⢠⠣⠸⣸ ⣧⠣ ⠡⡧⢸⠨ ⣧⡧⠸ ⠧⠱⠣ ⠨ ⠧ ⡧⡧ ⠨⢸⡧ ⢸⠡ ⢸ + ⠸⢦ ⢠⠧⢱⠡⢳⠡⠨⠱⠪⢸⢠⠧ ⠸⡧ ⢸ ⡧ ⢸ ⢸⡠⡸ ⡿ ⢻ ⢣⡮ ⠡⠣ ⢹ ⠣ ⠨ ⠸ + ⠨⠶⠪⠱⠤⠤⠼ ⠩ ⠨⠧ ⠨ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +2007-05-07 2014 2020-12-30 +PKEuS +14┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + ⠩⠩⠩⠩⠩⠡ ⡧ ⢸ + ⡧ ⢸ ⢰ + ⡧⡠⡠ ⢸ ⢸ + ⡧⡧⡧ ⢸ ⣿ + ⢰ ⡦ ⡦ ⣷⡧⡧ ⢸ ⣿ ⡦ + ⡤ ⢸ ⢠⡧ ⢸⡧ ⢠ ⣿⡧⡧ ⡸⡧ ⣿ ⡧ + ⡧ ⢸ ⢸⡧ ⢸⡧ ⢸ ⣿⢣⡧ ⡧⡧ ⡯⡦ ⡧ + ⡧ ⣿⢸⡦ ⢸⡧ ⢸⡧ ⣾ ⣿⢸⢧ ⢸⡧⡧ ⣷⠡⡧ ⡧ ⢸ + ⡧ ⣤⢿⢸⣧ ⢸⡧ ⢸⢣ ⣿ ⢠⠿⢸⢸ ⢸⡧⡧ ⣿ ⡧ ⢸⡧ ⢸ + ⡧ ⣿⢸⡼⡿⡠ ⢸⢱ ⢸⢸ ⣿ ⢸ ⢸⢸ ⢸⡧⡧ ⣿ ⡧ ⢸⡧ ⢸ + ⢠⢿ ⢸⠨⠸⡧⠡⡧ ⡧⢸ ⣧ ⢸⢸ ⡧⣿⢸ ⠨⢸ ⢸⡧⡧⢸ ⡯ ⡧ ⢸⡧ ⢸⡠ ⢸ + ⢰ ⢸⢸⡦ ⢸ ⡧ ⢧ ⡧⢸ ⢰ ⡧⢣ ⡼⢸ ⡧⣿⢸ ⢸ ⢸⠣⡧⣼⢰ ⢰⡦ ⡧ ⡧ ⢸⢣ ⢸⡧ ⡦ ⢠⢦ ⢸ + ⢸ ⢸⢸⡧ ⡮ ⡧ ⢸ ⡧⢸ ⢸ ⡠ ⡧ ⢣ ⡧⢸ ⡧⣿⢸ ⢸ ⡮ ⢧⡯⣾ ⢸⢧ ⡧ ⢧ ⢸⢸ ⡮⡧ ⢠ ⢠ ⢰⡧ ⡸⠸⡤ ⡯⡦ + ⣿ ⢸⢸⡧ ⡧ ⡧ ⢸ ⡧⢸ ⢸ ⢠⡧ ⢠⠧ ⢸ ⡧⠸⣼ ⣿⢸ ⠸⡤ ⡧ ⢸⡧⣿ ⡼⢸⢸ ⢸ ⡼⢸ ⡧⡧ ⢸ ⢸ ⢸⡧ ⢠⠣ ⢣ ⡧⡧ + ⡯⡦ ⣾⠸⡧ ⡧ ⠣ ⢸ ⡧⠸⡦⢸⡦ ⢸⡧⡦⢸ ⢸ ⡧ ⣿ ⠫⣾ ⡧⣶⡧ ⢸⠡⢻⢰ ⡧⠸⣾ ⠸⡦⡧⢸ ⡧⡧ ⢸ ⢸ ⢰⡮⡧ ⡮ ⠨⡦ ⡧⡧ ⢰⡤ ⣴ + ⡤⢠⠧⣧⢠⠼⠸ ⢸⢸ ⡧⣤⠧ ⢸⡧⡧ ⡧⢧⡧⢸ ⠸⡠ ⡧ ⢿ ⣿ ⡧⡿⡧ ⠸ ⣿⡧ ⡧ ⣿ ⠸⠧⢸ ⡧⢧ ⡧⡧ ⢠ ⡸⡠ ⢸⠧⢸⢠⠤⠤⠴⠡ ⢸ ⣤⠣⡧ ⡧⠸⡤ ⢠⠮⢸ + ⡧⢸ ⣿⢸ ⢸⢸ ⡧⣿ ⢸⡧⡧⢠⠣⢸⢣⢸ ⡧ ⡧ ⢸ ⣿ ⢣⠣⡧ ⡿⢣⢠⠣ ⢻ ⢸ ⡧⢸ ⡧⡧ ⡼⡧⡧⡧ ⡼ ⢸⡼ ⢧ ⡼⢹ ⢣ ⢸ ⠸⡤ ⢠⠣ ⠸⡤ + ⢰⢹⡼ ⠡⡯ ⢸⡯ ⢷⢹ ⡧⠩⠩ ⢸⢸⢸ ⡧⡰⠡ ⠨ ⣿ ⢸ ⠡ ⡧⢸⢸ ⢸ ⢸ ⢠⠣⠨⠩⠡⢧⢠⠣⢸⡧⣿⡠⡧ ⢸⡧ ⠸⡠⡼ ⠨ ⢸⡷⢤⡠ ⢠⡠⠲⢧⡧ ⠸⡤ ⡰⠡ ⡧ + ⢠⣠⣠⣠⣠⣠⣠⣠⣠⣠⡠⠤⠤⠤⠤⠼⠸⠡ ⠧ ⠸ ⠸⠸ ⠧ ⠸⠸⠧ ⠿ ⠧ ⠸ ⠧ ⠧ ⠸ ⠸⠤⠣ ⠸⠣ ⠧⠧⠧⠧ ⠸ ⠿ ⠸ ⠨⠢⠤⠤⠤⠤⠤⠤⠲⠡ ⠸ ⠸⠼ ⠧ + ⢠⣠⣠⣠⣠⣠⣠⣠⣠⣠⡠⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠲⠲⠲⠲⠲⠲⠲⠲⠲⠲⠩⠩⠩⠩⠩⠩⠩⠩⠩⠩⠡ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +2007-05-07 2014 2020-12-30 + +commit e2facf9d06fe71e5992132faedcb80f2aac97901 creates a barchart: █ █ diff --git a/git-stats.c b/git-stats.c @@ -1,5 +1,6 @@ #! /usr/bin/env sheepy -/* or direct path to sheepy: #! /usr/local/bin/sheepy */ +/* -./; + * or direct path to sheepy: #! /usr/local/bin/sheepy */ /* Libsheepy documentation: https://spartatek.se/libsheepy/ */ #include "libsheepyObject.h" @@ -28,16 +29,10 @@ winSizet wsize (void) return (winSizet){w.ws_row, w.ws_col}; } -int main(int ARGC, char** ARGV) { - - argc = ARGC; argv = ARGV; - - initLibsheepy(ARGV[0]); - setLogMode(LOG_DATE); - //openProgLogFile(); - //setLogSymbols(LOG_UTF8); - //disableLibsheepyErrorLogs; +time_t earliest, latest; +char *firstDate = null, *lastDate = null; +void drawGraph(char *from, char *gitopts, bool setTimeRange) { // Steps // get git statistics @@ -52,20 +47,24 @@ int main(int ARGC, char** ARGV) { // fill the display with the plot // show plot - char *from = null; - if (argc > 1) { - from = argv[1]; - } - // get git statistics - cleanListP(dates) = execOut("git log --pretty=format:\"%ai\""); + cleanListP(dates) = execOutf("git log %s --pretty=format:\"%%ai\"", gitopts); // sort the dates sortG(&dates); - int cut = 0; + char *ld = getG(dates, unusedV, -1); + ld[4] = 0; + if (parseInt(ld) < parseInt(from)) /* the last date is earlier than from, stop. */ return; + ld[4] = '-'; + + int cut = 0; + int fromYear = parseInt(from); enumerateS(dates, s, i) { - if (startsWithG(s, from)) { + s[4] = 0; + int year = parseInt(s); + s[4] = '-'; + if (year >= fromYear) { cut = i; break; } @@ -75,6 +74,8 @@ int main(int ARGC, char** ARGV) { delG(&dates, 0, cut); } + if (lenG(dates) < 2) /* skip plot only 1 commit */ return; + // remove time, keep only the date in a slice: slDates // count daily commits typ struct { @@ -118,10 +119,12 @@ int main(int ARGC, char** ARGV) { } // compute earliest and latest dates in unix time - time_t earliest, latest; - - earliest = strToUnixTime(sliceFirst(&slDates).date, "%Y-%m-%d"); - latest = strToUnixTime(sliceLast (&slDates).date, "%Y-%m-%d"); + if (setTimeRange) { + earliest = strToUnixTime(sliceFirst(&slDates).date, "%Y-%m-%d"); + latest = strToUnixTime(sliceLast (&slDates).date, "%Y-%m-%d"); + firstDate = strdup(sliceFirst(&slDates).date); + lastDate = strdup(sliceLast (&slDates).date); + } //logI("%"PRIu64" %"PRIu64" = %f", earliest, latest, (f64)((latest-earliest)/86400)/365); @@ -244,8 +247,8 @@ int main(int ARGC, char** ARGV) { printf(HORIZONTAL_DASH); } put; - int col = strlen(sliceFirst(&slDates).date); - printf(BLD"%s"RST, sliceFirst(&slDates).date); + int col = strlen(firstDate); + printf(BLD"%s"RST, firstDate); time_t halfTime = (earliest+latest)/2; char hT[5]; bLTimeToYMDS(hT, sizeof(hT), halfTime); @@ -253,12 +256,45 @@ int main(int ARGC, char** ARGV) { col += spaces + 4 /*halfTime string length*/; range(i, spaces) printf(" "); printf(BLD"%s"RST, hT); - spaces = wz.cols - strlen(sliceLast(&slDates).date) - col; + spaces = wz.cols - strlen(lastDate) - col; range(i, spaces) printf(" "); - printf(BLD"%s"RST, sliceLast(&slDates).date); + printf(BLD"%s"RST, lastDate); sliceFree(&slPl); sliceFree(&slDates); } + +int main(int ARGC, char** ARGV) { + + argc = ARGC; argv = ARGV; + + initLibsheepy(ARGV[0]); + setLogMode(LOG_DATE); + //openProgLogFile(); + //setLogSymbols(LOG_UTF8); + //disableLibsheepyErrorLogs; + + + char *from = null; + if (argc > 1) { + from = argv[1]; + } + + drawGraph(from, "", /*setTimeRange*/ yes); + + // get list of authors + // git shortlog -sn + cleanListP(authors) = execOut("git shortlog -sn"); + + // get commit dates for each author + // git log --author="Remy" --pretty=format:"%ai" + forEachS(authors, l) { + puts(&l[7]); + cleanCharP(auth) = formatS("--author=\"%s\"", &l[7]); + drawGraph(from, auth, /*setTimeRange*/ no); + } + + freeManyS(firstDate, lastDate); +} // vim: set expandtab ts=2 sw=2: diff --git a/package.yml b/package.yml @@ -0,0 +1,37 @@ +--- + name: git-stats + version: 0.0.1 + description: "Show plots of git commits over time" + bin: ./git-stats.c + #cflags: -DA -g3 -std=gnu11 -fPIC -pipe + #lflags: -lpcre + repository: + type: git + url: git+https://github.com/USER/git-stats.git + keywords: + - utility + - command + author: Remy Noulin + license: MIT + bugs: + url: https://noulin.net + homepage: https://noulin.net + #compileHelp: # text displayed when there is a compilation error + #dependencies: + # md4c: + # Test configuration: + #testBin: ./testGit-stats.c + #testCflags: -g3 -std=gnu11 -fPIC -pipe -fprofile-arcs -ftest-coverage -Wall -Wextra + #testLflags: -lcheck_pic -lrt -lm -lsubunit -fprofile-arcs -ftest-coverage -rdynamic + # Memcheck configuration (sheepy -m): + #memcheckBin: ./memcheckGit-stats.c + #memcheckCmd: valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 + #memcheckCflags: -g3 -std=gnu11 -fPIC -pipe + #memcheckLflags: -rdynamic + # The asan* are the options for building the libsasan tests (sheepy -a): + #asanBin: ./asanGit-stats.c + #asanCmd: env ASAN_OPTIONS="detect_leaks=1:detect_stack_use_after_return=1:halt_on_error=0:log_path=stdout:color=always:print_cmdline=1" + #asanCflags: -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address # these flags are overriden by package.yml + #asanLflags: -rdynamic -fsanitize=address -lasan # these flags are overriden by package.yml + #documentationCmd: # command for generating the documentation with spm doc + private: false # true for private package