{"thread":{"id":"12923","subject":"[PATCH] Add new git-graph command","startedAt":"2008-03-30T19:58:41Z","lastAt":"2008-04-01T05:07:52Z","messageCount":14,"participants":["Adam Simpkins","Jakub Narebski","Matthieu Moy","Johannes Schindelin","Junio C Hamano","Teemu Likonen","Stephen Sinclair","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"73363","messageId":"20080330195840.GA8695@adamsimpkins.net","threadId":"12923","inReplyTo":null,"subject":"[PATCH] Add new git-graph command","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-03-30T19:58:41Z","receivedAt":"2008-03-30T19:58:41Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"This is a first pass at a command to print a text-based graph of the commit\nhistory.  It is similar to the history graph shown by gitk, but doesn't\nrequire a windowing system.\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n\nI added this since I really like gitk, but don't always have easy\naccess to an X display on some of the systems I use.  I tried using\ntig, but I found its graph output very hard to read.  The graph\nproduced by git-graph is less compact, but much more readable.\n\nUltimately, it would probably be better to integrate this\nfunctionality into git-log, instead of having it as a standalone\ncommand.  For example, a new --graph option could be added to cause\nthe graph to be displayed alongside the existing git log output.\nHowever, this would require tighter integration between the graphing\ncode and the log_tree.c and pretty.c code, which I'm not all that\nfamiliar with.\n\n\n .gitignore                         |    1 +\n Documentation/git-graph.txt        |   56 ++++\n Documentation/pretty-formats.txt   |    4 +\n Documentation/rev-list-options.txt |   15 +\n Makefile                           |    1 +\n builtin-graph.c                    |  553 ++++++++++++++++++++++++++++++++++++\n builtin.h                          |    1 +\n git.c                              |    1 +\n 8 files changed, 632 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-graph.txt\n create mode 100644 builtin-graph.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 4ff2fec..b9b4c76 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -49,6 +49,7 @@ git-fsck\n git-fsck-objects\n git-gc\n git-get-tar-commit-id\n+git-graph\n git-grep\n git-hash-object\n git-http-fetch\ndiff --git a/Documentation/git-graph.txt b/Documentation/git-graph.txt\nnew file mode 100644\nindex 0000000..022cb09\n--- /dev/null\n+++ b/Documentation/git-graph.txt\n@@ -0,0 +1,56 @@\n+git-graph(1)\n+============\n+\n+NAME\n+----\n+git-graph - Show a graph of commit history\n+\n+\n+SYNOPSIS\n+--------\n+'git-graph' <option>...\n+\n+DESCRIPTION\n+-----------\n+Shows a text-based representation of the commit history graph.\n+\n+The command takes options applicable to the linkgit:git-rev-list[1]\n+command to control what is shown and how.\n+\n+\n+OPTIONS\n+-------\n+\n+-<n>::\n+\tLimits the number of commits to show.\n+\n+<since>..<until>::\n+\tShow only commits between the named two commits.  When\n+\teither <since> or <until> is omitted, it defaults to\n+\t`HEAD`, i.e. the tip of the current branch.\n+\tFor a more complete list of ways to spell <since>\n+\tand <until>, see \"SPECIFYING REVISIONS\" section in\n+\tlinkgit:git-rev-parse[1].\n+\n+:git-graph: 1\n+include::rev-list-options.txt[]\n+\n+include::pretty-formats.txt[]\n+\n+\n+See Also\n+--------\n+linkgit:gitk[1]\n+\n+Author\n+------\n+Written by Adam Simpkins <adam@adamsimpkins.net>,\n+and the git list <git@vger.kernel.org>\n+\n+Documentation\n+--------------\n+Documentation by Adam Simpkins and the git list.\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 0193c3c..da76c97 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -1,6 +1,7 @@\n PRETTY FORMATS\n --------------\n \n+ifndef::git-graph[]\n If the commit is a merge, and if the pretty-format\n is not 'oneline', 'email' or 'raw', an additional line is\n inserted before the 'Author:' line.  This line begins with\n@@ -10,6 +11,7 @@ necessarily be the list of the *direct* parent commits if you\n have limited your view of history: for example, if you are\n only interested in changes related to a certain directory or\n file.\n+endif::git-graph[]\n \n Here are some additional details for each format:\n \n@@ -19,6 +21,7 @@ Here are some additional details for each format:\n +\n This is designed to be as compact as possible.\n \n+ifndef::git-graph[]\n * 'short'\n \n \t  commit <sha1>\n@@ -75,6 +78,7 @@ displayed in full, regardless of whether --abbrev or\n --no-abbrev are used, and 'parents' information show the\n true parent commits, without taking grafts nor history\n simplification into account.\n+endif::git-graph[]\n \n * 'format:'\n +\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 2648a55..b461478 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -9,6 +9,7 @@ endif::git-rev-list[]\n \n include::pretty-options.txt[]\n \n+ifndef::git-graph[]\n --relative-date::\n \n \tSynonym for `--date=relative`.\n@@ -37,11 +38,13 @@ format, often found in E-mail messages.\n \n \tPrint the contents of the commit in raw-format; each record is\n \tseparated with a NUL character.\n+endif::git-graph[]\n \n --parents::\n \n \tPrint the parents of the commit.\n \n+ifndef::git-graph[]\n --timestamp::\n \tPrint the raw commit timestamp.\n \n@@ -104,6 +107,7 @@ options may be given. See linkgit:git-diff-files[1] for more options.\n -t::\n \n \tShow the tree objects in the diff output. This implies '-r'.\n+endif::git-graph[]\n \n Commit Limiting\n ~~~~~~~~~~~~~~~\n@@ -136,6 +140,7 @@ ifdef::git-rev-list[]\n \tLimit the commits output to specified time range.\n endif::git-rev-list[]\n \n+ifndef::git-graph[]\n --author='pattern', --committer='pattern'::\n \n \tLimit the commits output to ones with author/committer\n@@ -184,6 +189,7 @@ endif::git-rev-list[]\n \tadjusting to updated upstream from time to time, and\n \tthis option allows you to ignore the individual commits\n \tbrought in to your history by such a merge.\n+endif::git-graph[]\n \n --not::\n \n@@ -195,6 +201,7 @@ endif::git-rev-list[]\n \tPretend as if all the refs in `$GIT_DIR/refs/` are listed on the\n \tcommand line as '<commit>'.\n \n+ifndef::git-graph[]\n --stdin::\n \n \tIn addition to the '<commit>' listed on the command\n@@ -260,6 +267,7 @@ merges that do not touch the given paths.\n Use the '--sparse' flag to makes the command output all eligible commits\n (still subject to count and age limitation), but apply merge\n simplification nevertheless.\n+endif::git-graph[]\n \n ifdef::git-rev-list[]\n --bisect::\n@@ -316,7 +324,12 @@ endif::git-rev-list[]\n Commit Ordering\n ~~~~~~~~~~~~~~~\n \n+ifndef::git-graph[]\n By default, the commits are shown in reverse chronological order.\n+endif::git-graph[]\n+ifdef::git-graph[]\n+By default, the commits are shown in topological order.\n+endif::git-graph[]\n \n --topo-order::\n \n@@ -329,6 +342,7 @@ By default, the commits are shown in reverse chronological order.\n \tparent comes before all of its children, but otherwise things\n \tare still ordered in the commit timestamp order.\n \n+ifndef::git-graph[]\n --reverse::\n \n \tOutput the commits in reverse order.\n@@ -366,3 +380,4 @@ These options are mostly targeted for packing of git repositories.\n --do-walk::\n \n \tOverrides a previous --no-walk.\n+endif::git-graph[]\ndiff --git a/Makefile b/Makefile\nindex 7c70b00..da4ce9d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -495,6 +495,7 @@ BUILTIN_OBJS += builtin-fmt-merge-msg.o\n BUILTIN_OBJS += builtin-for-each-ref.o\n BUILTIN_OBJS += builtin-fsck.o\n BUILTIN_OBJS += builtin-gc.o\n+BUILTIN_OBJS += builtin-graph.o\n BUILTIN_OBJS += builtin-grep.o\n BUILTIN_OBJS += builtin-init-db.o\n BUILTIN_OBJS += builtin-log.o\ndiff --git a/builtin-graph.c b/builtin-graph.c\nnew file mode 100644\nindex 0000000..1b8be2c\n--- /dev/null\n+++ b/builtin-graph.c\n@@ -0,0 +1,553 @@\n+/*\n+ * Builtin \"git graph\"\n+ */\n+#include \"cache.h\"\n+#include \"color.h\"\n+#include \"commit.h\"\n+#include \"diff.h\"\n+#include \"log-tree.h\"\n+#include \"revision.h\"\n+\n+/*\n+ * TODO:\n+ * - Add colors to the graph.\n+ *   Pick a color for each column, and print all characters\n+ *   in that column with the specified color.\n+ *\n+ * - Limit the number of columns, similar to the way gitk does.\n+ *   If we reach more than a specified number of columns, omit\n+ *   sections of some columns.\n+ *\n+ * - Get rid of some unnecessary memory allocations.\n+ *   graph_commit() allocates new_columns, mapping, and new_mapping each\n+ *   time it is called.  These could be stored in the struct graph, and\n+ *   re-used each time.  They only need to be re-allocated if the number\n+ *   of columns grows too big.  (If we place a limit on the max number of\n+ *   columns to be displayed, we may never need to reallocate them.)\n+ *\n+ * - It would be nice to support other --pretty formats.\n+ *   Currently things don't work well if print_commit_info() prints\n+ *   multiple lines.  However, we could make it always prefix the output\n+ *   with the right characters.  It wouldn't be too hard to prefix it with\n+ *   the correct branch lines, using \"|\" characters (we could hijack\n+ *   opts->diffopt.line_termination).  It would be nicer if we could get it\n+ *   to work so that the extra info was printed along side the\n+ *   \"branch collapsing\" phase.\n+ *\n+ * - Perhaps this entire command could just be merged into git-log.\n+ *   It would certainly be nicer if git-log just had a --graph option that\n+ *   caused the output to be prefixed by the graph.  This would require\n+ *   the graphing code be integrated with the log-tree.c and pretty.c code.\n+ */\n+\n+struct column {\n+\t/*\n+\t * The parent commit of this column.\n+\t */\n+\tstruct commit *commit;\n+\t/*\n+\t * XXX: Once we add support for colors, struct column could also\n+\t * contain the color of its branch line.\n+\t */\n+};\n+\n+struct graph {\n+\tint num_columns;\n+\tstruct column *columns;\n+};\n+\n+static void update_columns(struct commit *commit,\n+\t\t\t  struct column *columns,\n+\t\t\t  int *num_columns,\n+\t\t\t  int *mapping,\n+\t\t\t  int mapping_index)\n+{\n+\tint i;\n+\n+\t/*\n+\t * If the commit is already in the columns list, we don't need to\n+\t * add it.  Just update the mapping correctly.\n+\t */\n+\tfor (i = 0; i < *num_columns; ++i) {\n+\t\tif (columns[i].commit == commit) {\n+\t\t\tmapping[mapping_index] = i;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * This commit isn't already in columns.  Add it.\n+\t */\n+\tcolumns[*num_columns].commit = commit;\n+\tmapping[mapping_index] = *num_columns;\n+\t++*num_columns;\n+}\n+\n+static int is_mapping_correct(int const *mapping, int mapping_size)\n+{\n+\tint i;\n+\n+\t/*\n+\t * The mapping is up to date if each entry is at its target,\n+\t * or is 1 greater than its target.\n+\t * (If it is 1 greater than the target, '/' will be printed, so it\n+\t * will look correct on the next row.)\n+\t */\n+\tfor (i = 0; i < mapping_size; ++i) {\n+\t\tint target = mapping[i];\n+\t\tif (target < 0)\n+\t\t\tcontinue;\n+\t\tif (target == (i / 2))\n+\t\t\tcontinue;\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static void draw_pre_commit_rows(struct rev_info *opts,\n+\t\t\t\t struct commit *commit,\n+\t\t\t\t struct graph *graph,\n+\t\t\t\t int num_parents)\n+{\n+    int num_expansion_rows;\n+    int i, j, seen_this;\n+\n+    /*\n+     * If there are less than 3 parents, we don't need to do anything.\n+     * We can immediately draw the commit line.\n+     */\n+    if (num_parents < 3)\n+\treturn;\n+\n+    /*\n+     * This commit has more than 2 parents, so we need to expand the\n+     * branch lines around it to allow more room for it.\n+     *\n+     * We need 2 extra rows for every parent over 2\n+     */\n+    num_expansion_rows = (num_parents - 2) * 2;\n+    for (i = 0; i < num_expansion_rows; ++i) {\n+\tseen_this = 0;\n+\tfor (j = 0; j < graph->num_columns; ++j) {\n+\t    struct column *col = &graph->columns[j];\n+\t    if (col->commit == commit) {\n+\t\tseen_this = 1;\n+\t\tprintf(\"| %*s\", i, \"\");\n+\t    } else if (seen_this) {\n+\t\tprintf(\"\\\\ \");\n+\t    } else {\n+\t\tprintf(\"| \");\n+\t    }\n+\t}\n+\tprintf(\"\\n\");\n+    }\n+}\n+\n+static void print_commit_info(struct rev_info *opts, struct commit *commit)\n+{\n+\tlog_tree_commit(opts, commit);\n+\n+\t/*\n+\t * log_tree_commit doesn't quite do what we want with respect to\n+\t * newlines.  Unless CMIT_FMT_ONELINE is being used, it wants to\n+\t * print the newline just before each log message, instead of after\n+\t * it.  Manually print the line terminator, and stop\n+\t * log_tree_commit() from printing one the next time it is called.\n+\t */\n+\tif (opts->commit_format != CMIT_FMT_ONELINE)\n+\t{\n+\t\tputchar(opts->diffopt.line_termination);\n+\t\topts->shown_one = 0;\n+\t}\n+}\n+\n+static void draw_commit_row(struct rev_info *opts,\n+\t\t\t    struct commit *commit,\n+\t\t\t    struct graph *graph,\n+\t\t\t    int num_parents)\n+{\n+    int seen_this = 0;\n+    int i, j;\n+\n+    /*\n+     * Print the row containing this commit\n+     */\n+    seen_this = 0;\n+    for (i = 0; i <= graph->num_columns; ++i) {\n+\tstruct commit *col_commit;\n+\tif (i == graph->num_columns) {\n+\t    if (seen_this)\n+\t\tbreak;\n+\t    col_commit = commit;\n+\t} else {\n+\t    col_commit = graph->columns[i].commit;\n+\t}\n+\n+\tif (col_commit == commit) {\n+\t    seen_this = 1;\n+\t    if (num_parents > 1)\n+\t\tputchar('M');\n+\t    else\n+\t\tputchar('*');\n+\n+\t    if (num_parents < 2)\n+\t\tputchar(' ');\n+\t    else if (num_parents == 2)\n+\t\tprintf(\"  \");\n+\t    else {\n+\t\tint num_dashes = ((num_parents - 2) * 2) - 1;\n+\t\tfor (j = 0; j < num_dashes; ++j)\n+\t\t    putchar('-');\n+\t\tputchar('.');\n+\t\tputchar(' ');\n+\t    }\n+\t} else if (seen_this && (num_parents > 1)) {\n+\t    printf(\"\\\\ \");\n+\t} else {\n+\t    printf(\"| \");\n+\t}\n+    }\n+\n+    /*\n+     * Print the commit description.\n+     * This will include a newline.\n+     */\n+    print_commit_info(opts, commit);\n+}\n+\n+static void draw_post_commit_row(struct rev_info *opts,\n+\t\t\t\t struct commit *commit,\n+\t\t\t\t struct graph *graph,\n+\t\t\t\t int num_parents)\n+{\n+    int seen_this = 0;\n+    int i, j;\n+\n+    /*\n+     * Unless this is a merge commit, we don't have anything to do\n+     */\n+    if (num_parents < 2)\n+\treturn;\n+\n+    /*\n+     * For merge commits, we need 1 additional row after the commit\n+     * row to straighten out the branch lines\n+     */\n+    for (i = 0; i <= graph->num_columns; ++i) {\n+\tstruct commit *col_commit;\n+\tif (i == graph->num_columns) {\n+\t    if (seen_this)\n+\t\tbreak;\n+\t    col_commit = commit;\n+\t} else {\n+\t    col_commit = graph->columns[i].commit;\n+\t}\n+\n+\tif (col_commit == commit) {\n+\t    seen_this = 1;\n+\t    putchar('|');\n+\t    for (j = 0; j < num_parents - 1; ++j)\n+\t\tprintf(\"\\\\ \");\n+\t    if (num_parents == 2)\n+\t\tputchar(' ');\n+\t} else if (seen_this && (num_parents > 2)) {\n+\t    printf(\"\\\\ \");\n+\t} else {\n+\t    printf(\"| \");\n+\t}\n+    }\n+    putchar('\\n');\n+}\n+\n+static void graph_commit(struct rev_info *opts, struct commit *commit,\n+\t\t\t  struct graph *graph)\n+{\n+\tstruct commit_list *parent;\n+\tint num_parents = 0;\n+\tint seen_this = 0;\n+\tint i;\n+\tint num_new_columns, max_new_columns;\n+\tstruct column *new_columns;\n+\tint *mapping;\n+\tint *new_mapping;\n+\tint mapping_size, mapping_idx;\n+\n+\t/*\n+\t * Count how many parents this commit has\n+\t */\n+\tfor (parent = commit->parents; parent; parent = parent->next)\n+\t\t++num_parents;\n+\n+\tdraw_pre_commit_rows(opts, commit, graph, num_parents);\n+\tdraw_commit_row(opts, commit, graph, num_parents);\n+\tdraw_post_commit_row(opts, commit, graph, num_parents);\n+\n+\t/*\n+\t * Allocate room for the updated column info.\n+\t * At most, there will be graph->num_columns + num_parents\n+\t * columns for the next commit.\n+\t *\n+\t * XXX: We could avoid this allocation.  We really only need 2\n+\t * column arrays, and we can swap between them each time\n+\t * graph_commit() is called.\n+\t */\n+\tmax_new_columns = graph->num_columns + num_parents;\n+\tnew_columns = xmalloc(sizeof(struct column) * max_new_columns);\n+\n+\tmapping_size = max_new_columns * 2;\n+\tmapping = xmalloc(sizeof(int) * mapping_size);\n+\tnew_mapping = xmalloc(sizeof(int) * mapping_size);\n+\tfor (i = 0; i < mapping_size; ++i)\n+\t\tmapping[i] = -1;\n+\n+\t/*\n+\t * Some of the parents of this commit may already be in\n+\t * graph->columns.  If so, we need to collapse them with the\n+\t * existing branch lines.\n+\t */\n+\tnum_new_columns = 0;\n+\tseen_this = 0;\n+\tmapping_idx = 0;\n+\tfor (i = 0; i <= graph->num_columns; ++i) {\n+\t\tstruct commit *col_commit;\n+\t\tif (i == graph->num_columns) {\n+\t\t\tif (seen_this)\n+\t\t\t\tbreak;\n+\t\t\tcol_commit = commit;\n+\t\t} else {\n+\t\t\tcol_commit = graph->columns[i].commit;\n+\t\t}\n+\n+\t\tif (col_commit == commit) {\n+\t\t\tseen_this = 1;\n+\t\t\tfor (parent = commit->parents;\n+\t\t\t     parent;\n+\t\t\t     parent = parent->next) {\n+\t\t\t\tupdate_columns(parent->item, new_columns,\n+\t\t\t\t\t       &num_new_columns, mapping,\n+\t\t\t\t\t       mapping_idx);\n+\t\t\t\tmapping_idx += 2;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tupdate_columns(col_commit, new_columns,\n+\t\t\t\t       &num_new_columns, mapping, mapping_idx);\n+\t\t\tmapping_idx += 2;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Shrink mapping_size to be the minimum necessary\n+\t */\n+\twhile (mapping_size > 1 && mapping[mapping_size - 1] < 0)\n+\t\t--mapping_size;\n+\n+\t/*\n+\t * mapping now indicates a mapping between incoming branch lines\n+\t * and which column they need to go to.\n+\t *\n+\t * Print lines to match the branch lines up to the correct column.\n+\t */\n+\twhile (!is_mapping_correct(mapping, mapping_size))\n+\t{\n+\t\tint *tmp_mapping;\n+\n+\t\tfor (i = 0; i < mapping_size; ++i)\n+\t\t\tnew_mapping[i] = -1;\n+\n+\t\tfor (i = 0; i < mapping_size; ++i) {\n+\t\t\tint target = mapping[i];\n+\t\t\tif (target < 0)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * Since update_columns() always inserts the\n+\t\t\t * leftmost column first, each branch's target\n+\t\t\t * location should always be either its current\n+\t\t\t * location or to the left of its current location.\n+\t\t\t *\n+\t\t\t * We never have to move branches to the right.\n+\t\t\t * This makes the graph much more legible, since\n+\t\t\t * whenever branches cross, only one is moving\n+\t\t\t * directions.\n+\t\t\t */\n+\t\t\tassert(target * 2 <= i);\n+\n+\t\t\tif (target * 2 == i) {\n+\t\t\t\t/*\n+\t\t\t\t * This column is already in the\n+\t\t\t\t * correct place\n+\t\t\t\t */\n+\t\t\t\tassert(new_mapping[i] == -1);\n+\t\t\t\tnew_mapping[i] = target;\n+\t\t\t} else if (new_mapping[i - 1] < 0) {\n+\t\t\t\t/*\n+\t\t\t\t * Nothing is to the left.\n+\t\t\t\t * Move to the left by one\n+\t\t\t\t */\n+\t\t\t\tnew_mapping[i - 1] = target;\n+\t\t\t} else if (new_mapping[i - 1] == target) {\n+\t\t\t\t/*\n+\t\t\t\t * There is a branch line to our left\n+\t\t\t\t * already, and it is our target.  We\n+\t\t\t\t * combine with this line, since we share\n+\t\t\t\t * the same parent commit.\n+\t\t\t\t *\n+\t\t\t\t * We don't have to add anything to the\n+\t\t\t\t * output or new_mapping, since the\n+\t\t\t\t * existing branch line has already taken\n+\t\t\t\t * care of it.\n+\t\t\t\t */\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * There is a branch line to our left,\n+\t\t\t\t * but it isn't our target.  We need to\n+\t\t\t\t * cross over it.\n+\t\t\t\t *\n+\t\t\t\t * The space just to the left of this\n+\t\t\t\t * branch should always be empty.\n+\t\t\t\t */\n+\t\t\t\tassert(new_mapping[i - 1] > target);\n+\t\t\t\tassert(new_mapping[i - 2] < 0);\n+\t\t\t\tnew_mapping[i - 2] = target;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/*\n+\t\t * The new mapping may be 1 smaller than the old mapping\n+\t\t */\n+\t\tif (new_mapping[mapping_size - 1] < 0)\n+\t\t\t--mapping_size;\n+\n+\t\t/*\n+\t\t * Print out a line based on the new mapping info\n+\t\t */\n+\t\tfor (i = 0; i < mapping_size; ++i) {\n+\t\t\tint target = new_mapping[i];\n+\t\t\tif (target < 0)\n+\t\t\t\tputchar(' ');\n+\t\t\telse if (target * 2 == i)\n+\t\t\t\tputchar('|');\n+\t\t\telse\n+\t\t\t\tputchar('/');\n+\t\t}\n+\t\tputchar('\\n');\n+\n+\t\t/*\n+\t\t * Swap mapping and new_mapping\n+\t\t */\n+\t\ttmp_mapping = mapping;\n+\t\tmapping = new_mapping;\n+\t\tnew_mapping = tmp_mapping;\n+\t}\n+\n+\t/*\n+\t * Update the graph with the new branch line info.\n+\t */\n+\tgraph->num_columns = num_new_columns;\n+\tfree(graph->columns);\n+\tgraph->columns = new_columns;\n+\n+\tfree(mapping);\n+\tfree(new_mapping);\n+}\n+\n+static int cmd_graph_walk(struct rev_info *opts)\n+{\n+\tstruct commit *commit;\n+\n+\tstruct graph graph;\n+\tgraph.num_columns = 0;\n+\tgraph.columns = NULL;\n+\n+\t/*\n+\t * XXX: Should we handle opts->early_output?\n+\t */\n+\n+\tif (prepare_revision_walk(opts))\n+\t\tdie(\"revision walk setup failed\");\n+\n+\t/*\n+\t * Walk through the commits.\n+\t *\n+\t * XXX: This only works if the commits form a contiguous group.\n+\t * graph_commit() expects to see all commits in a particular chain.\n+\t * If intervening commits are left out, it doesn't work\n+\t * properly--it can't detect the parent-child relationships, and\n+\t * incorrectly displays commits with missing children as the tip of\n+\t * a new branch.  For example, this happens when you run\n+\t * \"git graph <path>\".\n+\t */\n+\twhile ((commit = get_revision(opts)) != NULL) {\n+\t\tgraph_commit(opts, commit, &graph);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void cmd_graph_init(int ac, const char **av, const char *prefix,\n+\t\t\t   struct rev_info *opts)\n+{\n+\tint i;\n+\n+\tgit_config(git_diff_ui_config);\n+\tif (diff_use_color_default == -1)\n+\t\tdiff_use_color_default = git_use_color_default;\n+\n+\tinit_revisions(opts, prefix);\n+\n+\topts->always_show_header = 1;\n+\topts->abbrev = DEFAULT_ABBREV;\n+\topts->topo_order = 1;\n+\topts->commit_format = CMIT_FMT_ONELINE;\n+\topts->verbose_header = 1;\n+\n+\t/*\n+\t * XXX: We don't really want to allow all of the options\n+\t * that setup_revisions() parses.  In particular, most of the\n+\t * diff-related options don't apply to us.\n+\t */\n+\tac = setup_revisions(ac, av, opts, \"HEAD\");\n+\n+\t/*\n+\t * XXX: At the moment, we can't handle --reverse\n+\t */\n+\tif (opts->reverse)\n+\t\tdie(\"--reverse is unsupported\");\n+\n+\t/*\n+\t * Only CMIT_FMT_ONELINE and CMIT_FMT_USERFORMAT make sense,\n+\t * since the commit info needs to be on a single line.\n+\t *\n+\t * XXX: It would be nice to have access to cmt_fmts from pretty.c,\n+\t * so we could print the name of the unsupported format.\n+\t */\n+\tif (opts->commit_format != CMIT_FMT_ONELINE &&\n+\t    opts->commit_format != CMIT_FMT_USERFORMAT)\n+\t\tdie(\"--pretty must be oneline or format\");\n+\n+\t/*\n+\t * We don't handle first_parent_only yet.\n+\t * (It isn't too hard to implement, but it doesn't seem terribly\n+\t * useful.)\n+\t */\n+\tif (opts->first_parent_only)\n+\t    die(\"--first-parent is not implemented for git-graph\");\n+\n+\t/*\n+\t * Handle remaining arguments.\n+\t * Eventually we may support other options here.\n+\t */\n+\tfor (i = 1; i < ac; i++) {\n+\t\tconst char *arg = av[i];\n+\t\tdie(\"unrecognized argument: %s\", arg);\n+\t}\n+}\n+\n+int cmd_graph(int ac, const char **av, const char *prefix)\n+{\n+\tstruct rev_info opts;\n+\n+\tcmd_graph_init(ac, av, prefix, &opts);\n+\treturn cmd_graph_walk(&opts);\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 95126fd..1801993 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -43,6 +43,7 @@ extern int cmd_format_patch(int argc, const char **argv, const char *prefix);\n extern int cmd_fsck(int argc, const char **argv, const char *prefix);\n extern int cmd_gc(int argc, const char **argv, const char *prefix);\n extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);\n+extern int cmd_graph(int argc, const char **argv, const char *prefix);\n extern int cmd_grep(int argc, const char **argv, const char *prefix);\n extern int cmd_help(int argc, const char **argv, const char *prefix);\n extern int cmd_http_fetch(int argc, const char **argv, const char *prefix);\ndiff --git a/git.c b/git.c\nindex b7729d7..9d5fd09 100644\n--- a/git.c\n+++ b/git.c\n@@ -306,6 +306,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"fsck-objects\", cmd_fsck, RUN_SETUP },\n \t\t{ \"gc\", cmd_gc, RUN_SETUP },\n \t\t{ \"get-tar-commit-id\", cmd_get_tar_commit_id },\n+\t\t{ \"graph\", cmd_graph, RUN_SETUP | USE_PAGER },\n \t\t{ \"grep\", cmd_grep, RUN_SETUP | USE_PAGER },\n \t\t{ \"help\", cmd_help },\n #ifndef NO_CURL\n-- \n1.5.5.rc2.1.ge6b5a.dirty\n"},{"id":"73365","messageId":"m33aq8ueq5.fsf@localhost.localdomain","threadId":"12923","inReplyTo":"20080330195840.GA8695@adamsimpkins.net","subject":"Re: [PATCH] Add new git-graph command","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-03-30T20:41:43Z","receivedAt":"2008-03-30T20:41:43Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Adam Simpkins <adam@adamsimpkins.net> writes:\n\n> This is a first pass at a command to print a text-based graph of the commit\n> history.  It is similar to the history graph shown by gitk, but doesn't\n> require a windowing system.\n\nShould I understand that git-show-branch has too cryptic an output?\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"73366","messageId":"vpqiqz4vt5e.fsf@bauges.imag.fr","threadId":"12923","inReplyTo":"20080330195840.GA8695@adamsimpkins.net","subject":"Re: [PATCH] Add new git-graph command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2008-03-30T20:44:29Z","receivedAt":"2008-03-30T20:44:29Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"Adam Simpkins <adam@adamsimpkins.net> writes:\n\n> This is a first pass at a command to print a text-based graph of the commit\n> history.  It is similar to the history graph shown by gitk, but doesn't\n> require a windowing system.\n\nDid you look at git-forest too?\n\nhttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-ed34e1966c28f24d459c5ad30a21457baa9bed23\n\n-- \nMatthieu\n"},{"id":"73369","messageId":"20080330220037.GA10978@adamsimpkins.net","threadId":"12923","inReplyTo":"m33aq8ueq5.fsf@localhost.localdomain","subject":"Re: [PATCH] Add new git-graph command","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-03-30T22:00:38Z","receivedAt":"2008-03-30T22:00:38Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Sun, Mar 30, 2008 at 01:41:43PM -0700, Jakub Narebski wrote:\n> Adam Simpkins <adam@adamsimpkins.net> writes:\n> \n> > This is a first pass at a command to print a text-based graph of the commit\n> > history.  It is similar to the history graph shown by gitk, but doesn't\n> > require a windowing system.\n> \n> Should I understand that git-show-branch has too cryptic an output?\n\nYes, I find it a bit hard to read with more than just a few branches.\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73370","messageId":"20080330220236.GB10978@adamsimpkins.net","threadId":"12923","inReplyTo":"vpqiqz4vt5e.fsf@bauges.imag.fr","subject":"Re: [PATCH] Add new git-graph command","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-03-30T22:02:36Z","receivedAt":"2008-03-30T22:02:36Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Sun, Mar 30, 2008 at 10:44:29PM +0200, Matthieu Moy wrote:\n> Adam Simpkins <adam@adamsimpkins.net> writes:\n> \n> > This is a first pass at a command to print a text-based graph of the commit\n> > history.  It is similar to the history graph shown by gitk, but doesn't\n> > require a windowing system.\n> \n> Did you look at git-forest too?\n> \n> http://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-ed34e1966c28f24d459c5ad30a21457baa9bed23\n\nInteresting, I wasn't aware of this utility.  It does look very\nsimilar.\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73372","messageId":"alpine.DEB.1.00.0803310052400.2919@eeepc-johanness","threadId":"12923","inReplyTo":"20080330195840.GA8695@adamsimpkins.net","subject":"Re: [PATCH] Add new git-graph command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-30T22:54:32Z","receivedAt":"2008-03-30T22:54:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 30 Mar 2008, Adam Simpkins wrote:\n\n> This is a first pass at a command to print a text-based graph of the \n> commit history.  It is similar to the history graph shown by gitk, but \n> doesn't require a windowing system.\n> \n> Signed-off-by: Adam Simpkins <adam@adamsimpkins.net>\n> ---\n> \n> I added this since I really like gitk, but don't always have easy access \n> to an X display on some of the systems I use.  I tried using tig, but I \n> found its graph output very hard to read.  The graph produced by \n> git-graph is less compact, but much more readable.\n> \n> Ultimately, it would probably be better to integrate this functionality \n> into git-log, instead of having it as a standalone command.  For \n> example, a new --graph option could be added to cause the graph to be \n> displayed alongside the existing git log output. However, this would \n> require tighter integration between the graphing code and the log_tree.c \n> and pretty.c code, which I'm not all that familiar with.\n\nFunny.  Some time ago, Shawn and I mused how involved it would be to write \nsome tool very similar to this, only that the output could be read back by \ngit-gui.  This would be needed for a sensible \"rebase -i\" implementation \nin git-gui.\n\nCiao,\nDscho\n"},{"id":"73383","messageId":"7vprtbwz5v.fsf@gitster.siamese.dyndns.org","threadId":"12923","inReplyTo":"20080330195840.GA8695@adamsimpkins.net","subject":"Re: [PATCH] Add new git-graph command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-30T23:49:16Z","receivedAt":"2008-03-30T23:49:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"What's wrong with \"tig -g\", I have to wonder...\n"},{"id":"73416","messageId":"20080331070904.GA19242@adamsimpkins.net","threadId":"12923","inReplyTo":"7vprtbwz5v.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Add new git-graph command","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-03-31T07:09:05Z","receivedAt":"2008-03-31T07:09:05Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Sun, Mar 30, 2008 at 04:49:16PM -0700, Junio C Hamano wrote:\n> What's wrong with \"tig -g\", I have to wonder...\n\nAs I mentioned in my initial email, I tried using the graph in tig,\nbut I found it very hard to read.  However, going back and taking a\ncloser look at tig, I think its graph is just plain wrong in some\ncases.\n\nFor example, here's a comparison of the first several lines of output\nof \"git-graph --date-order --all\" and \"tig -- --all\" in my repository\nfor one of the projects I am working on.  I've replaced the commit\nsubjects with just the abbreviated hashes.  (Hopefully your mail\nreader displays this with a monospace font.)\n\ngit-graph:\n\n*   8076867\n*   2613e2b\nM    542f526\n|\\  \n* |   642b381\n| | *   e73dfa2\n| \\ \\ \n|  \\ \\ \nM-. \\ \\   64e1d85\n|\\ \\ \\ \\ \n| | * | |   836521f\n| | | | | *   ce43181\n| | | | | *   eaeeb08\n| | | | | M    79d3db3\n| | | | | |\\  \n| | | | | | *   da5bc9e\n| * | | | | |   b947aab\n| | | | | M  \\   9ade1bc\n| | | | | |\\  | \n| | | | * | | |   8f3727b\n| | | | * | | |   2d102cd\n* | | | | | | |   bf5c6e3\n| |/ / / / / / \n|/| / / / / / \n* | | | | | |   a570370\n|/ / / / / / \n* | | | | |   dde9a00\n| | | | | *   09048ce\n| | | | |/ \n| | | | *   4ee2351\n\n\n\ntig (version 0.10.git):\n\n+ 8076867\n* 2613e2b\nM 542f526\n* | 642b381\n| | + e73dfa2\nM |  \\ \\ 64e1d85\n|`.`* | | 836521f\n| | | | | + ce43181\n| | | | | * eaeeb08\n| | | | | M 79d3db3\n| | | | | |`* da5bc9e\n| * | | | | | b947aab\n| | | | | M  \\ 9ade1bc\n| | | | * |`.`. 8f3727b\n| | | | * | | | 2d102cd\n* | | | | | | | bf5c6e3\n*' / / / / / / a570370\n*' / / / / / dde9a00\n| | | | | * 09048ce\n| | | | *' 4ee2351\n\n\nLooking at the tig output, it seems like tig is definitely screwing up\naround the 3-way merge at 64e1d85.  The way I read the output, it\nlooks like it is telling me that commit 836521f is a parent of\n542f526, which is incorrect.\n\nIt also doesn't seem to display things correctly when branch lines\nneed to cross.  For example, 836521f is a child of a570370, and\nb947aab is a child of dde9a00.  However, the way I read tig's graph,\nit looks like it is telling me exactly the opposite--that 836521f is a\nchild of dde9a00 and b947aab is a child of a570370.\n\nI think part of the problem is that tig displays exactly 1 commit per\nline.  It's hard to represent octopus merges and crossing branches if\nit all has to fit in 1 line.  This is why git-graph sometimes takes\nextra lines in between commits to display where the branch lines are\ngoing.\n\nThe git-forest command that Matthieu Moy pointed out also seems to do\na good job of representing the graph, while being a bit more compact\nthan git-graph when displaying crossing branches.  (I just joined the\nlist in the last couple of days, so I wasn't aware of git-forest.  I\ndid try googling for similar tools before I started, but only came up\nwith tig.)\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73445","messageId":"200803312017.28354.tlikonen@iki.fi","threadId":"12923","inReplyTo":"20080330195840.GA8695@adamsimpkins.net","subject":"Re: [PATCH] Add new git-graph command","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-03-31T17:17:28Z","receivedAt":"2008-03-31T17:17:28Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Adam Simpkins kirjoitti:\n\n> I added this since I really like gitk, but don't always have easy\n> access to an X display on some of the systems I use.  I tried using\n> tig, but I found its graph output very hard to read.  The graph\n> produced by git-graph is less compact, but much more readable.\n>\n> Ultimately, it would probably be better to integrate this\n> functionality into git-log, instead of having it as a standalone\n> command.  For example, a new --graph option could be added to cause\n> the graph to be displayed alongside the existing git log output.\n> However, this would require tighter integration between the graphing\n> code and the log_tree.c and pretty.c code, which I'm not all that\n> familiar with.\n\nI just want to say that I really like your 'git graph'. I would like to \nsee it integrated to 'git log', perhaps as 'git log --pretty=graph' \nor 'git log --graph'.\n"},{"id":"73449","messageId":"20080331184737.GA28412@adamsimpkins.net","threadId":"12923","inReplyTo":"200803312017.28354.tlikonen@iki.fi","subject":"Re: [PATCH] Add new git-graph command","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-03-31T18:47:38Z","receivedAt":"2008-03-31T18:47:38Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Mon, Mar 31, 2008 at 08:17:28PM +0300, Teemu Likonen wrote:\n> Adam Simpkins kirjoitti:\n> \n> > Ultimately, it would probably be better to integrate this\n> > functionality into git-log, instead of having it as a standalone\n> > command.  For example, a new --graph option could be added to cause\n> > the graph to be displayed alongside the existing git log output.\n> > However, this would require tighter integration between the graphing\n> > code and the log_tree.c and pretty.c code, which I'm not all that\n> > familiar with.\n> \n> I just want to say that I really like your 'git graph'. I would like to \n> see it integrated to 'git log', perhaps as 'git log --pretty=graph' \n> or 'git log --graph'.\n\nThanks!\n\nI was thinking more about how to add it to 'git log', and it might not\nbe all that difficult.  Instead of providing the graphing\nfunctionality as a standalone command, it could be wrapped up in the\nfollowing API:\n\n  struct graph;\n  void graph_update(struct graph *graph, struct commit *commit);\n  void graph_next_line(struct graph *graph, struct strbuf *sb);\n  bool graph_is_commit_finished(struct graph *graph);\n\nWhile walking through the commit list, graph_update() should be called\nonce for each commit.  After graph_update() has been called,\ngraph_next_line() can then be called to format the next line of the\ngraph into the strbuf.  It should be called multiple times, until\ngraph_is_commit_finished() returns true.  Then graph_update() can be\ncalled with the next commit.\n\nIf graph_next_line() is called when graph_is_commit_finished()\nreturns, it would simply format straight lines for each column.  For\nexample, if there were currently 3 columns, it would format \"| | |\".\nThis allows graph_next_line() to be used to vertically pad the graph.\n\nThis API would allow the 'git log' code to format each line of the\ngraph into a strbuf, and print it out in front of each line of normal\nlog output.  This way, it could work even with something like\n\"git log --graph --pretty=full\".  The graph would be prefixed to the\nnormal output, and padded vertically for as long as necessary.\n\nI'm just not sure how difficult it will be to change the log-tree.c\ncode to invoke graph_next_line() before each individual line of\noutput.  It certainly shouldn't be that difficult just to implement\n'git log --pretty=graph', but it may be more complicated if we want to\nmake the graphing be a boolean option that can be enabled with any\n--pretty format.\n\nI might try coding it up next weekend.\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73475","messageId":"9b3e2dc20803312105i1f890784v29928321e3e51374@mail.gmail.com","threadId":"12923","inReplyTo":"200803312017.28354.tlikonen@iki.fi","subject":"Re: [PATCH] Add new git-graph command","fromName":"Stephen Sinclair","fromEmail":"radarsat1@gmail.com","sentAt":"2008-04-01T04:05:17Z","receivedAt":"2008-04-01T04:05:17Z","isPatch":true,"sender":{"key":"radarsat1@gmail.com","avatar":null},"body":"On Mon, Mar 31, 2008 at 1:17 PM, Teemu Likonen <tlikonen@iki.fi> wrote:\n>  I just want to say that I really like your 'git graph'. I would like to\n>  see it integrated to 'git log', perhaps as 'git log --pretty=graph'\n>  or 'git log --graph'.\n\nAny reason?\n\nI don't see why it's necessary to bundle all useful commands into one\nbig super-command.  I like the idea of typing \"git-graph\".\nThen again, I happen to like the git-command syntax which seems to\nhave fallen out of favour, so don't pay attention to me.\n\n\nSteve\n"},{"id":"73476","messageId":"200804010729.51202.tlikonen@iki.fi","threadId":"12923","inReplyTo":"9b3e2dc20803312105i1f890784v29928321e3e51374@mail.gmail.com","subject":"Re: [PATCH] Add new git-graph command","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-01T04:29:51Z","receivedAt":"2008-04-01T04:29:51Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Stephen Sinclair kirjoitti:\n\n> On Mon, Mar 31, 2008 at 1:17 PM, Teemu Likonen <tlikonen@iki.fi> \nwrote:\n> >  I just want to say that I really like your 'git graph'. I would\n> > like to see it integrated to 'git log', perhaps as 'git log\n> > --pretty=graph' or 'git log --graph'.\n>\n> Any reason?\n>\n> I don't see why it's necessary to bundle all useful commands into one\n> big super-command.  I like the idea of typing \"git-graph\".\n> Then again, I happen to like the git-command syntax which seems to\n> have fallen out of favour, so don't pay attention to me.\n\nAdam's 'git graph' is a way of viewing log (in terminal environment), it \nlooks very similar to 'git log --pretty=oneline' and it accepts very \nmuch the same command line options. That's why I see 'git log' being \nlogical place for such functionality.\n\nActually, to me it would be more logical if 'git whatchanged' was 'git \nlog --changed' or '--verbose / -v' something.\n"},{"id":"73479","messageId":"20080401050229.GA23876@coredump.intra.peff.net","threadId":"12923","inReplyTo":"200804010729.51202.tlikonen@iki.fi","subject":"Re: [PATCH] Add new git-graph command","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-04-01T05:02:29Z","receivedAt":"2008-04-01T05:02:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Apr 01, 2008 at 07:29:51AM +0300, Teemu Likonen wrote:\n\n> Adam's 'git graph' is a way of viewing log (in terminal environment), it \n> looks very similar to 'git log --pretty=oneline' and it accepts very \n> much the same command line options. That's why I see 'git log' being \n> logical place for such functionality.\n\nAdam suggested that it may be possible to abstract the graphing API so\nthat it can be called progressively. I would love to see:\n\n  git log --pretty=format:'%g %h %s'\n\nwhere %g would be \"the graph lines for this commit.\" But maybe that is\nnot workable since the graph may take multiple lines to show.\n\n> Actually, to me it would be more logical if 'git whatchanged' was 'git \n> log --changed' or '--verbose / -v' something.\n\nHow about:\n\n  git log --raw --full-history --always\n\nwhich is identical. Though in most cases, one would be happy with \"git\nlog --raw\". I think whatchanged really only exists separately because it\npredates the merging of many of the revision-walking commands.\n\n-Peff\n"},{"id":"73483","messageId":"200804010807.52914.tlikonen@iki.fi","threadId":"12923","inReplyTo":"200804010729.51202.tlikonen@iki.fi","subject":"Re: [PATCH] Add new git-graph command","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-01T05:07:52Z","receivedAt":"2008-04-01T05:07:52Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Teemu Likonen kirjoitti:\n\n> Adam's 'git graph' is a way of viewing log (in terminal environment),\n> it looks very similar to 'git log --pretty=oneline' and it accepts\n> very much the same command line options. That's why I see 'git log'\n> being logical place for such functionality.\n>\n> Actually, to me it would be more logical if 'git whatchanged' was\n> 'git log --changed' or '--verbose / -v' something.\n\nMay I add that I'm the kind of user who only understands the porcelain \nGit. In my mindset it's best when commands are in logical units by \ntheir functionality (from user's point of view).\n\nI see 'git log' as a command for studying history log and to me 'git \nwhatchanged' sounds like some user's personal _alias_ to 'git \nlog --raw --full-history --always' or something (thanks Jeff). \nSimilarly 'git graph' sounds like an alias to 'git log --pretty=graph' \nfor someone who uses this a lot.\n"}]}