bcf2385e0d128af41b2c21c2ffb1a7793b405c12 galt Tue Jun 15 23:39:48 2010 -0700 moving files to new home under kent diff --git git-reports.c git-reports.c deleted file mode 100644 index 9569301..0000000 --- git-reports.c +++ /dev/null @@ -1,848 +0,0 @@ -/* git-reports.c for creating git equivalent of cvs-reports. - * - * Copyright Galt Barber 2010. - * - * Anyone is free to use it. - * Just include me in your credits. - * Likewise cvs-reports was created by Mark Diekhans. - */ - -#include "common.h" -#include "options.h" -#include "dystring.h" -#include "errabort.h" -#include "hash.h" -#include "linefile.h" -#include "htmshell.h" -#include "portable.h" - -static char const rcsid[] = "$Id: git-reports.c,v 1.1 2010/03/02 08:43:07 galt Exp $"; - - -struct hash *userHash = NULL; -struct slName *users = NULL; - -char *startTag = NULL; -char *endTag = NULL; -char *startDate = NULL; -char *endDate = NULL; -char *title = NULL; -char *repoDir = NULL; -char *outDir = NULL; -char *outPrefix = NULL; - -char gitCmd[1024]; -char *tempMakeDiffName = NULL; - -struct files - { - struct files *next; - char type; - char *path; - int linesChanged; - }; - -struct commit - { - struct commit *next; - int commitNumber; // used for sorting fileviews - char *commitId; - char *author; - char *date; - char *comment; - struct files *files; - }; - - -struct comFile - { - struct comFile *next; - struct files *f; - struct commit *commit; - }; - -void usage(char *msg) -/* Explain usage and exit. */ -{ -errAbort( - "%s\n\n" - "git-reports - produce source code reports useful for code-review on git repository \n" - "\n" - "Usage:\n" - " git-reports startTag endTag startDate endDate title repoDir outDir outPrefix\n" - "where " - " startTag and endTag are repository tags marking the beginning and end of the git range\n" - " startDate and endDate and title are just strings that get printed on the report\n" - " title is usually the branch number, e.g. v225\n" - " repoDir is where the git repository (use absolute path)\n" - " outDir is the output directory (use absolute path).\n" - " outPrefix is typically \"branch\" or \"review\" directory.\n" - " --help - this help screen\n", - msg); -} - -static struct optionSpec options[] = -{ - {"-help", OPTION_BOOLEAN}, - {NULL, 0}, -}; - -void runShell(char *cmd) -/* Run a command and do simple error-checking */ -{ -int exitCode = system(cmd); -if (exitCode != 0) - errAbort("system command [%s] failed with exitCode %d", cmd, exitCode); -} - -struct commit* getCommits() -/* Get all commits from startTag to endTag */ -{ -int numCommits = 0; -safef(gitCmd,sizeof(gitCmd), "" -"git log %s..%s --name-status > commits.tmp" -, startTag, endTag); -runShell(gitCmd); -struct lineFile *lf = lineFileOpen("commits.tmp", TRUE); -int lineSize; -char *line; -struct commit *commits = NULL, *commit = NULL; -struct files *files = NULL, *f = NULL; -char *sep = ""; -while (lineFileNext(lf, &line, &lineSize)) - { - boolean isMerge = FALSE; - char *w = nextWord(&line); - AllocVar(commit); - if (!sameString("commit", w)) - errAbort("expected keyword commit parsing commits.tmp\n"); - commit->commitId = cloneString(nextWord(&line)); - commit->commitNumber = ++numCommits; - - lineFileNext(lf, &line, &lineSize); - w = nextWord(&line); - if (sameString("Merge:", w)) - { - isMerge = TRUE; - lineFileNext(lf, &line, &lineSize); - w = nextWord(&line); - } - if (!sameString("Author:", w)) - errAbort("expected keyword Author: parsing commits.tmp\n"); - - /* by request, keep just the email account name */ - char *lc = strchr(line, '<'); - if (!lc) - errAbort("expected '<' char in email address in Author: parsing commits.tmp\n"); - ++lc; - char *rc = strchr(lc, '>'); - if (!rc) - errAbort("expected '>' char in email address in Author: parsing commits.tmp\n"); - char *ac = strchr(lc, '@'); - if (ac) - rc = ac; - commit->author = cloneStringZ(lc, rc-lc); - - lineFileNext(lf, &line, &lineSize); - w = nextWord(&line); - if (!sameString("Date:", w)) - errAbort("expected keyword Date: parsing commits.tmp\n"); - commit->date = cloneString(line); - - lineFileNext(lf, &line, &lineSize); - if (!sameString("", line)) - errAbort("expected blank line parsing commits.tmp\n"); - - /* collect the comment-lines */ - struct dyString *dy = NULL; - dy = dyStringNew(0); - sep = ""; - files = NULL; - while (lineFileNext(lf, &line, &lineSize)) - { - if (sameString("", line)) - break; - w = skipLeadingSpaces(line); - dyStringPrintf(dy, "%s%s", w, sep); - sep = "\n"; - } - commit->comment = cloneString(dy->string); - freeDyString(&dy); - - if (!isMerge) - { - /* collect the files-list */ - while (lineFileNext(lf, &line, &lineSize)) - { - if (sameString("", line)) - break; - AllocVar(f); - w = nextWord(&line); - f->type = w[0]; - f->path = cloneString(line); - slAddHead(&files, f); - } - slReverse(&files); - } - - commit->files = files; - - slAddHead(&commits, commit); - - verbose(2, - "commitId: %s\n" - "author: %s\n" - "date: %s\n" - "comment: [%s]\n" - "file(s): \n" -, commit->commitId -, commit->author -, commit->date -, commit->comment); - - for (f=commit->files; f; f = f->next) - { - verbose(2, "%c %s\n", f->type, f->path); - - // anything other than M or A? - if (f->type != 'M' && f->type != 'A' ) - verbose(2, "special type: %c %s\n", f->type, f->path); - } - - - verbose(2, "------------\n"); - - } -lineFileClose(&lf); -slReverse(&commits); - - -unlink("commits.tmp"); -return commits; -} - -int makeHtml(char *diffPath, char *htmlPath, char *path, char *commitId) -/* Make a color-coded html diff - * Return the number of lines changed */ -{ -int linesChanged = 0; - -FILE *h = mustOpen(htmlPath, "w"); -struct lineFile *lf = lineFileOpen(diffPath, TRUE); -int lineSize; -char *line; -char *xline = NULL; -char fmtString[256]; -boolean inBody = FALSE; -boolean inBlock = TRUE; -int blockP = 0, blockN = 0; -fprintf(h, "<html>\n<head>\n<title>%s %s</title>\n</head>\n</body>\n<pre>\n", path, commitId); -boolean hasMore = TRUE; -while (hasMore) - { - boolean checkEob = FALSE; - hasMore = lineFileNext(lf, &line, &lineSize); - if (hasMore) - { - char *color = NULL; - xline = htmlEncode(line); - if (line[0] == '-') - { - color = "#FF9999"; /* deleted text light red */ - if (inBody) - { - inBlock = TRUE; - ++blockN; - } - } - else if (line[0] == '+') - { - color = "#99FF99"; /* added text light green */ - if (inBody) - { - inBlock = TRUE; - ++blockP; - } - } - else - { - if (line[0] == '@') - color = "#FFFF99"; /* diff control text light yellow (red+green) */ - checkEob = TRUE; - } - if (color) - safef(fmtString, sizeof(fmtString), "<span style=\"background-color:%s\">%%s</span>\n", color); - else - safef(fmtString, sizeof(fmtString), "%%s\n"); - fprintf(h, fmtString, xline); - - if (line[0] == '@') - inBody = TRUE; - - freeMem(xline); - } - else - { - checkEob = TRUE; - } - - if (checkEob && inBlock) - { - inBlock = FALSE; - if (blockP >= blockN) - linesChanged += blockP; - else - linesChanged += blockN; - blockP = 0; - blockN = 0; - } - - } - -lineFileClose(&lf); -fprintf(h, "</pre>\n</body>\n</html>\n"); -fclose(h); -return linesChanged; -} - - -void makeDiffAndSplit(struct commit *c, char *u, boolean full) -/* Generate a full diff and then split it up into its parts. - * This was motivated because no other way to show deleted files - * since they are not in repo and git paths must actually exist - * in working repo dir. However leaving off the path produces - * a diff with everything we want, we just have to split it up. */ -{ -safef(gitCmd,sizeof(gitCmd), - "git diff -b -w --no-prefix%s %s^! > %s" - , full ? " --unified=10000" : "" - , c->commitId, tempMakeDiffName); -//git shorthand: x^! is equiv to range x^ x, -// i.e. just the one commit and nothing more. - -runShell(gitCmd); - - -// now parse it and split it into separate files with the right path. -struct lineFile *lf = lineFileOpen(tempMakeDiffName, TRUE); -int lineSize; -char *line; -FILE *h = NULL; -while (lineFileNext(lf, &line, &lineSize)) - { - if (startsWith("diff --git ", line)) - { - if (h) - { - fclose(h); - h = NULL; - } - char *fpath = line + strlen("diff --git "); - fpath = strchr(fpath, ' '); - ++fpath; // now we should be pointing to the world - - char path[1024]; - char *r = strrchr(fpath, '/'); - if (r) - { - *r = 0; - /* make internal levels of subdirs */ - safef(path, sizeof(path), "mkdir -p %s/%s/%s/%s/%s/%s", outDir, outPrefix, "user", u, full ? "full" : "context", fpath); - runShell(path); - *r = '/'; - } - safef(path, sizeof(path), "%s/%s/%s/%s/%s/%s%s.diff" - , outDir, outPrefix, "user", u, full ? "full" : "context", fpath, c->commitId); - - h = mustOpen(path, "w"); - fprintf(h, "%s\n", c->commitId); - fprintf(h, "%s\n", c->author); - fprintf(h, "%s\n", c->date); - fprintf(h, "%s\n", c->comment); - } - else if (startsWith("@@", line)) - { - char *end = strchr(line+2, '@'); - *(end+2) = 0; // chop the weird unwanted context string from here following e.g. - //@@ -99,7 +99,9 @@ weird unwanted context string here - // converts to - //@@ -99,7 +99,9 @@ - // saves 17 seconds over the more expensive sed command - } - if (h) - fprintf(h, "%s\n", line); - } -if (h) - { - fclose(h); - h = NULL; - } -lineFileClose(&lf); -} - - -void doUserCommits(char *u, struct commit *commits, int *saveUlc, int *saveUfc) -/* process one user, commit-view */ -{ - - -char userPath[1024]; -safef(userPath, sizeof(userPath), "%s/%s/%s/%s/index.html", outDir, outPrefix, "user", u); - -FILE *h = mustOpen(userPath, "w"); -fprintf(h, "<html>\n<head>\n<title>Commits for %s</title>\n</head>\n</body>\n", u); -fprintf(h, "<h1>Commits for %s</h1>\n", u); - -fprintf(h, "switch to <A href=\"index-by-file.html\">files view</A>, <A href=\"../index.html\">user index</A>\n"); -fprintf(h, "<h3>%s to %s (%s to %s) %s</h3>\n", startTag, endTag, startDate, endDate, title); - -fprintf(h, "<ul>\n"); - - -int userLinesChanged = 0; -int userFileCount = 0; - -char *cDiff = NULL, *cHtml = NULL, *fDiff = NULL, *fHtml = NULL; -char *relativePath = NULL; -char *commonPath = NULL; - -struct commit *c = NULL; -struct files *f = NULL; -for(c = commits; c; c = c->next) - { - if (sameString(c->author, u)) - { - //fprintf(h, "%s\n", c->commitId); - //fprintf(h, "%s\n", c->date); - - char *cc = htmlEncode(c->comment); - char *ccc = replaceChars(cc, "\n", "<br>\n"); - fprintf(h, "<li>%s\n", ccc); - freeMem(cc); - freeMem(ccc); - - makeDiffAndSplit(c, u, FALSE); - makeDiffAndSplit(c, u, TRUE); - for(f = c->files; f; f = f->next) - { - char path[1024]; - - // context unified - safef(path, sizeof(path), "%s/%s%s", "context", f->path, c->commitId); - relativePath = cloneString(path); - - safef(path, sizeof(path), "%s/%s/%s/%s/%s", outDir, outPrefix, "user", u, relativePath); - commonPath = cloneString(path); - - safef(path, sizeof(path), "%s.html", commonPath); - cHtml = cloneString(path); - - safef(path, sizeof(path), "%s.diff", commonPath); - cDiff = cloneString(path); - - // make context html page - f->linesChanged = makeHtml(cDiff, cHtml, f->path, c->commitId); - - userLinesChanged += f->linesChanged; - ++userFileCount; - - freeMem(cDiff); - freeMem(cHtml); - safef(path, sizeof(path), "%s.html", relativePath); - cHtml = cloneString(path); - safef(path, sizeof(path), "%s.diff", relativePath); - cDiff = cloneString(path); - - - - // full text (up to 10,000 lines) - freeMem(relativePath); - safef(path, sizeof(path), "%s/%s%s", "full", f->path, c->commitId); - relativePath = cloneString(path); - - safef(path, sizeof(path), "%s/%s/%s/%s/%s", outDir, outPrefix, "user", u, relativePath); - freeMem(commonPath); - commonPath = cloneString(path); - - safef(path, sizeof(path), "%s.html", commonPath); - fHtml = cloneString(path); - - safef(path, sizeof(path), "%s.diff", commonPath); - fDiff = cloneString(path); - - - //git show --unified=10000 11a20b6cd113d75d84549eb642b7f2ac7a2594fe src/utils/qa/weeklybld/buildEnv.csh - - // make full html page - makeHtml(fDiff, fHtml, f->path, c->commitId); - - freeMem(fDiff); - freeMem(fHtml); - safef(path, sizeof(path), "%s.html", relativePath); - fHtml = cloneString(path); - safef(path, sizeof(path), "%s.diff", relativePath); - fDiff = cloneString(path); - - // make file diff links - fprintf(h, "<ul><li> %s - lines changed %d, " - "context: <A href=\"%s\">html</A>, <A href=\"%s\">text</A>, " - "full: <A href=\"%s\">html</A>, <A href=\"%s\">text</A></li></ul>\n" - , f->path, f->linesChanged - , cHtml, cDiff, fHtml, fDiff); - - freeMem(relativePath); - freeMem(commonPath); - freeMem(cDiff); - freeMem(cHtml); - freeMem(fDiff); - freeMem(fHtml); - - } - fprintf(h, "</li>\n"); - } - } -fprintf(h, "</ul>\n"); -fprintf(h, "switch to <A href=\"index-by-file.html\">files view</A>, <A href=\"../index.html\">user index</A>\n"); -fprintf(h, "</body>\n</html>\n"); -fclose(h); -*saveUlc = userLinesChanged; -*saveUfc = userFileCount; -} - - - - -int slComFileCmp(const void *va, const void *vb) -/* Compare two slNames. */ -{ -const struct comFile *a = *((struct comFile **)va); -const struct comFile *b = *((struct comFile **)vb); -int result = strcmp(a->f->path, b->f->path); -if (result == 0) - result = a->commit->commitNumber - b->commit->commitNumber; -return result; -} - - -void doUserFiles(char *u, struct commit *commits) -/* process one user's files-view (or all if u is NULL) */ -{ - -// http://hgwdev.cse.ucsc.edu/cvs-reports/branch/user/galt/index-by-file.html -// if u is NULL -// http://hgwdev.cse.ucsc.edu/cvs-reports/branch/file/index.html -char userPath[1024]; -if (u) - safef(userPath, sizeof(userPath), "%s/%s/%s/%s/index-by-file.html", outDir, outPrefix, "user", u); -else - safef(userPath, sizeof(userPath), "%s/%s/%s/index.html", outDir, outPrefix, "file"); - -FILE *h = mustOpen(userPath, "w"); -if (u) - { - fprintf(h, "<html>\n<head>\n<title>File Changes for %s</title>\n</head>\n</body>\n", u); - fprintf(h, "<h1>File Changes for %s</h1>\n", u); - fprintf(h, "switch to <A href=\"index.html\">commits view</A>, <A href=\"../index.html\">user index</A>"); - } -else - { - fprintf(h, "<html>\n<head>\n<title>All File Changes</title>\n</head>\n</body>\n"); - fprintf(h, "<h1>All File Changes</h1>\n"); - } - -fprintf(h, "<h3>%s to %s (%s to %s) %s</h3>\n", startTag, endTag, startDate, endDate, title); - -fprintf(h, "<ul>\n"); - - -int totalLinesChanged = 0; -int totalFileCount = 0; - -char *cDiff = NULL, *cHtml = NULL, *fDiff = NULL, *fHtml = NULL; -char *relativePath = NULL; - -struct commit *c = NULL; -struct files *f = NULL; - -struct comFile *comFiles = NULL, *cf = NULL; - -// pre-filter for u if u is not NULL -for(c = commits; c; c = c->next) - { - if (!u || (u && sameString(c->author, u))) - { - for(f = c->files; f; f = f->next) - { - AllocVar(cf); - cf->f = f; - cf->commit = c; - slAddHead(&comFiles, cf); - } - } - } -// sort by file path, and then by reverse commitNumber -// so that newest commit is on top. -slSort(&comFiles, slComFileCmp); - -char *lastPath = ""; -char *closure = ""; - -for(cf = comFiles; cf; cf = cf->next) - { - c = cf->commit; - f = cf->f; - - if (!sameString(f->path, lastPath)) - { - fprintf(h, "%s", closure); - lastPath = f->path; - fprintf(h, "<li>%s\n", f->path); - closure = "</li>\n"; - } - - - char path[1024]; - - // context unified - if (u) - safef(path, sizeof(path), "%s/%s%s", "context", f->path, c->commitId); - else - safef(path, sizeof(path), "../user/%s/%s/%s%s", c->author, "context", f->path, c->commitId); - relativePath = cloneString(path); - safef(path, sizeof(path), "%s.html", relativePath); - cHtml = cloneString(path); - safef(path, sizeof(path), "%s.diff", relativePath); - cDiff = cloneString(path); - - - - // full text (up to 10,000 lines) - freeMem(relativePath); - if (u) - safef(path, sizeof(path), "%s/%s%s", "full", f->path, c->commitId); - else - safef(path, sizeof(path), "../user/%s/%s/%s%s", c->author, "context", f->path, c->commitId); - relativePath = cloneString(path); - safef(path, sizeof(path), "%s.html", relativePath); - fHtml = cloneString(path); - safef(path, sizeof(path), "%s.diff", relativePath); - fDiff = cloneString(path); - - // make file view links - fprintf(h, "<ul><li>"); - fprintf(h, " lines changed %d, " - "context: <A href=\"%s\">html</A>, <A href=\"%s\">text</A>, " - "full: <A href=\"%s\">html</A>, <A href=\"%s\">text</A><br>\n" - , f->linesChanged - , cHtml, cDiff, fHtml, fDiff); - - //fprintf(h, " %s\n", c->commitId); - //fprintf(h, " %s\n", c->date); - char *cc = htmlEncode(c->comment); - char *ccc = replaceChars(cc, "\n", "<br>\n"); - fprintf(h, " %s\n", ccc); - freeMem(cc); - freeMem(ccc); - fprintf(h, "</li></ul>\n"); - - freeMem(relativePath); - freeMem(cDiff); - freeMem(cHtml); - freeMem(fDiff); - freeMem(fHtml); - - totalLinesChanged += f->linesChanged; - ++totalFileCount; - - fprintf(h, "\n"); - } -fprintf(h, "%s", closure); -fprintf(h, "</ul>\n"); -if (u) - { - fprintf(h, "switch to <A href=\"index.html\">commits view</A>, <A href=\"../index.html\">user index</A>"); - } -else - { - fprintf(h, "<ul>\n"); - fprintf(h, "<li> lines changed: %d</li>\n", totalLinesChanged); - fprintf(h, "<li> files changed: %d</li>\n", totalFileCount); - fprintf(h, "</ul>\n"); - } -fprintf(h, "</body>\n</html>\n"); -fclose(h); -} - - -void doMainIndex() -/* Create simple main index page */ -{ -char path[256]; -safef(path, sizeof(path), "%s/%s/index.html", outDir, outPrefix); - -FILE *h = mustOpen(path, "w"); -fprintf(h, "<html>\n<head>\n<title>Source Code Changes</title>\n</head>\n</body>\n"); -fprintf(h, "<h1>%s %s Changes</h1>\n", title, outPrefix); - -fprintf(h, "<h2>%s to %s (%s to %s) %s</h2>\n", startTag, endTag, startDate, endDate, title); - -fprintf(h, "<ul>\n"); - -fprintf(h, "<li> <A href=\"user/index.html\">Changes by User</A></li>\n"); -fprintf(h, "\n"); -fprintf(h, "<li> <A href=\"file/index.html\">All File Changes</A></li>\n"); - -fprintf(h, "</ul>\n</body>\n</html>\n"); -fclose(h); - -} - -void makeMyDir(char *path) -/* Make a single dir if it does not already exit */ -{ -if (!fileExists(path) && mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) - errnoAbort("unable to mkdir %s", path); -} - -void gitReports() -/* Generate code-review reports from git repo */ -{ -int totalChangedLines = 0; -int totalChangedFiles = 0; - -int userChangedLines = 0; -int userChangedFiles = 0; - -tempMakeDiffName = cloneString(rTempName("/tmp", "makeDiff", ".tmp")); - -/* read the commits */ -struct commit *commits = getCommits(), *c = NULL; -/* make the user list */ -for(c = commits; c; c = c->next) - { - - if (!hashLookup(userHash, c->author)) - { - hashStore(userHash, c->author); - struct slName *name = newSlName(c->author); - slAddHead(&users, name); - } - } -slNameSort(&users); - -/* create prefix dir */ -char path[256]; -safef(path, sizeof(path), "%s/%s", outDir, outPrefix); -makeMyDir(path); - -/* create file dir */ -safef(path, sizeof(path), "%s/%s/%s", outDir, outPrefix, "file"); -makeMyDir(path); - -/* create user dir */ -safef(path, sizeof(path), "%s/%s/%s", outDir, outPrefix, "user"); -makeMyDir(path); - - -char usersPath[1024]; -safef(usersPath, sizeof(usersPath), "%s/%s/%s/index.html", outDir, outPrefix, "user"); - -FILE *h = mustOpen(usersPath, "w"); -fprintf(h, "<html>\n<head>\n<title>Changes By User</title>\n</head>\n</body>\n"); -fprintf(h, "<h1>Changes By User</h1>\n"); - -fprintf(h, "<h2>%s to %s (%s to %s) %s</h2>\n", startTag, endTag, startDate, endDate, title); - -fprintf(h, "<ul>\n"); - - - -struct slName*u; -for(u = users; u; u = u->next) - { - printf("user: %s\n", u->name); - - /* create user/name dir */ - safef(path, sizeof(path), "%s/%s/%s/%s", outDir, outPrefix, "user", u->name); - makeMyDir(path); - - /* create user/name/context dir */ - safef(path, sizeof(path), "%s/%s/%s/%s/%s", outDir, outPrefix, "user", u->name, "context"); - makeMyDir(path); - - /* create user/name/full dir */ - safef(path, sizeof(path), "%s/%s/%s/%s/%s", outDir, outPrefix, "user", u->name, "full"); - makeMyDir(path); - - userChangedLines = 0; - userChangedFiles = 0; - - /* make user's reports */ - doUserCommits(u->name, commits, &userChangedLines, &userChangedFiles); - - doUserFiles(u->name, commits); - - char relPath[1024]; - safef(relPath, sizeof(relPath), "%s/index.html", u->name); - fprintf(h, "<li> <A href=\"%s\">%s</A> - changed lines: %d, files: %d</li>\n", relPath, u->name, userChangedLines, userChangedFiles); - - totalChangedLines += userChangedLines; - totalChangedFiles += userChangedFiles; - - } - -fprintf(h, "</ul>\n"); -if (u) - { - fprintf(h, "switch to <A href=\"index.html\">commits view</A>, <A href=\"../index.html\">user index</A>"); - } -else - { - fprintf(h, "<ul>\n"); - fprintf(h, "<li> lines changed: %d</li>\n", totalChangedLines); - fprintf(h, "<li> files changed: %d</li>\n", totalChangedFiles); - fprintf(h, "</ul>\n"); - } -fprintf(h, "</body>\n</html>\n"); - -fclose(h); - -// make index of all files view -doUserFiles(NULL, commits); - -// make main index page -doMainIndex(); - -// tidying up -unlink(tempMakeDiffName); -freez(&tempMakeDiffName); -} - -int main(int argc, char *argv[]) -{ -optionInit(&argc, argv, options); -if (argc != 9) - usage("wrong number of args"); -if (optionExists("-help")) - usage("help"); - -startTag = argv[1]; -endTag = argv[2]; -startDate = argv[3]; -endDate = argv[4]; -title = argv[5]; -repoDir = argv[6]; -outDir = argv[7]; -outPrefix = argv[8]; - -userHash = hashNew(5); - -chdir(repoDir); - -gitReports(); - -hashFree(&userHash); -printf("Done.\n"); -return 0; -} -