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 2bb5384da11a7487e29b2354c8e5a00dcefea2c3
parent 4f3c7fe232f50f0ea15a2aeab7ba910f0276eaa7
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sat,  5 Jun 2021 16:09:56 +0200

add option bar to show barchart instead of plot

README.md   |   7 ++-
git-stats.c | 183 ++++++++++++++++++++++++++++++++++++++++--------------------
2 files changed, 128 insertions(+), 62 deletions(-)

Diffstat:
MREADME.md | 7++++++-
Mgit-stats.c | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
2 files changed, 128 insertions(+), 62 deletions(-)

diff --git a/README.md b/README.md @@ -1,4 +1,9 @@ -This version creates a plot: +This version adds the option 'bar' to show a barchart instead of a dot plot + +git-stats bar +git-stats 2021 bar + +commit 4f3c7fe232f50f0ea15a2aeab7ba910f0276eaa7 creates a plot: 50┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ ⡧ diff --git a/git-stats.c b/git-stats.c @@ -21,8 +21,7 @@ typ struct {u16 rows; u16 cols;} winSizet; #include <unistd.h> // get terminal window size -winSizet wsize (void) -{ +winSizet wsize (void) { struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); @@ -32,7 +31,7 @@ winSizet wsize (void) time_t earliest, latest; char *firstDate = null, *lastDate = null; -void drawGraph(char *from, char *gitopts, bool setTimeRange) { +void drawGraph(char *from, char *gitopts, bool setTimeRange, bool barchart) { // Steps // get git statistics @@ -54,6 +53,7 @@ void drawGraph(char *from, char *gitopts, bool setTimeRange) { sortG(&dates); char *ld = getG(dates, unusedV, -1); + if (!ld) /*dates is empty, stop.*/ return; ld[4] = 0; if (parseInt(ld) < parseInt(from)) /* the last date is earlier than from, stop. */ return; ld[4] = '-'; @@ -149,7 +149,11 @@ void drawGraph(char *from, char *gitopts, bool setTimeRange) { sliceForEach(&slDates, date) { time_t t = strToUnixTime(date->date, "%Y-%m-%d"); - int col = (f64)(t-earliest)/(f64)(latest-earliest) * (f64)(wz.cols*2-1/*-1 to make sure it writes inside the display*/); + int col; + if (barchart) + col = (f64)(t-earliest)/(f64)(latest-earliest) * (f64)(wz.cols-1/*-1 to make sure it writes inside the display*/); + else + col = (f64)(t-earliest)/(f64)(latest-earliest) * (f64)(wz.cols*2-1/*-1 to make sure it writes inside the display*/); if (col == p.col) { p.value = MAX(date->count, p.value); } @@ -162,66 +166,107 @@ void drawGraph(char *from, char *gitopts, bool setTimeRange) { sliceAppend(&slPl, p); // create a blank display array + // create a blank display array + char *display[wz.cols][graphHeight]; + + char *BARS[] = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}; + u32 dotDisplay[wz.cols][graphHeight]; - // clear dotDisplay - range(i, wz.cols) { - range(j, graphHeight) { - dotDisplay[i][j] = ' '; + if (barchart) { + // clear display + range(i, wz.cols) { + range(j, graphHeight) { + display[i][j] = BARS[0]; + } } } - - // for more information about braille, see: - // https://en.wikipedia.org/wiki/Braille_Patterns - const u32 brailleOffset = 0x2800; - const u32 braille[4][2] = { - {0x01, 0x08}, - {0x02, 0x10}, - {0x04, 0x20}, - {0x40, 0x80}, - }; - - void setPoint(i32 x, i32 y) { - dotDisplay[x/2][y/4] |= braille[y%4][x%2] + brailleOffset; + else { + // clear dotDisplay + range(i, wz.cols) { + range(j, graphHeight) { + dotDisplay[i][j] = ' '; + } + } } - void line(i32 x0, i32 y0, i32 x1, i32 y1) { - // bresenham's line algorithm - // https://en.wikipedia.org/wiki/Bresenham's_line_algorithm - i32 dx = abs(x1-x0); - i32 sx = x0<x1 ? 1 : -1; - i32 dy = -abs(y1-y0); - i32 sy = y0<y1 ? 1 : -1; - // error value e_xy - i32 err = dx+dy; - loopBreakerInit; - forever { - loopBreaker(10000); - setPoint(x0, y0); - if (x0 == x1 and y0 == y1) break; - i32 e2 = 2*err; - if (e2 >= dy) { - // e_xy+e_x > 0 - err += dy; - x0 += sx; + if (barchart) { + // fill the display with the plot + sliceForEach(&slPl, elm) { + int dpHeight = (f64)(graphHeight*8) - (f64)elm->value/(f64)maxCount * (f64)(graphHeight*8); + int bits = dpHeight & 7; + int cbar = 8 - bits; + + // draw bar + int h = dpHeight / 8; + + if (bits) { + // print BARS[cbar] + display[elm->col][h] = BARS[cbar]; + inc h; } - if (e2 <= dx) { - // e_xy+e_y < 0 - err += dx; - y0 += sy; + + rangeFrom(i, h, graphHeight) { + display[elm->col][i] = BARS[8]; } + + //logI("col %d, value %d", elm->col, elm->value); + //logI("col %3d, value %4d %3d cbar %d bits %d", elm->col, dpHeight, h, cbar, bits); } } + else { + // for more information about braille, see: + // https://en.wikipedia.org/wiki/Braille_Patterns + const u32 brailleOffset = 0x2800; + const u32 braille[4][2] = { + {0x01, 0x08}, + {0x02, 0x10}, + {0x04, 0x20}, + {0x40, 0x80}, + }; + + void setPoint(i32 x, i32 y) { + dotDisplay[x/2][y/4] |= braille[y%4][x%2] + brailleOffset; + } + + void line(i32 x0, i32 y0, i32 x1, i32 y1) { + // bresenham's line algorithm + // https://en.wikipedia.org/wiki/Bresenham's_line_algorithm + i32 dx = abs(x1-x0); + i32 sx = x0<x1 ? 1 : -1; + i32 dy = -abs(y1-y0); + i32 sy = y0<y1 ? 1 : -1; + // error value e_xy + i32 err = dx+dy; + loopBreakerInit; + forever { + loopBreaker(10000); + setPoint(x0, y0); + if (x0 == x1 and y0 == y1) break; + i32 e2 = 2*err; + if (e2 >= dy) { + // e_xy+e_x > 0 + err += dy; + x0 += sx; + } + if (e2 <= dx) { + // e_xy+e_y < 0 + err += dx; + y0 += sy; + } + } + } - // fill the display with the plot - sliceEnumerate(&slPl, sli, elm) { - if (sli > 0) { - int dpHeight = (f64)(graphHeight*4) - (f64)elm->value/(f64)maxCount * (f64)(graphHeight*4); - int prevHeight = (f64)(graphHeight*4) - (f64)sliceAt(&slPl,sli-1).value/(f64)maxCount * (f64)(graphHeight*4); - line(sliceAt(&slPl,sli-1).col, prevHeight, elm->col, dpHeight); + // fill the display with the plot + sliceEnumerate(&slPl, sli, elm) { + if (sli > 0) { + int dpHeight = (f64)(graphHeight*4) - (f64)elm->value/(f64)maxCount * (f64)(graphHeight*4); + int prevHeight = (f64)(graphHeight*4) - (f64)sliceAt(&slPl,sli-1).value/(f64)maxCount * (f64)(graphHeight*4); + line(sliceAt(&slPl,sli-1).col, prevHeight, elm->col, dpHeight); + } + //logI("col %d, value %d", elm->col, elm->value); } - //logI("col %d, value %d", elm->col, elm->value); } // show plot @@ -235,13 +280,23 @@ void drawGraph(char *from, char *gitopts, bool setTimeRange) { printf(HORIZONTAL_DASH); } put; - range(j, graphHeight) { - range(i, wz.cols) { - char dots[10] = {0}; - bRune2CodeUTF8(dots, dotDisplay[i][j]); - printf(BLD"%s"RST, dots); + if (barchart) { + range(j, graphHeight) { + range(i, wz.cols) { + printf(display[i][j]); + } + put; + } + } + else { + range(j, graphHeight) { + range(i, wz.cols) { + char dots[10] = {0}; + bRune2CodeUTF8(dots, dotDisplay[i][j]); + printf(BLD"%s"RST, dots); + } + put; } - put; } range(i, wz.cols) { printf(HORIZONTAL_DASH); @@ -276,12 +331,18 @@ int main(int ARGC, char** ARGV) { //disableLibsheepyErrorLogs; - char *from = null; + char *from = null; + bool barchart = no; if (argc > 1) { - from = argv[1]; + if (isInt(argv[1])) { + from = argv[1]; + barchart = argv[2] and eqG(argv[2], "bar"); + } + else + barchart = argv[1] and eqG(argv[1], "bar"); } - drawGraph(from, "", /*setTimeRange*/ yes); + drawGraph(from, "", /*setTimeRange*/ yes, barchart); // get list of authors // git shortlog -sn @@ -292,7 +353,7 @@ int main(int ARGC, char** ARGV) { forEachS(authors, l) { puts(&l[7]); cleanCharP(auth) = formatS("--author=\"%s\"", &l[7]); - drawGraph(from, auth, /*setTimeRange*/ no); + drawGraph(from, auth, /*setTimeRange*/ no, barchart); } freeManyS(firstDate, lastDate);