{"thread":{"id":"7032","subject":"[PATCH/RFC] statplot: a tool for mining repository statistics.","startedAt":"2007-03-01T07:29:53Z","lastAt":"2007-03-02T19:10:06Z","messageCount":6,"participants":["Shawn O. Pearce","Alex Riesen","Jakub Narebski","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"35922","messageId":"20070301072953.GA8730@spearce.org","threadId":"7032","inReplyTo":null,"subject":"[PATCH/RFC] statplot: a tool for mining repository statistics.","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-03-01T07:29:53Z","receivedAt":"2007-03-01T07:29:53Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This is a simple program to dump statistics about number of objects\nper day, and how much disk space is used per day.  It can be used\nto generate data files suitable for plotting repository growth\nover time.\n\nThis program avoids displaying information about tags, but it does\ncover the commits referenced by those tags.  Since annotated tags\nare a very small portion of most projects I don't consider this to\nbe a major issue at this time.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n\n This is on top of Nico's cleanups that are currently in next,\n as I'm using the newer read_sha1_file interface.  Otherwise it\n has no dependencies on stuff in next.\n\n I am normally very anti-self-linking, but I have posted graphs from\n git.git and linux-2.6.git on my website if people are interested,\n so I'm self-linking:\n\n   http://www.spearce.org/2007/03/git-and-linux-repository-growth.html\n\n I figured a link in email would be better than an attachment.  ;-)\n\n This is a toy, but its more than good enough for production, so uh,\n here it is.  Should it be included in git.git?  Don't know....\n\n .gitignore                     |    1 +\n Documentation/git-statplot.txt |   63 ++++++++\n Makefile                       |    1 +\n builtin-statplot.c             |  318 ++++++++++++++++++++++++++++++++++++++++\n builtin.h                      |    1 +\n git.c                          |    1 +\n 6 files changed, 385 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-statplot.txt\n create mode 100644 builtin-statplot.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 0eaba0a..4c73efb 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -119,6 +119,7 @@ git-ssh-fetch\n git-ssh-pull\n git-ssh-push\n git-ssh-upload\n+git-statplot\n git-status\n git-stripspace\n git-svn\ndiff --git a/Documentation/git-statplot.txt b/Documentation/git-statplot.txt\nnew file mode 100644\nindex 0000000..77dd0d4\n--- /dev/null\n+++ b/Documentation/git-statplot.txt\n@@ -0,0 +1,63 @@\n+git-statplot(1)\n+=========\n+\n+NAME\n+----\n+git-statplot - Generate repository statistics for plotting\n+\n+\n+SYNOPSIS\n+--------\n+'git-statplot' [<commit-ish>*] >repo.dat\n+\n+DESCRIPTION\n+-----------\n+Scans all current packs and all reachable objects (optionally\n+starting from <object>* rather than all refs) and determines the\n+earliest date that each object was referenced by a commit.\n+\n+For each date, the number of new commits, number of new objects,\n+and total bytes used within the packfile are reported to standard\n+output.  The output format is suitable for plotting with Gnuplot,\n+or any number of other popular plotting and analysis tools.\n+\n+A sample output follows, taken from the early days of git's own\n+history:\n+\n+....\n+\t# Total days:    2\n+\t# Total commits: 5\n+\t# Date range: [\"2005-04-06\":\"2005-04-07\"]\n+\t# <date> <new_commits> <new_trees> <new_blobs> <new_objects> <new_bytes>\n+\t2005-04-06 2 2 19 23 8219\n+\t2005-04-07 3 3 11 17 4742\n+....\n+\n+Here we see that during Git's first day developers created 23 objects\n+(2 commits, 2 trees, 19 blobs), and these objects currently occupy\n+8,219 bytes of disk space.\n+\n+During the second day an additional 17 objects and these objects\n+currently occupy 4,742 bytes of disk space.\n+\n+Notes\n+-----\n+This program only examines packed objects.  If unpacked objects\n+are reachable from the input arguments these will not be counted in\n+the output.  Running only on a fully packed repository is suggested.\n+\n+Commit counts, object counts and bytes can vary between runs\n+for several reasons.  One cause of this change can be merging of\n+long-lived branches. The merged commits will count on the day they\n+were committed, not on the day they arrived in this repository.\n+Another cause can be from repacking with the -f option, as deltas\n+may be recomputed and recompressed, possibly altering the amount\n+of space used by an object.\n+\n+Author\n+------\n+Written by Shawn O. Pearce <spearce@spearce.org>\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex e4bb865..83da7fd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -317,6 +317,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-runstatus.o \\\n \tbuiltin-shortlog.o \\\n \tbuiltin-show-branch.o \\\n+\tbuiltin-statplot.o \\\n \tbuiltin-stripspace.o \\\n \tbuiltin-symbolic-ref.o \\\n \tbuiltin-tar-tree.o \\\ndiff --git a/builtin-statplot.c b/builtin-statplot.c\nnew file mode 100644\nindex 0000000..7f5323f\n--- /dev/null\n+++ b/builtin-statplot.c\n@@ -0,0 +1,318 @@\n+/*\n+ * Computes the earliest commit that can reach each object, then\n+ * summarizes this for each day found within the commit history.\n+ *\n+ * Produces a datafile to stdout suitable for Gnuplot.\n+ */\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"refs.h\"\n+#include \"pack.h\"\n+#include \"tree-walk.h\"\n+\n+#define SEEN      (1u<<0)\n+\n+struct packed_object {\n+\tunsigned char sha1[20];\n+\tunsigned int packed_bytes;\n+\tunsigned int type : 2;\n+\tunsigned int date : 30;\n+};\n+\n+struct days_total {\n+\tunsigned long packed_bytes;\n+\tunsigned long new_objects[4];\n+};\n+\n+static const unsigned long one_day = 24 * 60 * 60;\n+static struct commit **all_commits;\n+static struct packed_object *all_objs;\n+static unsigned long nr_commits;\n+static unsigned long max_commits;\n+static unsigned long nr_objects;\n+static unsigned long nr_unpacked;\n+static unsigned int earliest_date;\n+static unsigned int latest_date;\n+\n+static int sort_by_sha1(const void *a_, const void *b_)\n+{\n+\tconst struct packed_object *a = a_;\n+\tconst struct packed_object *b = b_;\n+\treturn hashcmp(a->sha1, b->sha1);\n+}\n+\n+static int sort_by_offset(const void *a_, const void *b_)\n+{\n+\tconst struct packed_object *a = a_;\n+\tconst struct packed_object *b = b_;\n+\tif (a->packed_bytes < b->packed_bytes)\n+\t\treturn -1;\n+\telse if (a->packed_bytes > b->packed_bytes)\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static void index_one_pack(struct packed_git *p)\n+{\n+\tint i, num = num_packed_objects(p);\n+\tunsigned long s = nr_objects;\n+\tstruct packed_object *o, *last;\n+\n+\tif (num < 1)\n+\t\treturn;\n+\n+\tnr_objects += num;\n+\tall_objs = xrealloc(all_objs, nr_objects * sizeof(*all_objs));\n+\tfor (i = 0, o = all_objs + s; i < num; i++, o++) {\n+\t\tnth_packed_object_sha1(p, i, o->sha1);\n+\t\to->packed_bytes = find_pack_entry_one(o->sha1, p);\n+\t\to->type = 0;\n+\t\to->date = 0;\n+\t}\n+\n+\tqsort(all_objs + s, num, sizeof(*all_objs), sort_by_offset);\n+\n+\tlast = all_objs + s;\n+\tfor (i = 1, o = last + 1; i < num; i++, o++) {\n+\t\tlast->packed_bytes = o->packed_bytes - last->packed_bytes;\n+\t\tlast = o;\n+\t}\n+\tlast->packed_bytes = p->pack_size - last->packed_bytes - 32;\n+}\n+\n+static void scan_commit(struct commit *commit)\n+{\n+\tif (!commit || commit->object.flags & SEEN)\n+\t\treturn;\n+\n+\tparse_commit(commit);\n+\tcommit->object.flags |= SEEN;\n+\n+\tif (nr_commits == max_commits) {\n+\t\tmax_commits *= 2;\n+\t\tif (max_commits < 1024)\n+\t\t\tmax_commits = 1024;\n+\t\tall_commits = xrealloc(all_commits,\n+\t\t\tsizeof(*all_commits) * max_commits);\n+\t}\n+\tall_commits[nr_commits++] = commit;\n+}\n+\n+static int stat_handle_reflog_ent(unsigned char *osha1,\n+\tunsigned char *nsha1,\n+\tconst char *email,\n+\tunsigned long timestamp,\n+\tint tz,\n+\tconst char *message,\n+\tvoid *cb_data)\n+{\n+\tscan_commit(lookup_commit_reference_gently(nsha1, 1));\n+\treturn 0;\n+}\n+\n+static int stat_handle_reflog(const char *logname,\n+\tconst unsigned char *sha1,\n+\tint flag,\n+\tvoid *cb_data)\n+{\n+\tfor_each_reflog_ent(logname, stat_handle_reflog_ent, NULL);\n+\treturn 0;\n+}\n+\n+static int stat_handle_ref(const char *refname,\n+\tconst unsigned char *sha1,\n+\tint flag,\n+\tvoid *cb_data)\n+{\n+\tscan_commit(lookup_commit_reference_gently(sha1, 1));\n+\treturn 0;\n+}\n+\n+static int sort_by_cdate(const void *a_, const void *b_)\n+{\n+\tconst struct commit *a = *((const struct commit**)a_);\n+\tconst struct commit *b = *((const struct commit**)b_);\n+\tif (a->date < b->date)\n+\t\treturn -1;\n+\telse if (a->date > b->date)\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static void list_all_commits()\n+{\n+\tunsigned long i;\n+\n+\tfor (i = 0; i < nr_commits; i++) {\n+\t\tstruct commit_list *parents = all_commits[i]->parents;\n+\t\twhile (parents) {\n+\t\t\tscan_commit(parents->item);\n+\t\t\tparents = parents->next;\n+\t\t}\n+\t}\n+\n+\tqsort(all_commits, nr_commits, sizeof(*all_commits), sort_by_cdate);\n+}\n+\n+static struct packed_object* find_one(const unsigned char *sha1)\n+{\n+\tunsigned long hi = nr_objects;\n+\tunsigned long lo = 0;\n+\tdo {\n+\t\tunsigned long mi = (lo + hi) / 2;\n+\t\tint cmp = hashcmp(all_objs[mi].sha1, sha1);\n+\t\tif (!cmp)\n+\t\t\treturn all_objs + mi;\n+\t\tif (cmp > 0)\n+\t\t\thi = mi;\n+\t\telse\n+\t\t\tlo = mi + 1;\n+\t} while (lo < hi);\n+\treturn NULL;\n+}\n+\n+static int mark_one(\n+\tconst unsigned char *sha1,\n+\tunsigned int date,\n+\tenum object_type type)\n+{\n+\tstruct packed_object *obj = find_one(sha1);\n+\tif (!obj) {\n+\t\tnr_unpacked++;\n+\t\treturn 1;\n+\t}\n+\tif (obj->type)\n+\t\treturn 1;\n+\tobj->date = date;\n+\tobj->type = type;\n+\treturn 0;\n+}\n+\n+static void mark_tree(const unsigned char *sha1, unsigned int date)\n+{\n+\tstruct tree_desc desc;\n+\tstruct name_entry name_entry;\n+\tenum object_type type;\n+\tvoid *buf;\n+\n+\tif (mark_one(sha1, date, OBJ_TREE))\n+\t\treturn;\n+\n+\tbuf = read_sha1_file(sha1, &type, &desc.size);\n+\tif (!buf || type != OBJ_TREE)\n+\t\tdie(\"missing/corrupt tree object %s\", sha1_to_hex(sha1));\n+\tdesc.buf = buf;\n+\twhile (tree_entry(&desc, &name_entry)) {\n+\t\tif (S_ISDIR(name_entry.mode))\n+\t\t\tmark_tree(name_entry.sha1, date);\n+\t\telse\n+\t\t\tmark_one(name_entry.sha1, date, OBJ_BLOB);\n+\t}\n+\tfree(buf);\n+}\n+\n+static void compute_earliest_dates()\n+{\n+\tunsigned long i;\n+\n+\tif (!nr_commits)\n+\t\tdie(\"No commits?\");\n+\n+\tfor (i = 0; i < nr_commits; i++) {\n+\t\tstruct commit *commit = all_commits[i];\n+\t\tunsigned int day = commit->date / one_day;\n+\t\tmark_one(commit->object.sha1, day, OBJ_COMMIT);\n+\t\tmark_tree(commit->tree->object.sha1, day);\n+\t}\n+\n+\tearliest_date = all_commits[0]->date / one_day;\n+\tlatest_date = all_commits[nr_commits - 1]->date / one_day;\n+}\n+\n+static void print_yyyymmdd(unsigned int day)\n+{\n+\ttime_t date = day * one_day;\n+\tstruct tm *when = localtime(&date);\n+\tprintf(\"%4.4i-%2.2i-%2.2i\",\n+\t\twhen->tm_year + 1900,\n+\t\twhen->tm_mon + 1,\n+\t\twhen->tm_mday);\n+}\n+\n+static void compute_date_buckets()\n+{\n+\tunsigned long days = latest_date - earliest_date + 1;\n+\tunsigned long i;\n+\tstruct days_total *totals;\n+\tstruct packed_object *o;\n+\n+\ttotals = xcalloc(days, sizeof(*totals));\n+\tfor (i = 0, o = all_objs; i < nr_objects; i++, o++) {\n+\t\tif (o->date) {\n+\t\t\tstruct days_total* t = &totals[o->date - earliest_date];\n+\t\t\tt->packed_bytes += o->packed_bytes;\n+\t\t\tt->new_objects[OBJ_NONE]++;\n+\t\t\tt->new_objects[o->type]++;\n+\t\t}\n+\t}\n+\n+\tprintf(\"# Total days:    %lu\\n\", days);\n+\tprintf(\"# Total commits: %lu\\n\", nr_commits);\n+\tprintf(\"# Date range: [\\\"\");\n+\tprint_yyyymmdd(earliest_date);\n+\tprintf(\"\\\":\\\"\");\n+\tprint_yyyymmdd(latest_date);\n+\tprintf(\"\\\"]\\n\");\n+\n+\tprintf(\"# <date> <new_commits> <new_trees> <new_blobs>\"\n+\t\t\" <new_objects> <new_bytes>\\n\");\n+\tfor (i = 0; i < days; i++) {\n+\t\tprint_yyyymmdd(earliest_date + i);\n+\t\tprintf(\" %lu %lu %lu %lu %lu\\n\",\n+\t\t\ttotals[i].new_objects[OBJ_COMMIT],\n+\t\t\ttotals[i].new_objects[OBJ_TREE],\n+\t\t\ttotals[i].new_objects[OBJ_BLOB],\n+\t\t\ttotals[i].new_objects[OBJ_NONE],\n+\t\t\ttotals[i].packed_bytes);\n+\t}\n+\n+\tfree(totals);\n+}\n+\n+int cmd_statplot(int argc, char **argv, const char *prefix)\n+{\n+\tint i;\n+\tstruct packed_git *p;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tunsigned char head_sha1[20];\n+\n+\t\tif (*arg == '-')\n+\t\t\tcontinue;\n+\t\telse if (!get_sha1(arg, head_sha1))\n+\t\t\tscan_commit(lookup_commit_reference(head_sha1));\n+\t\telse\n+\t\t\terror(\"invalid parameter: expected sha1, got '%s'\", arg);\n+\t}\n+\n+\tif (!nr_commits) {\n+\t\tfor_each_ref(stat_handle_ref, NULL);\n+\t\tfor_each_reflog(stat_handle_reflog, NULL);\n+\t}\n+\n+\tprepare_packed_git();\n+\tfor (p = packed_git; p; p = p->next)\n+\t\tindex_one_pack(p);\n+\tqsort(all_objs, nr_objects, sizeof(*all_objs), sort_by_sha1);\n+\n+\tlist_all_commits();\n+\tcompute_earliest_dates();\n+\tcompute_date_buckets();\n+\n+\tif (nr_unpacked)\n+\t\twarn(\"%lu unpacked objects were not included in output.\\n\",\n+\t\t\tnr_unpacked);\n+\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 48d68c8..8a6ee95 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -66,6 +66,7 @@ extern int cmd_runstatus(int argc, const char **argv, const char *prefix);\n extern int cmd_shortlog(int argc, const char **argv, const char *prefix);\n extern int cmd_show(int argc, const char **argv, const char *prefix);\n extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n+extern int cmd_statplot(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/git.c b/git.c\nindex 008e016..994f22e 100644\n--- a/git.c\n+++ b/git.c\n@@ -278,6 +278,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"shortlog\", cmd_shortlog, RUN_SETUP | USE_PAGER },\n \t\t{ \"show-branch\", cmd_show_branch, RUN_SETUP },\n \t\t{ \"show\", cmd_show, RUN_SETUP | USE_PAGER },\n+\t\t{ \"statplot\", cmd_statplot, RUN_SETUP },\n \t\t{ \"stripspace\", cmd_stripspace },\n \t\t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t\t{ \"tar-tree\", cmd_tar_tree },\n-- \n1.5.0.2.809.g0f936\n"},{"id":"35951","messageId":"81b0412b0703010158i1a3963f4wabda2f72c1fd0bc4@mail.gmail.com","threadId":"7032","inReplyTo":"20070301072953.GA8730@spearce.org","subject":"Re: [PATCH/RFC] statplot: a tool for mining repository statistics.","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-03-01T09:58:52Z","receivedAt":"2007-03-01T09:58:52Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 3/1/07, Shawn O. Pearce <spearce@spearce.org> wrote:\n> This is a simple program to dump statistics about number of objects\n> per day, and how much disk space is used per day.  It can be used\n> to generate data files suitable for plotting repository growth\n> over time.\n>\n> This program avoids displaying information about tags, but it does\n> cover the commits referenced by those tags.  Since annotated tags\n> are a very small portion of most projects I don't consider this to\n> be a major issue at this time.\n>\n\nTried it on my repo but the program produced some records with\nall zeros. I.e.:\n2005-11-10 6 222 235 463 1785888\n2005-11-11 3 34 2 39 9729\n2005-11-12 0 0 0 0 0\n2005-11-13 0 0 0 0 0\n2005-11-14 6 227 330 563 716230\n2005-11-15 3 121 209 333 1162839\n\nWhat could this possibly mean?\n\n>  This is a toy, but its more than good enough for production, so uh,\n>  here it is.  Should it be included in git.git?  Don't know....\n>\n\nDefinitely! It's interesting, and fun.\n"},{"id":"35992","messageId":"20070301183004.GB8858@spearce.org","threadId":"7032","inReplyTo":"81b0412b0703010158i1a3963f4wabda2f72c1fd0bc4@mail.gmail.com","subject":"Re: [PATCH/RFC] statplot: a tool for mining repository statistics.","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-03-01T18:30:04Z","receivedAt":"2007-03-01T18:30:04Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Alex Riesen <raa.lkml@gmail.com> wrote:\n> Tried it on my repo but the program produced some records with\n> all zeros. I.e.:\n> 2005-11-10 6 222 235 463 1785888\n> 2005-11-11 3 34 2 39 9729\n> 2005-11-12 0 0 0 0 0\n> 2005-11-13 0 0 0 0 0\n> 2005-11-14 6 227 330 563 716230\n> 2005-11-15 3 121 209 333 1162839\n> \n> What could this possibly mean?\n\nThere were no new objects attributed to 12 and 13 of Nov, 2005.\nThe program allocates a zero'd array for every day in the entire\nrange, then increments each day with the number of objects introduced\nby commit(s) on that day.  It then just prints the entire array,\nrather than only printing the non-zero lines.  ;-)\n\nHorribly simple code.  Maybe too dumb for inclusion in git.git.\n\n-- \nShawn.\n"},{"id":"36007","messageId":"81b0412b0703011336p324b3202k1ccabb7899238acd@mail.gmail.com","threadId":"7032","inReplyTo":"20070301183004.GB8858@spearce.org","subject":"Re: [PATCH/RFC] statplot: a tool for mining repository statistics.","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-03-01T21:36:23Z","receivedAt":"2007-03-01T21:36:23Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 3/1/07, Shawn O. Pearce <spearce@spearce.org> wrote:\n> > Tried it on my repo but the program produced some records with\n> > all zeros. I.e.:\n> > 2005-11-10 6 222 235 463 1785888\n> > 2005-11-11 3 34 2 39 9729\n> > 2005-11-12 0 0 0 0 0\n> > 2005-11-13 0 0 0 0 0\n> > 2005-11-14 6 227 330 563 716230\n> > 2005-11-15 3 121 209 333 1162839\n> >\n> > What could this possibly mean?\n>\n> There were no new objects attributed to 12 and 13 of Nov, 2005.\n> The program allocates a zero'd array for every day in the entire\n> range, then increments each day with the number of objects introduced\n> by commit(s) on that day.  It then just prints the entire array,\n> rather than only printing the non-zero lines.  ;-)\n\noh, ok.\n\n> Horribly simple code.  Maybe too dumb for inclusion in git.git.\n\nI still think it's good for something (I used it to quickly find days\nfor weekly and monthly report).\n"},{"id":"36113","messageId":"es9qf4$cj0$1@sea.gmane.org","threadId":"7032","inReplyTo":"20070301183004.GB8858@spearce.org","subject":"Re: [PATCH/RFC] statplot: a tool for mining repository statistics.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-03-02T18:32:17Z","receivedAt":"2007-03-02T18:32:17Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Shawn O. Pearce wrote:\n\n> Horribly simple code.  Maybe too dumb for inclusion in git.git.\n\nPerhaps it would be better to include it (first) in contrib/ section, then?\nIt looks more like contrib, by the way, than something than should be in\ngit.git core...\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"36115","messageId":"Pine.LNX.4.63.0703022008550.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"7032","inReplyTo":"es9qf4$cj0$1@sea.gmane.org","subject":"Re: [PATCH/RFC] statplot: a tool for mining repository statistics.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-03-02T19:10:06Z","receivedAt":"2007-03-02T19:10:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 2 Mar 2007, Jakub Narebski wrote:\n\n> Shawn O. Pearce wrote:\n> \n> > Horribly simple code.  Maybe too dumb for inclusion in git.git.\n> \n> Perhaps it would be better to include it (first) in contrib/ section, \n> then? It looks more like contrib, by the way, than something than should \n> be in git.git core...\n\nIt depends not only on libgit.a, but also on being a builtin. I actually \n_like_ writing builtins better than non-builtins, since the infrastructure \nis so simple.\n\nIf now only a unified option parser would appear out of the void, that'd \nbe perfect.\n\nCiao,\nDscho\n"}]}