{"thread":{"id":"13005","subject":"[PATCH 4/4] git log: Updated --graph to work even when the commit list is pruned","startedAt":"2008-04-06T18:42:08Z","lastAt":"2008-04-08T00:58:43Z","messageCount":37,"participants":["Adam Simpkins","Teemu Likonen","Johannes Schindelin","Junio C Hamano","Jeff King","Jakub Narebski","Linus Torvalds","Govind Salinas"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"73760","messageId":"1207507332-1866-1-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":null,"subject":"[PATCH 1/4] Add history graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T18:42:08Z","receivedAt":"2008-04-06T18:42:08Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"This new API allows the commit history to be displayed as a text-based\ngraphical representation.\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\nThis is a replacement for the git-graph patch I submitted last weekend.\nIt adds the graph functionality as a new API.  A subsequent commit\nupdates \"git log\" and \"git rev-list\" to use the new API.\n\nI saw that Jan Engelhardt also submitted a patch for his git-forest\nscript this past week.  It looks like a fine tool, too.\n\nI think there are benefits to having the graphing functionality built in\nto git.  This way all of the existing log functionality (argument\nparsing, pretty formats, etc) can be easily re-used.  Other builtin\nparts of git can also take advantage of the graphing API.  It is also\none less tool for system administrators to install and maintain\n(although this depends on how the distros decide to package things, I\nsuppose).\n\nI also prefer the output of my graphing API better than that of\ngit-forest (although, as the author, I'm a bit biased).  It is more\nsimilar to gitk's graph, which many people are already familiar with.\nIt also doesn't require a terminal with Unicode support.\n\nI do like the fact that git-forest prints the names of the refs that\npoint to each commit.  For the graphing API, we could perhaps add a \"%r\"\nspecifier to --pretty=format to print the refs pointing to the commit.\n\n Documentation/technical/api-history-graph.txt |  108 ++++\n Makefile                                      |    2 +\n graph.c                                       |  711 +++++++++++++++++++++++++\n graph.h                                       |   57 ++\n 4 files changed, 878 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/technical/api-history-graph.txt\n create mode 100644 graph.c\n create mode 100644 graph.h\n\ndiff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt\nnew file mode 100644\nindex 0000000..56a0a1e\n--- /dev/null\n+++ b/Documentation/technical/api-history-graph.txt\n@@ -0,0 +1,108 @@\n+history graph API\n+=================\n+\n+The graph API is used to draw a text-based representation of the commit\n+history.  The API generates the graph in a line-by-line fashion.\n+\n+Calling sequence\n+----------------\n+\n+* Create a `struct git_graph` by calling `graph_init()`\n+\n+* Use the revision walking API to walk through a group of contiguous commits.\n+\n+* For each commit you walk through, call `graph_update()`.  Then call\n+  `graph_next_line()` repeatedly, until `graph_is_commit_finished()` returns\n+  non-zero.  Each call go `graph_next_line()` will output a single line of the\n+  graph.  The resulting lines will not contain any newlines.\n+  `graph_next_line()` returns 1 if the resulting line contains the current\n+  commit, or 0 if this is merely a line needed to adjust the graph before or\n+  after the current commit.  This return value can be used to determine where\n+  to print the commit summary information alongside the graph output.\n+\n+Data structure\n+--------------\n+`struct git_graph` is an opaque data type used to store the current graph\n+state.\n+\n+Limitations\n+-----------\n+\n+* `graph_update()` must be called with commits in topological order.  The\n+  commits must also be a contiguous group--intervening parents should not\n+  be ommitted.  Otherwise, the graph API cannot determine the proper\n+  parent-child relationships between the commits.  If intervening parents\n+  are ommitted, the next ancestor that is used will appear to be on a\n+  separate branch in the graph.\n+\n+* The graph API does not currently support reverse commit ordering.  In\n+  order to implement reverse ordering, the graphing API needs an\n+  (efficient) mechanism to find the children of a commit.\n+\n+Sample usage\n+------------\n+\n+------------\n+struct commit *commit;\n+struct git_graph *graph = graph_init();\n+\n+while ((commit = get_revision(opts)) != NULL) {\n+\tgraph_update(graph, commit);\n+\twhile (!graph_is_commit_finished(graph))\n+\t{\n+\t\tstruct strbuf sb;\n+\t\tint is_commit_line;\n+\n+\t\tstrbuf_init(&sb, 0);\n+\t\tis_commit_line = graph_next_line(graph, &sb);\n+\t\tfputs(sb.buf, stdout);\n+\n+\t\tif (is_commit_line)\n+\t\t\tlog_tree_commit(opts, commit);\n+\t\telse\n+\t\t\tputchar(opts->diffopt.line_termination);\n+\t}\n+}\n+\n+graph_release(graph);\n+------------\n+\n+Sample output\n+-------------\n+\n+The following is an example of the output from the graph API.  This output does\n+not include any commit summary information--callers of responsible for\n+outputting that information, if desired.\n+\n+------------\n+*\n+*\n+M\n+|\\\n+* |\n+| | *\n+| \\ \\\n+|  \\ \\\n+M-. \\ \\\n+|\\ \\ \\ \\\n+| | * | |\n+| | | | | *\n+| | | | | *\n+| | | | | M\n+| | | | | |\\\n+| | | | | | *\n+| * | | | | |\n+| | | | | M  \\\n+| | | | | |\\  |\n+| | | | * | | |\n+| | | | * | | |\n+* | | | | | | |\n+| |/ / / / / /\n+|/| / / / / /\n+* | | | | | |\n+|/ / / / / /\n+* | | | | |\n+| | | | | *\n+| | | | |/\n+| | | | *\n+------------\ndiff --git a/Makefile b/Makefile\nindex 7c70b00..3395b59 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -345,6 +345,7 @@ LIB_H += diff.h\n LIB_H += dir.h\n LIB_H += fsck.h\n LIB_H += git-compat-util.h\n+LIB_H += graph.h\n LIB_H += grep.h\n LIB_H += hash.h\n LIB_H += list-objects.h\n@@ -409,6 +410,7 @@ LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n LIB_OBJS += exec_cmd.o\n LIB_OBJS += fsck.o\n+LIB_OBJS += graph.o\n LIB_OBJS += grep.o\n LIB_OBJS += hash.o\n LIB_OBJS += help.o\ndiff --git a/graph.c b/graph.c\nnew file mode 100644\nindex 0000000..d8cb7d4\n--- /dev/null\n+++ b/graph.c\n@@ -0,0 +1,711 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"graph.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+ * - The output during the GRAPH_PRE_COMMIT and GRAPH_COLLAPSING states\n+ *   could be made more compact by printing horizontal lines, instead of\n+ *   long diagonal lines.  For example, during collapsing, something like\n+ *   this:          instead of this:\n+ *   | | | | |      | | | | |\n+ *   | |_|_|/       | | | |/\n+ *   |/| | |        | | |/|\n+ *   | | | |        | |/| |\n+ *                  |/| | |\n+ *                  | | | |\n+ *\n+ *   If there are several parallel diagonal lines, they will need to be\n+ *   replaced with horizontal lines on subsequent rows.\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+enum graph_state {\n+\tGRAPH_PADDING,\n+\tGRAPH_SKIP,\n+\tGRAPH_PRE_COMMIT,\n+\tGRAPH_COMMIT,\n+\tGRAPH_POST_MERGE,\n+\tGRAPH_COLLAPSING\n+};\n+\n+struct git_graph {\n+\t/*\n+\t * The commit currently being processed\n+\t */\n+\tstruct commit *commit;\n+\t/*\n+\t * The number of parents this commit has.\n+\t * (Stored so we don't have to walk over them each time we need\n+\t * this number)\n+\t */\n+\tint num_parents;\n+\t/*\n+\t * The next expansion row to print\n+\t * when state is GRAPH_PRE_COMMIT\n+\t */\n+\tint expansion_row;\n+\t/*\n+\t * The current output state.\n+\t * This tells us what kind of line graph_next_line() should output.\n+\t */\n+\tenum graph_state state;\n+\t/*\n+\t * The maximum number of columns that can be stored in the columns\n+\t * and new_columns arrays.  This is also half the number of entries\n+\t * that can be stored in the mapping and new_mapping arrays.\n+\t */\n+\tint column_capacity;\n+\t/*\n+\t * The number of columns (also called \"branch lines\" in some places)\n+\t */\n+\tint num_columns;\n+\t/*\n+\t * The number of columns in the new_columns array\n+\t */\n+\tint num_new_columns;\n+\t/*\n+\t * The number of entries in the mapping array\n+\t */\n+\tint mapping_size;\n+\t/*\n+\t * The column state before we output the current commit.\n+\t */\n+\tstruct column *columns;\n+\t/*\n+\t * The new column state after we output the current commit.\n+\t * Only valid when state is GRAPH_COLLAPSING.\n+\t */\n+\tstruct column *new_columns;\n+\t/*\n+\t * An array that tracks the current state of each\n+\t * character in the output line during state GRAPH_COLLAPSING.\n+\t * Each entry is -1 if this character is empty, or a non-negative\n+\t * integer if the character contains a branch line.  The value of\n+\t * the integer indicates the target position for this branch line.\n+\t * (I.e., this array maps the current column positions to their\n+\t * desired positions.)\n+\t *\n+\t * The maximum capacity of this array is always\n+\t * sizeof(int) * 2 * column_capacity.\n+\t */\n+\tint *mapping;\n+\t/*\n+\t * A temporary array for computing the next mapping state\n+\t * while we are outputting a mapping line.  This is stored as part\n+\t * of the git_graph simply so we don't have to allocate a new\n+\t * temporary array each time we have to output a collapsing line.\n+\t */\n+\tint *new_mapping;\n+};\n+\n+struct git_graph *graph_init()\n+{\n+\tstruct git_graph *graph = xmalloc(sizeof(struct git_graph));\n+\tgraph->commit = NULL;\n+\tgraph->num_parents = 0;\n+\tgraph->expansion_row = 0;\n+\tgraph->state = GRAPH_PADDING;\n+\tgraph->num_columns = 0;\n+\tgraph->num_new_columns = 0;\n+\tgraph->mapping_size = 0;\n+\n+\t/*\n+\t * Allocate a reasonably large default number of columns\n+\t * We'll automatically grow columns later if we need more room.\n+\t */\n+\tgraph->column_capacity = 30;\n+\tgraph->columns = xmalloc(sizeof(struct column) *\n+\t\t\t\t graph->column_capacity);\n+\tgraph->new_columns = xmalloc(sizeof(struct column) *\n+\t\t\t\t     graph->column_capacity);\n+\tgraph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);\n+\tgraph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);\n+\n+\treturn graph;\n+}\n+\n+void graph_release(struct git_graph *graph)\n+{\n+\tfree(graph->columns);\n+\tfree(graph->new_columns);\n+\tfree(graph->mapping);\n+\tfree(graph);\n+}\n+\n+static void graph_ensure_capacity(struct git_graph *graph, int num_columns)\n+{\n+\tif (graph->column_capacity >= num_columns)\n+\t\treturn;\n+\n+\tdo {\n+\t\tgraph->column_capacity *= 2;\n+\t} while (graph->column_capacity < num_columns);\n+\n+\tgraph->columns = xrealloc(graph->columns,\n+\t\t\t\t  sizeof(struct column) *\n+\t\t\t\t  graph->column_capacity);\n+\tgraph->new_columns = xrealloc(graph->new_columns,\n+\t\t\t\t      sizeof(struct column) *\n+\t\t\t\t      graph->column_capacity);\n+\tgraph->mapping = xrealloc(graph->mapping,\n+\t\t\t\t  sizeof(int) * 2 * graph->column_capacity);\n+\tgraph->new_mapping = xrealloc(graph->new_mapping,\n+\t\t\t\t      sizeof(int) * 2 * graph->column_capacity);\n+}\n+\n+static void graph_insert_into_new_columns(struct git_graph *graph,\n+\t\t\t\t\t  struct commit *commit,\n+\t\t\t\t\t  int mapping_index)\n+{\n+\tint i;\n+\n+\t/*\n+\t * If the commit is already in the new_columns list, we don't need to\n+\t * add it.  Just update the mapping correctly.\n+\t */\n+\tfor (i = 0; i < graph->num_new_columns; ++i) {\n+\t\tif (graph->new_columns[i].commit == commit) {\n+\t\t\tgraph->mapping[mapping_index] = i;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * This commit isn't already in new_columns.  Add it.\n+\t */\n+\tgraph->new_columns[graph->num_new_columns].commit = commit;\n+\tgraph->mapping[mapping_index] = graph->num_new_columns;\n+\t++graph->num_new_columns;\n+}\n+\n+static void graph_update_columns(struct git_graph *graph)\n+{\n+\tstruct commit_list *parent;\n+\tstruct column *tmp_columns;\n+\tint max_new_columns;\n+\tint mapping_idx;\n+\tint i, seen_this;\n+\n+\t/*\n+\t * Swap graph->columns with graph->new_columns\n+\t * graph->columns contains the state for the previous commit,\n+\t * and new_columns now contains the state for our commit.\n+\t *\n+\t * We'll re-use the old columns array as storage to compute the new\n+\t * columns list for the commit after this one.\n+\t */\n+\ttmp_columns = graph->columns;\n+\tgraph->columns = graph->new_columns;\n+\tgraph->num_columns = graph->num_new_columns;\n+\n+\tgraph->new_columns = tmp_columns;\n+\tgraph->num_new_columns = 0;\n+\n+\t/*\n+\t * Now update new_columns and mapping with the information for the\n+\t * commit after this one.\n+\t *\n+\t * First, make sure we have enough room.  At most, there will\n+\t * be graph->num_columns + graph->num_parents columns for the next\n+\t * commit.\n+\t */\n+\tmax_new_columns = graph->num_columns + graph->num_parents;\n+\tgraph_ensure_capacity(graph, max_new_columns);\n+\n+\t/*\n+\t * Clear out graph->mapping\n+\t */\n+\tgraph->mapping_size = 2 * max_new_columns;\n+\tfor (i = 0; i < graph->mapping_size; ++i)\n+\t\tgraph->mapping[i] = -1;\n+\n+\t/*\n+\t * Populate graph->new_columns and graph->mapping\n+\t *\n+\t * Some of the parents of this commit may already be in\n+\t * graph->columns.  If so, graph->new_columns should only contain a\n+\t * single entry for each such commit.  graph->mapping should\n+\t * contain information about where each current branch line is\n+\t * supposed to end up after the collapsing is performed.\n+\t */\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 = graph->commit;\n+\t\t} else {\n+\t\t\tcol_commit = graph->columns[i].commit;\n+\t\t}\n+\n+\t\tif (col_commit == graph->commit) {\n+\t\t\tseen_this = 1;\n+\t\t\tfor (parent = graph->commit->parents;\n+\t\t\t     parent;\n+\t\t\t     parent = parent->next) {\n+\t\t\t\tgraph_insert_into_new_columns(graph,\n+\t\t\t\t\t\t\t      parent->item,\n+\t\t\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\tgraph_insert_into_new_columns(graph, col_commit,\n+\t\t\t\t\t\t      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 (graph->mapping_size > 1 &&\n+\t       graph->mapping[graph->mapping_size - 1] < 0)\n+\t\t--graph->mapping_size;\n+}\n+\n+void graph_update(struct git_graph *graph, struct commit *commit)\n+{\n+\tstruct commit_list *parent;\n+\n+\t/*\n+\t * Set the new commit\n+\t */\n+\tgraph->commit = commit;\n+\n+\t/*\n+\t * Count how many parents this commit has\n+\t */\n+\tgraph->num_parents = 0;\n+\tfor (parent = commit->parents; parent; parent = parent->next)\n+\t\t++(graph->num_parents);\n+\n+\t/*\n+\t * Call graph_update_columns() to update\n+\t * columns, new_columns, and mapping.\n+\t */\n+\tgraph_update_columns(graph);\n+\n+\tgraph->expansion_row = 0;\n+\n+\t/*\n+\t * Update graph->state.\n+\t *\n+\t * If the previous commit didn't get to the GRAPH_PADDING state,\n+\t * it never finished its output.  Goto GRAPH_SKIP, to print out\n+\t * a line to indicate that portion of the graph is missing.\n+\t *\n+\t * Otherwise, if there are 3 or more parents, we need to print\n+\t * extra rows before the commit, to expand the branch lines around\n+\t * it and make room for it.\n+\t *\n+\t * If there are less than 3 parents, we can immediately print the\n+\t * commit line.\n+\t */\n+\tif (graph->state != GRAPH_PADDING)\n+\t\tgraph->state = GRAPH_SKIP;\n+\telse if (graph->num_parents >= 3)\n+\t\tgraph->state = GRAPH_PRE_COMMIT;\n+\telse\n+\t\tgraph->state = GRAPH_COMMIT;\n+}\n+\n+static int graph_is_mapping_correct(struct git_graph *graph)\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 < graph->mapping_size; ++i) {\n+\t\tint target = graph->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 graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)\n+{\n+\t/*\n+\t * Add additional spaces to the end of the strbuf, so that all\n+\t * lines for a particular commit have the same width.\n+\t *\n+\t * This way, fields printed to the right of the graph will remain\n+\t * aligned for the entire commit.\n+\t *\n+\t * This computation results in 3 extra space to the right in most\n+\t * cases, but only 1 extra space if the commit doesn't have any\n+\t * children that have already been displayed in the graph (i.e.,\n+\t * if the current commit isn't in graph->columns).\n+\t */\n+\tsize_t extra;\n+\tsize_t final_width = graph->num_columns + graph->num_parents;\n+\tif (graph->num_parents < 1)\n+\t\t++final_width;\n+\tfinal_width *= 2;\n+\n+\tif (sb->len >= final_width)\n+\t\treturn;\n+\n+\textra = final_width - sb->len;\n+\tstrbuf_addf(sb, \"%*s\", extra, \"\");\n+}\n+\n+static void graph_output_padding_line(struct git_graph *graph,\n+\t\t\t\t      struct strbuf *sb)\n+{\n+\tint i;\n+\n+\t/*\n+\t * We could conceivable be called with a NULL commit\n+\t * if our caller has a bug, and invokes graph_next_line()\n+\t * immediately after graph_init(), without first calling\n+\t * graph_update().  Return without outputting anything in this\n+\t * case.\n+\t */\n+\tif (!graph->commit)\n+\t\treturn;\n+\n+\t/*\n+\t * Output a padding row, that leaves all branch lines unchanged\n+\t */\n+\tfor (i = 0; i < graph->num_new_columns; ++i) {\n+\t\tstrbuf_addstr(sb, \"| \");\n+\t}\n+\n+\tgraph_pad_horizontally(graph, sb);\n+}\n+\n+static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)\n+{\n+\t/*\n+\t * Output an ellipsis to indicate that a portion\n+\t * of the graph is missing.\n+\t */\n+\tstrbuf_addstr(sb, \"...\");\n+\tgraph_pad_horizontally(graph, sb);\n+\n+\tif (graph->num_parents >= 3)\n+\t\tgraph->state = GRAPH_PRE_COMMIT;\n+\telse\n+\t\tgraph->state = GRAPH_COMMIT;\n+}\n+\n+static void graph_output_pre_commit_line(struct git_graph *graph,\n+\t\t\t\t\t struct strbuf *sb)\n+{\n+\tint num_expansion_rows;\n+\tint i, seen_this;\n+\n+\t/*\n+\t * This function formats a row that increases the space around a commit\n+\t * with multiple parents, to make room for it.  It should only be\n+\t * called when there are 3 or more parents.\n+\t *\n+\t * We need 2 extra rows for every parent over 2.\n+\t */\n+\tassert(graph->num_parents >= 3);\n+\tnum_expansion_rows = (graph->num_parents - 2) * 2;\n+\n+\t/*\n+\t * graph->expansion_row tracks the current expansion row we are on.\n+\t * It should be in the range [0, num_expansion_rows - 1]\n+\t */\n+\tassert(0 <= graph->expansion_row &&\n+\t       graph->expansion_row < num_expansion_rows);\n+\n+\t/*\n+\t * Output the row\n+\t */\n+\tseen_this = 0;\n+\tfor (i = 0; i < graph->num_columns; ++i) {\n+\t\tstruct column *col = &graph->columns[i];\n+\t\tif (col->commit == graph->commit) {\n+\t\t\tseen_this = 1;\n+\t\t\tstrbuf_addf(sb, \"| %*s\", graph->expansion_row, \"\");\n+\t\t} else if (seen_this) {\n+\t\t\tstrbuf_addstr(sb, \"\\\\ \");\n+\t\t} else {\n+\t\t\tstrbuf_addstr(sb, \"| \");\n+\t\t}\n+\t}\n+\n+\tgraph_pad_horizontally(graph, sb);\n+\n+\t/*\n+\t * Increment graph->expansion_row,\n+\t * and move to state GRAPH_COMMIT if necessary\n+\t */\n+\t++graph->expansion_row;\n+\tif (graph->expansion_row >= num_expansion_rows)\n+\t\tgraph->state = GRAPH_COMMIT;\n+}\n+\n+void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)\n+{\n+\tint seen_this = 0;\n+\tint i, j;\n+\n+\t/*\n+\t * Output the row containing this commit\n+\t * Iterate up to and including graph->num_columns,\n+\t * since the current commit may not be in any of the existing\n+\t * columns.  (This happens when the current commit doesn't have any\n+\t * children that we have already processed.)\n+\t */\n+\tseen_this = 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 = graph->commit;\n+\t\t} else {\n+\t\t\tcol_commit = graph->columns[i].commit;\n+\t\t}\n+\n+\t\tif (col_commit == graph->commit) {\n+\t\t\tseen_this = 1;\n+\t\t\tif (graph->num_parents > 1)\n+\t\t\t\tstrbuf_addch(sb, 'M');\n+\t\t\telse\n+\t\t\t\tstrbuf_addch(sb, '*');\n+\n+\t\t\tif (graph->num_parents < 2)\n+\t\t\t\tstrbuf_addch(sb, ' ');\n+\t\t\telse if (graph->num_parents == 2)\n+\t\t\t\tstrbuf_addstr(sb, \"  \");\n+\t\t\telse {\n+\t\t\t\tint num_dashes =\n+\t\t\t\t\t((graph->num_parents - 2) * 2) - 1;\n+\t\t\t\tfor (j = 0; j < num_dashes; ++j)\n+\t\t\t\t\tstrbuf_addch(sb, '-');\n+\t\t\t\tstrbuf_addstr(sb, \". \");\n+\t\t\t}\n+\t\t} else if (seen_this && (graph->num_parents > 1)) {\n+\t\t\tstrbuf_addstr(sb, \"\\\\ \");\n+\t\t} else {\n+\t\t\tstrbuf_addstr(sb, \"| \");\n+\t\t}\n+\t}\n+\n+\tgraph_pad_horizontally(graph, sb);\n+\n+\t/*\n+\t * Update graph->state\n+\t */\n+\tif (graph->num_parents > 1)\n+\t\tgraph->state = GRAPH_POST_MERGE;\n+\telse if (graph_is_mapping_correct(graph))\n+\t\tgraph->state = GRAPH_PADDING;\n+\telse\n+\t\tgraph->state = GRAPH_COLLAPSING;\n+}\n+\n+void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)\n+{\n+\tint seen_this = 0;\n+\tint i, j;\n+\n+\t/*\n+\t * Output the post-merge row\n+\t */\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 = graph->commit;\n+\t\t} else {\n+\t\t\tcol_commit = graph->columns[i].commit;\n+\t\t}\n+\n+\t\tif (col_commit == graph->commit) {\n+\t\t\tseen_this = 1;\n+\t\t\tstrbuf_addch(sb, '|');\n+\t\t\tfor (j = 0; j < graph->num_parents - 1; ++j)\n+\t\t\t\tstrbuf_addstr(sb, \"\\\\ \");\n+\t\t\tif (graph->num_parents == 2)\n+\t\t\t\tstrbuf_addch(sb, ' ');\n+\t\t} else if (seen_this && (graph->num_parents > 2)) {\n+\t\t\tstrbuf_addstr(sb, \"\\\\ \");\n+\t\t} else {\n+\t\t\tstrbuf_addstr(sb, \"| \");\n+\t\t}\n+\t}\n+\n+\tgraph_pad_horizontally(graph, sb);\n+\n+\t/*\n+\t * Update graph->state\n+\t */\n+\tif (graph_is_mapping_correct(graph))\n+\t\tgraph->state = GRAPH_PADDING;\n+\telse\n+\t\tgraph->state = GRAPH_COLLAPSING;\n+}\n+\n+void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)\n+{\n+\tint i;\n+\tint *tmp_mapping;\n+\n+\t/*\n+\t * Clear out the new_mapping array\n+\t */\n+\tfor (i = 0; i < graph->mapping_size; ++i)\n+\t\tgraph->new_mapping[i] = -1;\n+\n+\tfor (i = 0; i < graph->mapping_size; ++i) {\n+\t\tint target = graph->mapping[i];\n+\t\tif (target < 0)\n+\t\t\tcontinue;\n+\n+\t\t/*\n+\t\t * Since update_columns() always inserts the leftmost\n+\t\t * column first, each branch's target location should\n+\t\t * always be either its current location or to the left of\n+\t\t * its current location.\n+\t\t *\n+\t\t * We never have to move branches to the right.  This makes\n+\t\t * the graph much more legible, since whenever branches\n+\t\t * cross, only one is moving directions.\n+\t\t */\n+\t\tassert(target * 2 <= i);\n+\n+\t\tif (target * 2 == i) {\n+\t\t\t/*\n+\t\t\t * This column is already in the\n+\t\t\t * correct place\n+\t\t\t */\n+\t\t\tassert(graph->new_mapping[i] == -1);\n+\t\t\tgraph->new_mapping[i] = target;\n+\t\t} else if (graph->new_mapping[i - 1] < 0) {\n+\t\t\t/*\n+\t\t\t * Nothing is to the left.\n+\t\t\t * Move to the left by one\n+\t\t\t */\n+\t\t\tgraph->new_mapping[i - 1] = target;\n+\t\t} else if (graph->new_mapping[i - 1] == target) {\n+\t\t\t/*\n+\t\t\t * There is a branch line to our left\n+\t\t\t * already, and it is our target.  We\n+\t\t\t * combine with this line, since we share\n+\t\t\t * the same parent commit.\n+\t\t\t *\n+\t\t\t * We don't have to add anything to the\n+\t\t\t * output or new_mapping, since the\n+\t\t\t * existing branch line has already taken\n+\t\t\t * care of it.\n+\t\t\t */\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * There is a branch line to our left,\n+\t\t\t * but it isn't our target.  We need to\n+\t\t\t * cross over it.\n+\t\t\t *\n+\t\t\t * The space just to the left of this\n+\t\t\t * branch should always be empty.\n+\t\t\t */\n+\t\t\tassert(graph->new_mapping[i - 1] > target);\n+\t\t\tassert(graph->new_mapping[i - 2] < 0);\n+\t\t\tgraph->new_mapping[i - 2] = target;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The new mapping may be 1 smaller than the old mapping\n+\t */\n+\tif (graph->new_mapping[graph->mapping_size - 1] < 0)\n+\t\t--graph->mapping_size;\n+\n+\t/*\n+\t * Output out a line based on the new mapping info\n+\t */\n+\tfor (i = 0; i < graph->mapping_size; ++i) {\n+\t\tint target = graph->new_mapping[i];\n+\t\tif (target < 0)\n+\t\t\tstrbuf_addch(sb, ' ');\n+\t\telse if (target * 2 == i)\n+\t\t\tstrbuf_addch(sb, '|');\n+\t\telse\n+\t\t\tstrbuf_addch(sb, '/');\n+\t}\n+\n+\tgraph_pad_horizontally(graph, sb);\n+\n+\t/*\n+\t * Swap mapping and new_mapping\n+\t */\n+\ttmp_mapping = graph->mapping;\n+\tgraph->mapping = graph->new_mapping;\n+\tgraph->new_mapping = tmp_mapping;\n+\n+\t/*\n+\t * If graph->mapping indicates that all of the branch lines\n+\t * are already in the correct positions, we are done.\n+\t * Otherwise, we need to collapse some branch lines together.\n+\t */\n+\tif (graph_is_mapping_correct(graph))\n+\t\tgraph->state = GRAPH_PADDING;\n+}\n+\n+int graph_next_line(struct git_graph *graph, struct strbuf *sb)\n+{\n+\tswitch (graph->state) {\n+\tcase GRAPH_PADDING:\n+\t\tgraph_output_padding_line(graph, sb);\n+\t\treturn 0;\n+\tcase GRAPH_SKIP:\n+\t\tgraph_output_skip_line(graph, sb);\n+\t\treturn 0;\n+\tcase GRAPH_PRE_COMMIT:\n+\t\tgraph_output_pre_commit_line(graph, sb);\n+\t\treturn 0;\n+\tcase GRAPH_COMMIT:\n+\t\tgraph_output_commit_line(graph, sb);\n+\t\treturn 1;\n+\tcase GRAPH_POST_MERGE:\n+\t\tgraph_output_post_merge_line(graph, sb);\n+\t\treturn 0;\n+\tcase GRAPH_COLLAPSING:\n+\t\tgraph_output_collapsing_line(graph, sb);\n+\t\treturn 0;\n+\t}\n+\n+\tassert(0);\n+\treturn 0;\n+}\n+\n+int graph_is_commit_finished(struct git_graph const *graph)\n+{\n+\treturn (graph->state == GRAPH_PADDING);\n+}\ndiff --git a/graph.h b/graph.h\nnew file mode 100644\nindex 0000000..fc23bf2\n--- /dev/null\n+++ b/graph.h\n@@ -0,0 +1,57 @@\n+#ifndef GRAPH_H\n+#define GRAPH_H\n+\n+/* A graph is a pointer to this opaque structure */\n+struct git_graph;\n+\n+/* Defined in commit.h */\n+struct commit;\n+/* Defined in strbuf.h */\n+struct strbuf;\n+\n+/*\n+ * Create a new struct git_graph.\n+ * The graph should be freed with graph_release() when no longer needed.\n+ */\n+struct git_graph *graph_init();\n+\n+/*\n+ * Destroy a struct git_graph and free associated memory.\n+ */\n+void graph_release(struct git_graph *graph);\n+\n+/*\n+ * Update a git_graph with a new commit.\n+ * This will cause the graph to begin outputting lines for the new commit\n+ * the next time graph_next_line() is called.\n+ *\n+ * If graph_update() is called before graph_is_commit_finished() returns 1,\n+ * the next call to graph_next_line() will output an ellipsis (\"...\")\n+ * to indicate that a portion of the graph is missing.\n+ */\n+void graph_update(struct git_graph *graph, struct commit *commit);\n+\n+/*\n+ * Output the next line for a graph.\n+ * This formats the next graph line into the specified strbuf.  It is not\n+ * terminated with a newline.\n+ *\n+ * Returns 1 if the line includes the current commit, and 0 otherwise.\n+ * graph_next_line() will return 1 exactly once for each time\n+ * graph_update() is called.\n+ */\n+int graph_next_line(struct git_graph *graph, struct strbuf *sb);\n+\n+/*\n+ * Determine if a graph has finished outputting lines for the current\n+ * commit.\n+ *\n+ * Returns 1 if graph_next_line() needs to be called again before\n+ * graph_update() should be called.  Returns 0 if no more lines are needed\n+ * for this commit.  If 0 is returned, graph_next_line() may still be\n+ * called without calling graph_update(), and it will merely output\n+ * appropriate \"vertical padding\" in the graph.\n+ */\n+int graph_is_commit_finished(struct git_graph const *graph);\n+\n+#endif /* GRAPH_H */\n-- \n1.5.3.6\n"},{"id":"73761","messageId":"1207507332-1866-2-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207507332-1866-1-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 2/4] graph API: Added additional utility functions to the graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T18:42:09Z","receivedAt":"2008-04-06T18:42:09Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"Added several graph_show_* functions that print directly to stdout instead\nof to a strbuf.  Also added functions for explicitly adding vertical padding\nin the graph.\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n Documentation/technical/api-history-graph.txt |   28 +++++\n graph.c                                       |  140 +++++++++++++++++++++++++\n graph.h                                       |   51 +++++++++\n 3 files changed, 219 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt\nindex 56a0a1e..34f45b0 100644\n--- a/Documentation/technical/api-history-graph.txt\n+++ b/Documentation/technical/api-history-graph.txt\n@@ -20,6 +20,34 @@ Calling sequence\n   after the current commit.  This return value can be used to determine where\n   to print the commit summary information alongside the graph output.\n \n+Utility functions\n+-----------------\n+\n+The following functions are wrappers around `graph_next_line()` and\n+`graph_is_commit_finished()`.  They always print the output to stdout.\n+They can all be called with a NULL graph argument, in which case no graph output\n+will be printed.\n+\n+* `graph_show_commit()` calls `graph_next_line()` until it returns non-zero.\n+  This prints all graph lines up to, and including, the line containing this\n+  commit.  Output is printed to stdout.  The last line printed does not contain\n+  a terminating newline.  This should not be called if the commit line has\n+  already been printed, or it will loop forever.\n+\n+* `graph_show_oneline()` calls `graph_next_line()` and prints the result to\n+  stdout.  The line printed does not contain a terminating newline.\n+\n+* `graph_show_remainder()` calls `graph_next_line()` until\n+  `graph_is_commit_finished()` returns non-zero.  Output is printed to stdout.\n+  The last line printed does not contain a terminating newline.  Returns 1 if\n+  output was printed, and 0 if no output was necessary.\n+\n+* `graph_show_strbuf()` prints the specified strbuf to stdout, prefixing all\n+  lines but the first with a graph line.  The caller is responsible for ensuring\n+  graph output for the first line has already been printed to stdout.  (This can\n+  be done with `graph_show_commit()` or `graph_show_oneline()`.)  If a NULL\n+  graph is supplied, the strbuf is printed as-is.\n+\n Data structure\n --------------\n `struct git_graph` is an opaque data type used to store the current graph\ndiff --git a/graph.c b/graph.c\nindex d8cb7d4..e6d1d3a 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -705,7 +705,147 @@ int graph_next_line(struct git_graph *graph, struct strbuf *sb)\n \treturn 0;\n }\n \n+void graph_padding_line(struct git_graph *graph, struct strbuf *sb)\n+{\n+\tint i, j;\n+\n+\tif (graph->state != GRAPH_COMMIT) {\n+\t\tgraph_next_line(graph, sb);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Output the row containing this commit\n+\t * Iterate up to and including graph->num_columns,\n+\t * since the current commit may not be in any of the existing\n+\t * columns.  (This happens when the current commit doesn't have any\n+\t * children that we have already processed.)\n+\t */\n+\tfor (i = 0; i < graph->num_columns; ++i) {\n+\t\tstruct commit *col_commit = graph->columns[i].commit;\n+\t\tif (col_commit == graph->commit) {\n+\t\t\tstrbuf_addch(sb, '|');\n+\n+\t\t\tif (graph->num_parents < 3)\n+\t\t\t\tstrbuf_addch(sb, ' ');\n+\t\t\telse {\n+\t\t\t\tint num_spaces = ((graph->num_parents - 2) * 2);\n+\t\t\t\tfor (j = 0; j < num_spaces; ++j)\n+\t\t\t\t\tstrbuf_addch(sb, ' ');\n+\t\t\t}\n+\t\t} else {\n+\t\t\tstrbuf_addstr(sb, \"| \");\n+\t\t}\n+\t}\n+\n+\tgraph_pad_horizontally(graph, sb);\n+}\n+\n int graph_is_commit_finished(struct git_graph const *graph)\n {\n \treturn (graph->state == GRAPH_PADDING);\n }\n+\n+void graph_show_commit(struct git_graph *graph)\n+{\n+\tstruct strbuf msgbuf;\n+\tint shown_commit_line = 0;\n+\n+\tif (!graph)\n+\t\treturn;\n+\n+\tstrbuf_init(&msgbuf, 0);\n+\n+\twhile (!shown_commit_line) {\n+\t\tshown_commit_line = graph_next_line(graph, &msgbuf);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tif (!shown_commit_line)\n+\t\t\tputchar('\\n');\n+\t\tstrbuf_setlen(&msgbuf, 0);\n+\t}\n+\n+\tstrbuf_release(&msgbuf);\n+}\n+\n+void graph_show_oneline(struct git_graph *graph)\n+{\n+\tstruct strbuf msgbuf;\n+\n+\tif (!graph)\n+\t\treturn;\n+\n+\tstrbuf_init(&msgbuf, 0);\n+\tgraph_next_line(graph, &msgbuf);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tstrbuf_release(&msgbuf);\n+}\n+\n+void graph_show_padding(struct git_graph *graph)\n+{\n+\tstruct strbuf msgbuf;\n+\n+\tif (!graph)\n+\t\treturn;\n+\n+\tstrbuf_init(&msgbuf, 0);\n+\tgraph_padding_line(graph, &msgbuf);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tstrbuf_release(&msgbuf);\n+}\n+\n+int graph_show_remainder(struct git_graph *graph)\n+{\n+\tstruct strbuf msgbuf;\n+\tint shown = 0;\n+\n+\tif (!graph)\n+\t\treturn 0;\n+\n+\tif (graph_is_commit_finished(graph))\n+\t\treturn 0;\n+\n+\tstrbuf_init(&msgbuf, 0);\n+\tfor (;;) {\n+\t\tgraph_next_line(graph, &msgbuf);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tstrbuf_setlen(&msgbuf, 0);\n+\t\tshown = 1;\n+\n+\t\tif (!graph_is_commit_finished(graph))\n+\t\t\tputchar('\\n');\n+\t\telse\n+\t\t\tbreak;\n+\t}\n+\tstrbuf_release(&msgbuf);\n+\n+\treturn shown;\n+}\n+\n+\n+void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n+{\n+\tif (!graph) {\n+\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Print the strbuf line by line,\n+\t * and display the graph info before each line but the first.\n+\t */\n+\tchar *p = sb->buf;\n+\twhile (p) {\n+\t\tsize_t len;\n+\t\tchar *next_p = strchr(p, '\\n');\n+\t\tif (next_p) {\n+\t\t\tnext_p++;\n+\t\t\tlen = next_p - p;\n+\t\t} else {\n+\t\t\tlen = (sb->buf + sb->len) - p;\n+\t\t}\n+\t\tfwrite(p, sizeof(char), len, stdout);\n+\t\tif (next_p && *next_p != '\\0')\n+\t\t\tgraph_show_oneline(graph);\n+\t\tp = next_p;\n+\t}\n+}\ndiff --git a/graph.h b/graph.h\nindex fc23bf2..817862e 100644\n--- a/graph.h\n+++ b/graph.h\n@@ -43,6 +43,15 @@ void graph_update(struct git_graph *graph, struct commit *commit);\n int graph_next_line(struct git_graph *graph, struct strbuf *sb);\n \n /*\n+ * Output a padding line in the graph.\n+ * This is similar to graph_next_line().  However, it is guaranteed to\n+ * never print the current commit line.  Instead, if the commit line is\n+ * next, it will simply output a line of vertical padding, extending the\n+ * branch lines downwards, but leaving them otherwise unchanged.\n+ */\n+void graph_padding_line(struct git_graph *graph, struct strbuf *sb);\n+\n+/*\n  * Determine if a graph has finished outputting lines for the current\n  * commit.\n  *\n@@ -54,4 +63,46 @@ int graph_next_line(struct git_graph *graph, struct strbuf *sb);\n  */\n int graph_is_commit_finished(struct git_graph const *graph);\n \n+\n+/*\n+ * graph_show_*: helper functions for printing to stdout\n+ */\n+\n+\n+/*\n+ * If the graph is non-NULL, print the history graph to stdout,\n+ * up to and including the line containing this commit.\n+ * Does not print a terminating newline on the last line.\n+ */\n+void graph_show_commit(struct git_graph *graph);\n+\n+/*\n+ * If the graph is non-NULL, print one line of the history graph to stdout.\n+ * Does not print a terminating newline on the last line.\n+ */\n+void graph_show_oneline(struct git_graph *graph);\n+\n+/*\n+ * If the graph is non-NULL, print one line of vertical graph padding to\n+ * stdout.  Does not print a terminating newline on the last line.\n+ */\n+void graph_show_padding(struct git_graph *graph);\n+\n+/*\n+ * If the graph is non-NULL, print the rest of the history graph for this\n+ * commit to stdout.  Does not print a terminating newline on the last line.\n+ */\n+int graph_show_remainder(struct git_graph *graph);\n+\n+/*\n+ * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the\n+ * first will be prefixed with the graph output.\n+ *\n+ * Since the firat line will not include the graph ouput, the caller is\n+ * responsible for printing this line's graph (perhaps via\n+ * graph_show_commit() or graph_show_oneline()) before calling\n+ * graph_show_strbuf().\n+ */\n+void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);\n+\n #endif /* GRAPH_H */\n-- \n1.5.3.6\n"},{"id":"73759","messageId":"1207507332-1866-3-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207507332-1866-2-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 3/4] git log and git rev-list: Add --graph option","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T18:42:10Z","receivedAt":"2008-04-06T18:42:10Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"The --graph option causes a text-based representation of the history\ngraph to be printed on the left-hand side of the output.\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\nThe --graph option can be used with any --pretty format.  If this change\nis too intrusive for people's liking, a smaller change could probably be\ndone that only adds --pretty=graph and --pretty=graph:<user_fmt>\noptions.\n\nAt the moment, --graph and --reverse are mutually exclusive.  With\nJunio's new --children patch for git log, it probably shouldn't be too\nhard to get them working together.\n\n Documentation/technical/api-history-graph.txt |   61 +++++++++++++++++------\n builtin-rev-list.c                            |   50 +++++++++++++++++--\n log-tree.c                                    |   65 +++++++++++++++++++++++--\n revision.c                                    |   28 ++++++++++-\n revision.h                                    |    3 +\n 5 files changed, 180 insertions(+), 27 deletions(-)\n\ndiff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt\nindex 34f45b0..c5f9054 100644\n--- a/Documentation/technical/api-history-graph.txt\n+++ b/Documentation/technical/api-history-graph.txt\n@@ -4,26 +4,34 @@ history graph API\n The graph API is used to draw a text-based representation of the commit\n history.  The API generates the graph in a line-by-line fashion.\n \n-Calling sequence\n-----------------\n+Functions\n+---------\n \n-* Create a `struct git_graph` by calling `graph_init()`\n+Core functions:\n \n-* Use the revision walking API to walk through a group of contiguous commits.\n+* `graph_init()` creates a new `struct git_graph`\n+\n+* `graph_release()` destroys a `struct git_graph`, and frees the memory\n+  associated with it.\n \n-* For each commit you walk through, call `graph_update()`.  Then call\n-  `graph_next_line()` repeatedly, until `graph_is_commit_finished()` returns\n-  non-zero.  Each call go `graph_next_line()` will output a single line of the\n-  graph.  The resulting lines will not contain any newlines.\n-  `graph_next_line()` returns 1 if the resulting line contains the current\n-  commit, or 0 if this is merely a line needed to adjust the graph before or\n-  after the current commit.  This return value can be used to determine where\n-  to print the commit summary information alongside the graph output.\n+* `graph_update()` moves the graph to a new commit.\n \n-Utility functions\n------------------\n+* `graph_next_line()` outputs the next line of the graph into a strbuf.  It does\n+  not add a terminating newline.\n \n-The following functions are wrappers around `graph_next_line()` and\n+* `graph_padding_line()` outputs a line of vertical padding in the graph.  It\n+  is similar to `graph_next_line()`, but is guaranteed to never print the line\n+  containing the current commit.  Where `graph_next_line()` would print the\n+  commit line next, `graph_padding_line()` prints a line that simply extends\n+  all branch lines downwards one row, leaving their positions unchanged.\n+\n+* `graph_is_commit_finished()` determines if the graph has output all lines\n+  necessary for the current commit.  If `graph_update()` is called before all\n+  lines for the current commit have been printed, the next call to\n+  `graph_next_line()` will output an ellipsis, to indicate that a portion of the\n+  graph was omitted.\n+\n+The following utility functions are wrappers around `graph_next_line()` and\n `graph_is_commit_finished()`.  They always print the output to stdout.\n They can all be called with a NULL graph argument, in which case no graph output\n will be printed.\n@@ -37,6 +45,9 @@ will be printed.\n * `graph_show_oneline()` calls `graph_next_line()` and prints the result to\n   stdout.  The line printed does not contain a terminating newline.\n \n+* `graph_show_padding()` calls `graph_padding_line()` and prints the result to\n+  stdout.  The line printed does not contain a terminating newline.\n+\n * `graph_show_remainder()` calls `graph_next_line()` until\n   `graph_is_commit_finished()` returns non-zero.  Output is printed to stdout.\n   The last line printed does not contain a terminating newline.  Returns 1 if\n@@ -53,6 +64,26 @@ Data structure\n `struct git_graph` is an opaque data type used to store the current graph\n state.\n \n+Calling sequence\n+----------------\n+\n+* Create a `struct git_graph` by calling `graph_init()`.  When using the\n+  revision walking API, this is done automatically by `setup_revisions()` if\n+  the '--graph' option is supplied.\n+\n+* Use the revision walking API to walk through a group of contiguous commits.\n+  The `get_revision()` function automatically calls `graph_update()` each time\n+  it is invoked.\n+\n+* For each commit, call `graph_next_line()` repeatedly, until\n+  `graph_is_commit_finished()` returns non-zero.  Each call go\n+  `graph_next_line()` will output a single line of the graph.  The resulting\n+  lines will not contain any newlines.  `graph_next_line()` returns 1 if the\n+  resulting line contains the current commit, or 0 if this is merely a line\n+  needed to adjust the graph before or after the current commit.  This return\n+  value can be used to determine where to print the commit summary information\n+  alongside the graph output.\n+\n Limitations\n -----------\n \ndiff --git a/builtin-rev-list.c b/builtin-rev-list.c\nindex edc0bd3..4427caa 100644\n--- a/builtin-rev-list.c\n+++ b/builtin-rev-list.c\n@@ -10,6 +10,7 @@\n #include \"list-objects.h\"\n #include \"builtin.h\"\n #include \"log-tree.h\"\n+#include \"graph.h\"\n \n /* bits #0-15 in revision.h */\n \n@@ -58,6 +59,8 @@ static const char *header_prefix;\n static void finish_commit(struct commit *commit);\n static void show_commit(struct commit *commit)\n {\n+\tgraph_show_commit(revs.graph);\n+\n \tif (show_timestamp)\n \t\tprintf(\"%lu \", commit->date);\n \tif (header_prefix)\n@@ -85,20 +88,55 @@ static void show_commit(struct commit *commit)\n \t\t}\n \t}\n \tshow_decorations(commit);\n-\tif (revs.commit_format == CMIT_FMT_ONELINE)\n-\t\tputchar(' ');\n-\telse\n-\t\tputchar('\\n');\n \n \tif (revs.verbose_header && commit->buffer) {\n \t\tstruct strbuf buf;\n+\n+\t\tif (revs.commit_format == CMIT_FMT_ONELINE) {\n+\t\t\tputchar(' ');\n+\t\t} else {\n+\t\t\tputchar('\\n');\n+\t\t\tgraph_show_oneline(revs.graph);\n+\t\t}\n+\n \t\tstrbuf_init(&buf, 0);\n \t\tpretty_print_commit(revs.commit_format, commit,\n \t\t\t\t    &buf, revs.abbrev, NULL, NULL,\n \t\t\t\t    revs.date_mode, 0);\n-\t\tif (buf.len)\n-\t\t\tprintf(\"%s%c\", buf.buf, hdr_termination);\n+\t\tif (buf.len) {\n+\t\t\tif (revs.graph) {\n+\t\t\t\tgraph_show_strbuf(revs.graph, &buf);\n+\t\t\t\t if (revs.commit_format == CMIT_FMT_ONELINE) {\n+\t\t\t\t\t /*\n+\t\t\t\t\t  * For CMIT_FMT_ONELINE, the buffer\n+\t\t\t\t\t  * doesn't end in a newline, so add one\n+\t\t\t\t\t  * first if graph_show_remainder()\n+\t\t\t\t\t  * needs to print more lines.\n+\t\t\t\t\t  */\n+\t\t\t\t\tif (!graph_is_commit_finished(revs.graph)) {\n+\t\t\t\t\t\tputchar('\\n');\n+\t\t\t\t\t\tgraph_show_remainder(revs.graph);\n+\t\t\t\t\t}\n+\t\t\t\t} else {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * For other formats, we want at least\n+\t\t\t\t\t * one line separating commits.  If\n+\t\t\t\t\t * graph_show_remainder() doesn't print\n+\t\t\t\t\t * anything, add a padding line.\n+\t\t\t\t\t */\n+\t\t\t\t\tif (!graph_show_remainder(revs.graph))\n+\t\t\t\t\t\tgraph_show_padding(revs.graph);\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\tfwrite(buf.buf, sizeof(char), buf.len, stdout);\n+\t\t\t}\n+\t\t\tputchar(hdr_termination);\n+\t\t}\n \t\tstrbuf_release(&buf);\n+\t} else {\n+\t\tputchar('\\n');\n+\t\tif (graph_show_remainder(revs.graph))\n+\t\t\tputchar('\\n');\n \t}\n \tmaybe_flush_or_die(stdout, \"stdout\");\n \tfinish_commit(commit);\ndiff --git a/log-tree.c b/log-tree.c\nindex 5b29639..5b78d1b 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"diff.h\"\n #include \"commit.h\"\n+#include \"graph.h\"\n #include \"log-tree.h\"\n #include \"reflog-walk.h\"\n \n@@ -221,6 +222,8 @@ void show_log(struct rev_info *opt, const char *sep)\n \n \topt->loginfo = NULL;\n \tif (!opt->verbose_header) {\n+\t\tgraph_show_commit(opt->graph);\n+\n \t\tif (commit->object.flags & BOUNDARY)\n \t\t\tputchar('-');\n \t\telse if (commit->object.flags & UNINTERESTING)\n@@ -235,6 +238,10 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\tif (opt->parents)\n \t\t\tshow_parents(commit, abbrev_commit);\n \t\tshow_decorations(commit);\n+\t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n+\t\t\tputchar('\\n');\n+\t\t\tgraph_show_remainder(opt->graph);\n+\t\t}\n \t\tputchar(opt->diffopt.line_termination);\n \t\treturn;\n \t}\n@@ -251,11 +258,19 @@ void show_log(struct rev_info *opt, const char *sep)\n \textra = \"\";\n \tif (*sep != '\\n' && opt->commit_format == CMIT_FMT_ONELINE)\n \t\textra = \"\\n\";\n-\tif (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)\n+\tif (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE) {\n+\t\tgraph_show_padding(opt->graph);\n \t\tputchar(opt->diffopt.line_termination);\n+\t}\n \topt->shown_one = 1;\n \n \t/*\n+\t * If the history graph was requested,\n+\t * print the graph, up to this commit's line\n+\t */\n+\tgraph_show_commit(opt->graph);\n+\n+\t/*\n \t * Print header line of header..\n \t */\n \n@@ -287,8 +302,19 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\t\t\t\t\t  abbrev_commit));\n \t\tshow_decorations(commit);\n \t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n-\t\tputchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\\n');\n+\t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n+\t\t\tputchar(' ');\n+\t\t} else {\n+\t\t\tputchar('\\n');\n+\t\t\tgraph_show_oneline(opt->graph);\n+\t\t}\n \t\tif (opt->reflog_info) {\n+\t\t\t/*\n+\t\t\t * setup_revisions() ensures that opt->reflog_info\n+\t\t\t * and opt->graph cannot both be set,\n+\t\t\t * so we don't need to worry about printing the\n+\t\t\t * graph info here.\n+\t\t\t */\n \t\t\tshow_reflog_message(opt->reflog_info,\n \t\t\t\t    opt->commit_format == CMIT_FMT_ONELINE,\n \t\t\t\t    opt->date_mode);\n@@ -314,11 +340,40 @@ void show_log(struct rev_info *opt, const char *sep)\n \n \tif (opt->add_signoff)\n \t\tappend_signoff(&msgbuf, opt->add_signoff);\n-\tif (opt->show_log_size)\n+\tif (opt->show_log_size) {\n \t\tprintf(\"log size %i\\n\", (int)msgbuf.len);\n+\t\tgraph_show_oneline(opt->graph);\n+\t}\n \n-\tif (msgbuf.len)\n-\t\tprintf(\"%s%s%s\", msgbuf.buf, extra, sep);\n+\tif (msgbuf.len) {\n+\t\tif (opt->graph) {\n+\t\t\tgraph_show_strbuf(opt->graph, &msgbuf);\n+\t\t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n+\t\t\t\t/*\n+\t\t\t\t * For CMIT_FMT_ONELINE, the buffer doesn't\n+\t\t\t\t * end in a newline, so add one first if\n+\t\t\t\t * graph_show_remainder() needs to print\n+\t\t\t\t * more lines.\n+\t\t\t\t */\n+\t\t\t\tif (!graph_is_commit_finished(opt->graph)) {\n+\t\t\t\t\tputchar('\\n');\n+\t\t\t\t\tgraph_show_remainder(opt->graph);\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * For other formats, we want at least one\n+\t\t\t\t * line separating commits.  If\n+\t\t\t\t * graph_show_remainder() doesn't print\n+\t\t\t\t * anything, add a padding line.\n+\t\t\t\t */\n+\t\t\t\tif (graph_show_remainder(opt->graph))\n+\t\t\t\t\tputchar('\\n');\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\t}\n+\t\tprintf(\"%s%s\", extra, sep);\n+\t}\n \tstrbuf_release(&msgbuf);\n }\n \ndiff --git a/revision.c b/revision.c\nindex 196fedc..6c9622c 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -6,6 +6,7 @@\n #include \"diff.h\"\n #include \"refs.h\"\n #include \"revision.h\"\n+#include \"graph.h\"\n #include \"grep.h\"\n #include \"reflog-walk.h\"\n #include \"patch-ids.h\"\n@@ -1201,6 +1202,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t\trevs->commit_format = get_commit_format(arg+8);\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!prefixcmp(arg, \"--graph\")) {\n+\t\t\t\trevs->topo_order = 1;\n+\t\t\t\trevs->graph = graph_init();\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--root\")) {\n \t\t\t\trevs->show_root_diff = 1;\n \t\t\t\tcontinue;\n@@ -1395,6 +1401,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \tif (revs->reverse && revs->reflog_info)\n \t\tdie(\"cannot combine --reverse with --walk-reflogs\");\n \n+\t/*\n+\t * Limitations on the graph functionality\n+\t */\n+\tif (revs->reverse && revs->graph)\n+\t\tdie(\"cannot combine --reverse with --graph\");\n+\n+\tif (revs->reflog_info && revs->graph)\n+\t\tdie(\"cannot combine --walk-reflogs with --graph\");\n+\n+\tif (revs->graph && revs->prune_data)\n+\t\tdie(\"cannot use --graph when pruning commit list\");\n+\n \treturn left;\n }\n \n@@ -1596,7 +1614,7 @@ static void gc_boundary(struct object_array *array)\n \t}\n }\n \n-struct commit *get_revision(struct rev_info *revs)\n+static struct commit *get_revision_internal(struct rev_info *revs)\n {\n \tstruct commit *c = NULL;\n \tstruct commit_list *l;\n@@ -1703,3 +1721,11 @@ struct commit *get_revision(struct rev_info *revs)\n \n \treturn c;\n }\n+\n+struct commit *get_revision(struct rev_info *revs)\n+{\n+\tstruct commit *c = get_revision_internal(revs);\n+\tif (c && revs->graph)\n+\t\tgraph_update(revs->graph, c);\n+\treturn c;\n+}\ndiff --git a/revision.h b/revision.h\nindex c8b3b94..d06b991 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -87,6 +87,9 @@ struct rev_info {\n \t/* Filter by commit log message */\n \tstruct grep_opt\t*grep_filter;\n \n+\t/* Display history graph */\n+\tstruct git_graph *graph;\n+\n \t/* special limits */\n \tint skip_count;\n \tint max_count;\n-- \n1.5.3.6\n"},{"id":"73758","messageId":"1207507332-1866-4-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207507332-1866-3-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 4/4] git log: Updated --graph to work even when the commit list is pruned","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T18:42:11Z","receivedAt":"2008-04-06T18:42:11Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"Signed-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n graph.c    |   22 +++++++++++++++-------\n revision.c |    3 ---\n 2 files changed, 15 insertions(+), 10 deletions(-)\n\ndiff --git a/graph.c b/graph.c\nindex e6d1d3a..be4000f 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -1,6 +1,8 @@\n #include \"cache.h\"\n #include \"commit.h\"\n #include \"graph.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n \n /*\n  * TODO:\n@@ -174,17 +176,24 @@ static void graph_ensure_capacity(struct git_graph *graph, int num_columns)\n \n static void graph_insert_into_new_columns(struct git_graph *graph,\n \t\t\t\t\t  struct commit *commit,\n-\t\t\t\t\t  int mapping_index)\n+\t\t\t\t\t  int *mapping_index)\n {\n \tint i;\n \n \t/*\n+\t * Ignore uinteresting and pruned commits\n+\t */\n+\tif (commit->object.flags & (UNINTERESTING | TREESAME))\n+\t\treturn;\n+\n+\t/*\n \t * If the commit is already in the new_columns list, we don't need to\n \t * add it.  Just update the mapping correctly.\n \t */\n \tfor (i = 0; i < graph->num_new_columns; ++i) {\n \t\tif (graph->new_columns[i].commit == commit) {\n-\t\t\tgraph->mapping[mapping_index] = i;\n+\t\t\tgraph->mapping[*mapping_index] = i;\n+\t\t\t*mapping_index += 2;\n \t\t\treturn;\n \t\t}\n \t}\n@@ -193,7 +202,8 @@ static void graph_insert_into_new_columns(struct git_graph *graph,\n \t * This commit isn't already in new_columns.  Add it.\n \t */\n \tgraph->new_columns[graph->num_new_columns].commit = commit;\n-\tgraph->mapping[mapping_index] = graph->num_new_columns;\n+\tgraph->mapping[*mapping_index] = graph->num_new_columns;\n+\t*mapping_index += 2;\n \t++graph->num_new_columns;\n }\n \n@@ -266,13 +276,11 @@ static void graph_update_columns(struct git_graph *graph)\n \t\t\t     parent = parent->next) {\n \t\t\t\tgraph_insert_into_new_columns(graph,\n \t\t\t\t\t\t\t      parent->item,\n-\t\t\t\t\t\t\t      mapping_idx);\n-\t\t\t\tmapping_idx += 2;\n+\t\t\t\t\t\t\t      &mapping_idx);\n \t\t\t}\n \t\t} else {\n \t\t\tgraph_insert_into_new_columns(graph, col_commit,\n-\t\t\t\t\t\t      mapping_idx);\n-\t\t\tmapping_idx += 2;\n+\t\t\t\t\t\t      &mapping_idx);\n \t\t}\n \t}\n \ndiff --git a/revision.c b/revision.c\nindex 6c9622c..6a1f513 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1410,9 +1410,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \tif (revs->reflog_info && revs->graph)\n \t\tdie(\"cannot combine --walk-reflogs with --graph\");\n \n-\tif (revs->graph && revs->prune_data)\n-\t\tdie(\"cannot use --graph when pruning commit list\");\n-\n \treturn left;\n }\n \n-- \n1.5.3.6\n"},{"id":"73771","messageId":"200804062330.16900.tlikonen@iki.fi","threadId":"13005","inReplyTo":"1207507332-1866-1-git-send-email-adam@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-06T20:30:16Z","receivedAt":"2008-04-06T20:30:16Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Adam Simpkins kirjoitti:\n\n> I think there are benefits to having the graphing functionality built\n> in to git.  This way all of the existing log functionality (argument\n> parsing, pretty formats, etc) can be easily re-used.  Other builtin\n> parts of git can also take advantage of the graphing API. \n\nI don't know anything about the inside stuff but from user's point of \nview this is the kind of text-based graph I'd agree to be integrated to \nGit (well, I integrated it already to my Git). I like this a lot, \nthanks.\n\nMaybe git-doc.txt should be updated with new option(s)?\n"},{"id":"73772","messageId":"alpine.DEB.1.00.0804062135290.12583@eeepc-johanness","threadId":"13005","inReplyTo":"1207507332-1866-1-git-send-email-adam@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-04-06T20:42:20Z","receivedAt":"2008-04-06T20:42:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 6 Apr 2008, Adam Simpkins wrote:\n\n> I do like the fact that git-forest prints the names of the refs that \n> point to each commit.  For the graphing API, we could perhaps add a \"%r\" \n> specifier to --pretty=format to print the refs pointing to the commit.\n\nWould \"--decorate\" help?\n\nCiao,\nDscho\n"},{"id":"73778","messageId":"alpine.DEB.1.00.0804062204580.12583@eeepc-johanness","threadId":"13005","inReplyTo":"1207507332-1866-1-git-send-email-adam@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-04-06T21:06:24Z","receivedAt":"2008-04-06T21:06:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 6 Apr 2008, Adam Simpkins wrote:\n\n> diff --git a/graph.h b/graph.h\n> new file mode 100644\n> index 0000000..fc23bf2\n> --- /dev/null\n> +++ b/graph.h\n> @@ -0,0 +1,57 @@\n> +#ifndef GRAPH_H\n> +#define GRAPH_H\n> +\n> +/* A graph is a pointer to this opaque structure */\n> +struct git_graph;\n> +\n> +/* Defined in commit.h */\n> +struct commit;\n> +/* Defined in strbuf.h */\n> +struct strbuf;\n\nYou do not need those.\n\nApart from that, it looks very, very clean to me.  (Except maybe the \nprefix ++ that could have been a postfix ++ in the line before, but that \nis just me.)\n\nCiao,\nDscho\n"},{"id":"73779","messageId":"200804070015.32783.tlikonen@iki.fi","threadId":"13005","inReplyTo":"1207507332-1866-3-git-send-email-adam@adamsimpkins.net","subject":"Re: [PATCH 3/4] git log and git rev-list: Add --graph option","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-06T21:15:32Z","receivedAt":"2008-04-06T21:15:32Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Adam Simpkins kirjoitti:\n\n> The --graph option causes a text-based representation of the history\n> graph to be printed on the left-hand side of the output.\n\nThe '--graph' seems to work nicely with every '--pretty=' option \nexcept 'email'.\n\n$ git log --graph --pretty=email\n\n\n|\nM     From 77ad7a49d3cc946487ca759e5361effbcfb03be5 [...]\nFrom: Junio C Hamano <gitster@pobox.com>\n|\\    Date: Fri, 4 Apr 2008 22:38:32 -0700\n| |   Subject: [PATCH] Merge git://repo.or.cz/git-gui\n| |   \n| |   * git://repo.or.cz/git-gui:\n| |     git-gui: use +/- instead of ]/[ to show [...]\n| |     git-gui: Update french translation\n| |     git-gui: Switch keybindings for [ and ] to [...]\n\n\nThe 'From:' field is always at the column 1.\n"},{"id":"73780","messageId":"1207517128-9776-1-git-send-email-tlikonen@iki.fi","threadId":"13005","inReplyTo":"1207507332-1866-1-git-send-email-adam@adamsimpkins.net","subject":"[PATCH] bash: Add command line completion of --graph (git log)","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-06T21:25:28Z","receivedAt":"2008-04-06T21:25:28Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Signed-off-by: Teemu Likonen <tlikonen@iki.fi>\n---\n contrib/completion/git-completion.bash |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 791e30f..cd26d0c 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -754,6 +754,7 @@ _git_log ()\n \t\t\t--pretty= --name-status --name-only --raw\n \t\t\t--not --all\n \t\t\t--left-right --cherry-pick\n+\t\t\t--graph\n \t\t\t\"\n \t\treturn\n \t\t;;\n-- \n1.5.5-rc3.GIT\n"},{"id":"73782","messageId":"20080406214445.GA5822@adamsimpkins.net","threadId":"13005","inReplyTo":"200804062330.16900.tlikonen@iki.fi","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T21:44:46Z","receivedAt":"2008-04-06T21:44:46Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Sun, Apr 06, 2008 at 11:30:16PM +0300, Teemu Likonen wrote:\n> \n> Maybe git-doc.txt should be updated with new option(s)?\n\nAh, you're right, I forgot about that.  I'll send out a new patch to\nupdate Documentation/rev-list-options.txt\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73783","messageId":"1207518444-5955-1-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207507332-1866-4-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 5/5] Document the new --graph option for log and rev-list","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T21:47:24Z","receivedAt":"2008-04-06T21:47:24Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"Signed-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n Documentation/rev-list-options.txt |   10 ++++++++++\n 1 files changed, 10 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 2648a55..ce6a101 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -75,6 +75,16 @@ you would get an output line this:\n \t-xxxxxxx... 1st on a\n -----------------------------------------------------------------------\n \n+--graph::\n+\n+\tDraw a text-based graphical representation of the commit history\n+\ton the left hand side of the output.  This may cause extra lines\n+\tto be printed in between commits, in order for the graph history\n+\tto be drawn properly.\n++\n+This implies the '--topo-order' option by default, but the\n+'--date-order' option may also be specified.\n+\n Diff Formatting\n ~~~~~~~~~~~~~~~\n \n-- \n1.5.3.6\n"},{"id":"73784","messageId":"20080406220447.GB5822@adamsimpkins.net","threadId":"13005","inReplyTo":"alpine.DEB.1.00.0804062204580.12583@eeepc-johanness","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T22:04:48Z","receivedAt":"2008-04-06T22:04:48Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Sun, Apr 06, 2008 at 10:06:24PM +0100, Johannes Schindelin wrote:\n> \n> On Sun, 6 Apr 2008, Adam Simpkins wrote:\n> \n> > +/* Defined in commit.h */\n> > +struct commit;\n> > +/* Defined in strbuf.h */\n> > +struct strbuf;\n> \n> You do not need those.\n\nI added them so that graph.h can be included without including any\nother header files first.  They can be taken out if we assume that all\nusers of graph.h will include commit.h and strbuf.h first.\n\n> Apart from that, it looks very, very clean to me.  (Except maybe the \n> prefix ++ that could have been a postfix ++ in the line before, but that \n> is just me.)\n\nSorry, force of habit.  I tried to remember to use postfix in most\nplaces, but I guess I forgot in that place.\n\nI do most of my programming in C++, which allows crazy things like\ndefining prefix and postfix ++ and -- operators on classes.  When\nusing these on classes, the prefix operator is normally more efficient\nthan the postfix version, so I'm just in the habit of using prefix\nincrement everywhere.  This can easily be changed if postfix is\npreferred for the git coding style.\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73785","messageId":"alpine.DEB.1.00.0804062315090.12583@eeepc-johanness","threadId":"13005","inReplyTo":"20080406220447.GB5822@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-04-06T22:15:58Z","receivedAt":"2008-04-06T22:15:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 6 Apr 2008, Adam Simpkins wrote:\n\n> On Sun, Apr 06, 2008 at 10:06:24PM +0100, Johannes Schindelin wrote:\n> > \n> > On Sun, 6 Apr 2008, Adam Simpkins wrote:\n> > \n> > > +/* Defined in commit.h */\n> > > +struct commit;\n> > > +/* Defined in strbuf.h */\n> > > +struct strbuf;\n> > \n> > You do not need those.\n> \n> I added them so that graph.h can be included without including any other \n> header files first.  They can be taken out if we assume that all users \n> of graph.h will include commit.h and strbuf.h first.\n\nAFAICT you do not even need them then.  Using \"struct strbuf *\" without \never declaring struct strbuf before that is perfectly valid.\n\nCiao,\nDscho\n"},{"id":"73787","messageId":"20080406224724.GC5822@adamsimpkins.net","threadId":"13005","inReplyTo":"alpine.DEB.1.00.0804062135290.12583@eeepc-johanness","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T22:47:25Z","receivedAt":"2008-04-06T22:47:25Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Sun, Apr 06, 2008 at 09:42:20PM +0100, Johannes Schindelin wrote:\n> Hi,\n> \n> On Sun, 6 Apr 2008, Adam Simpkins wrote:\n> \n> > I do like the fact that git-forest prints the names of the refs that \n> > point to each commit.  For the graphing API, we could perhaps add a \"%r\" \n> > specifier to --pretty=format to print the refs pointing to the commit.\n> \n> Would \"--decorate\" help?\n\nYes, it does.  Unfortunately, it doesn't have any effect with\n--pretty=format.\n\n\nActually, going back and testing this, it looks like I have a bug when\nhandling --graph together with --pretty=format.  There's a missing\nnewline after the user's format message and the next graph line.  I'll\ntry to fix this and submit a patch later this evening.\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73788","messageId":"20080406225131.GD5822@adamsimpkins.net","threadId":"13005","inReplyTo":"200804070015.32783.tlikonen@iki.fi","subject":"Re: [PATCH 3/4] git log and git rev-list: Add --graph option","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T22:51:32Z","receivedAt":"2008-04-06T22:51:32Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Mon, Apr 07, 2008 at 12:15:32AM +0300, Teemu Likonen wrote:\n> Adam Simpkins kirjoitti:\n> \n> > The --graph option causes a text-based representation of the history\n> > graph to be printed on the left-hand side of the output.\n> \n> The '--graph' seems to work nicely with every '--pretty=' option \n> except 'email'.\n> \n> $ git log --graph --pretty=email\n\nYep, I forgot to test that one.  Thanks for pointing it out.\nIt looks like it shouldn't be too hard to fix.  I'll try to fix it and\nsubmit a patch later this evening.\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73789","messageId":"20080406225819.GE5822@adamsimpkins.net","threadId":"13005","inReplyTo":"alpine.DEB.1.00.0804062315090.12583@eeepc-johanness","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-06T22:58:20Z","receivedAt":"2008-04-06T22:58:20Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Sun, Apr 06, 2008 at 11:15:58PM +0100, Johannes Schindelin wrote:\n> On Sun, 6 Apr 2008, Adam Simpkins wrote:\n> > On Sun, Apr 06, 2008 at 10:06:24PM +0100, Johannes Schindelin wrote:\n> > > On Sun, 6 Apr 2008, Adam Simpkins wrote:\n> > > \n> > > > +/* Defined in commit.h */\n> > > > +struct commit;\n> > > > +/* Defined in strbuf.h */\n> > > > +struct strbuf;\n> > > \n> > > You do not need those.\n> > \n> > I added them so that graph.h can be included without including any other \n> > header files first.  They can be taken out if we assume that all users \n> > of graph.h will include commit.h and strbuf.h first.\n> \n> AFAICT you do not even need them then.  Using \"struct strbuf *\" without \n> ever declaring struct strbuf before that is perfectly valid.\n\nTrying to compile the following test code with gcc 4.1.2 results in a\nwarning.\n\ntest.c:\n\t#include <stdio.h>\n\t\n\tvoid test(struct strbuf *sb);\n\tint main(int argc, char **argv)\n\t{\n\t\ttest(NULL);\n\t\treturn 0;\n\t}\n\n$ gcc -c test.c\ntest.c:3: warning: ‘struct strbuf’ declared inside parameter list\ntest.c:3: warning: its scope is only this definition or declaration, which is probably not what you want\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73795","messageId":"7v4paes6h9.fsf@gitster.siamese.dyndns.org","threadId":"13005","inReplyTo":"20080406220447.GB5822@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-04-07T03:12:50Z","receivedAt":"2008-04-07T03:12:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Simpkins <adam@adamsimpkins.net> writes:\n\n> On Sun, Apr 06, 2008 at 10:06:24PM +0100, Johannes Schindelin wrote:\n>> \n>> On Sun, 6 Apr 2008, Adam Simpkins wrote:\n>> \n>> > +/* Defined in commit.h */\n>> > +struct commit;\n>> > +/* Defined in strbuf.h */\n>> > +struct strbuf;\n>> \n>> You do not need those.\n>\n> I added them so that graph.h can be included without including any\n> other header files first.  They can be taken out if we assume that all\n> users of graph.h will include commit.h and strbuf.h first.\n\nThe current practice is we expect .c files to include necessary .h files\nthemselves and avoid including .h files from others (I do not personally\nnecessarily agree with this practice, by the way, but that is the way it\nis).  So please drop them.\n\n> Sorry, force of habit.  I tried to remember to use postfix in most\n> places, but I guess I forgot in that place.\n\nWe seem to consistently use postfix when increment is done only for its\nside effect.\n"},{"id":"73797","messageId":"200804070824.10764.tlikonen@iki.fi","threadId":"13005","inReplyTo":"20080406224724.GC5822@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-07T05:24:10Z","receivedAt":"2008-04-07T05:24:10Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Adam Simpkins kirjoitti:\n\n> Actually, going back and testing this, it looks like I have a bug\n> when handling --graph together with --pretty=format.  There's a\n> missing newline after the user's format message and the next graph\n> line.  I'll try to fix this and submit a patch later this evening.\n\nAlso, the output is not indented for options that display some \nadditional information to commit message. Those include:\n\n--raw\n--stat\n--numstat\n--shortstat\n--summary\n--name-only\n--name-status\n\nI'm not sure if the diff output of -p, -u etc. should be \nindented--probably not--but for different stat and summary options it \nwould be nice to not have their output displayed over the graph area. \nEspecially --name-status is funny since it displays \"M\" to column 1 to \nindicate modified file while \"M\" also means merge commit in the graph.\n"},{"id":"73801","messageId":"20080407072629.GA5168@mithlond.arda.local","threadId":"13005","inReplyTo":"1207507332-1866-1-git-send-email-adam@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-07T07:26:29Z","receivedAt":"2008-04-07T07:26:29Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Adam Simpkins kirjoitti (6.4.2008 klo 11.42):\n\n> [...] graphing API [...] is more similar to gitk's graph, which many\n> people are already familiar with. It also doesn't require a terminal\n> with Unicode support.\n\nAs I've spent some time in testing the --graph functionality I'm\nspamming my discoveries here.\n\nWhen limiting the log output to a subdirectory or to a file the graph\nbecomes quite hard to understand. Probably the easiest way to\ndemonstrate my point is to compare side by side (for example)\n\n  git log --graph --pretty=oneline -- Documentation/\n \nand\n\n  gitk -- Documentation/\n\nin the Git repository. gitk draws lines between commits even when they\nare not in direct parent-child relationship (i.e. there is longer series\nof commits between them). With log --graph it's hard to tell which\ndevelopment line some commits come from.\n\nThe whole-repository log graphs seem to work nicely though.\n"},{"id":"73806","messageId":"1207555281-9362-1-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207518444-5955-1-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 1/4] graph API: Fixed coding style problems","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-07T08:01:18Z","receivedAt":"2008-04-07T08:01:18Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"- Removed pre-declarations of structs from graph.h; all users are\n  expected to include the necessary header files first.\n- Replaced prefix increment and decrement operators with postfix\n  operators\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n graph.c |   42 +++++++++++++++++++++---------------------\n graph.h |    5 -----\n 2 files changed, 21 insertions(+), 26 deletions(-)\n\ndiff --git a/graph.c b/graph.c\nindex be4000f..6f99063 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -190,7 +190,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph,\n \t * If the commit is already in the new_columns list, we don't need to\n \t * add it.  Just update the mapping correctly.\n \t */\n-\tfor (i = 0; i < graph->num_new_columns; ++i) {\n+\tfor (i = 0; i < graph->num_new_columns; i++) {\n \t\tif (graph->new_columns[i].commit == commit) {\n \t\t\tgraph->mapping[*mapping_index] = i;\n \t\t\t*mapping_index += 2;\n@@ -204,7 +204,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph,\n \tgraph->new_columns[graph->num_new_columns].commit = commit;\n \tgraph->mapping[*mapping_index] = graph->num_new_columns;\n \t*mapping_index += 2;\n-\t++graph->num_new_columns;\n+\tgraph->num_new_columns++;\n }\n \n static void graph_update_columns(struct git_graph *graph)\n@@ -245,7 +245,7 @@ static void graph_update_columns(struct git_graph *graph)\n \t * Clear out graph->mapping\n \t */\n \tgraph->mapping_size = 2 * max_new_columns;\n-\tfor (i = 0; i < graph->mapping_size; ++i)\n+\tfor (i = 0; i < graph->mapping_size; i++)\n \t\tgraph->mapping[i] = -1;\n \n \t/*\n@@ -259,7 +259,7 @@ static void graph_update_columns(struct git_graph *graph)\n \t */\n \tseen_this = 0;\n \tmapping_idx = 0;\n-\tfor (i = 0; i <= graph->num_columns; ++i) {\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@@ -289,7 +289,7 @@ static void graph_update_columns(struct git_graph *graph)\n \t */\n \twhile (graph->mapping_size > 1 &&\n \t       graph->mapping[graph->mapping_size - 1] < 0)\n-\t\t--graph->mapping_size;\n+\t\tgraph->mapping_size--;\n }\n \n void graph_update(struct git_graph *graph, struct commit *commit)\n@@ -306,7 +306,7 @@ void graph_update(struct git_graph *graph, struct commit *commit)\n \t */\n \tgraph->num_parents = 0;\n \tfor (parent = commit->parents; parent; parent = parent->next)\n-\t\t++(graph->num_parents);\n+\t\tgraph->num_parents++;\n \n \t/*\n \t * Call graph_update_columns() to update\n@@ -348,7 +348,7 @@ static int graph_is_mapping_correct(struct git_graph *graph)\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 < graph->mapping_size; ++i) {\n+\tfor (i = 0; i < graph->mapping_size; i++) {\n \t\tint target = graph->mapping[i];\n \t\tif (target < 0)\n \t\t\tcontinue;\n@@ -377,7 +377,7 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)\n \tsize_t extra;\n \tsize_t final_width = graph->num_columns + graph->num_parents;\n \tif (graph->num_parents < 1)\n-\t\t++final_width;\n+\t\tfinal_width++;\n \tfinal_width *= 2;\n \n \tif (sb->len >= final_width)\n@@ -405,7 +405,7 @@ static void graph_output_padding_line(struct git_graph *graph,\n \t/*\n \t * Output a padding row, that leaves all branch lines unchanged\n \t */\n-\tfor (i = 0; i < graph->num_new_columns; ++i) {\n+\tfor (i = 0; i < graph->num_new_columns; i++) {\n \t\tstrbuf_addstr(sb, \"| \");\n \t}\n \n@@ -454,7 +454,7 @@ static void graph_output_pre_commit_line(struct git_graph *graph,\n \t * Output the row\n \t */\n \tseen_this = 0;\n-\tfor (i = 0; i < graph->num_columns; ++i) {\n+\tfor (i = 0; i < graph->num_columns; i++) {\n \t\tstruct column *col = &graph->columns[i];\n \t\tif (col->commit == graph->commit) {\n \t\t\tseen_this = 1;\n@@ -472,7 +472,7 @@ static void graph_output_pre_commit_line(struct git_graph *graph,\n \t * Increment graph->expansion_row,\n \t * and move to state GRAPH_COMMIT if necessary\n \t */\n-\t++graph->expansion_row;\n+\tgraph->expansion_row++;\n \tif (graph->expansion_row >= num_expansion_rows)\n \t\tgraph->state = GRAPH_COMMIT;\n }\n@@ -490,7 +490,7 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)\n \t * children that we have already processed.)\n \t */\n \tseen_this = 0;\n-\tfor (i = 0; i <= graph->num_columns; ++i) {\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@@ -514,7 +514,7 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)\n \t\t\telse {\n \t\t\t\tint num_dashes =\n \t\t\t\t\t((graph->num_parents - 2) * 2) - 1;\n-\t\t\t\tfor (j = 0; j < num_dashes; ++j)\n+\t\t\t\tfor (j = 0; j < num_dashes; j++)\n \t\t\t\t\tstrbuf_addch(sb, '-');\n \t\t\t\tstrbuf_addstr(sb, \". \");\n \t\t\t}\n@@ -546,7 +546,7 @@ void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)\n \t/*\n \t * Output the post-merge row\n \t */\n-\tfor (i = 0; i <= graph->num_columns; ++i) {\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@@ -559,7 +559,7 @@ void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)\n \t\tif (col_commit == graph->commit) {\n \t\t\tseen_this = 1;\n \t\t\tstrbuf_addch(sb, '|');\n-\t\t\tfor (j = 0; j < graph->num_parents - 1; ++j)\n+\t\t\tfor (j = 0; j < graph->num_parents - 1; j++)\n \t\t\t\tstrbuf_addstr(sb, \"\\\\ \");\n \t\t\tif (graph->num_parents == 2)\n \t\t\t\tstrbuf_addch(sb, ' ');\n@@ -589,10 +589,10 @@ void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)\n \t/*\n \t * Clear out the new_mapping array\n \t */\n-\tfor (i = 0; i < graph->mapping_size; ++i)\n+\tfor (i = 0; i < graph->mapping_size; i++)\n \t\tgraph->new_mapping[i] = -1;\n \n-\tfor (i = 0; i < graph->mapping_size; ++i) {\n+\tfor (i = 0; i < graph->mapping_size; i++) {\n \t\tint target = graph->mapping[i];\n \t\tif (target < 0)\n \t\t\tcontinue;\n@@ -653,12 +653,12 @@ void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)\n \t * The new mapping may be 1 smaller than the old mapping\n \t */\n \tif (graph->new_mapping[graph->mapping_size - 1] < 0)\n-\t\t--graph->mapping_size;\n+\t\tgraph->mapping_size--;\n \n \t/*\n \t * Output out a line based on the new mapping info\n \t */\n-\tfor (i = 0; i < graph->mapping_size; ++i) {\n+\tfor (i = 0; i < graph->mapping_size; i++) {\n \t\tint target = graph->new_mapping[i];\n \t\tif (target < 0)\n \t\t\tstrbuf_addch(sb, ' ');\n@@ -729,7 +729,7 @@ void graph_padding_line(struct git_graph *graph, struct strbuf *sb)\n \t * columns.  (This happens when the current commit doesn't have any\n \t * children that we have already processed.)\n \t */\n-\tfor (i = 0; i < graph->num_columns; ++i) {\n+\tfor (i = 0; i < graph->num_columns; i++) {\n \t\tstruct commit *col_commit = graph->columns[i].commit;\n \t\tif (col_commit == graph->commit) {\n \t\t\tstrbuf_addch(sb, '|');\n@@ -738,7 +738,7 @@ void graph_padding_line(struct git_graph *graph, struct strbuf *sb)\n \t\t\t\tstrbuf_addch(sb, ' ');\n \t\t\telse {\n \t\t\t\tint num_spaces = ((graph->num_parents - 2) * 2);\n-\t\t\t\tfor (j = 0; j < num_spaces; ++j)\n+\t\t\t\tfor (j = 0; j < num_spaces; j++)\n \t\t\t\t\tstrbuf_addch(sb, ' ');\n \t\t\t}\n \t\t} else {\ndiff --git a/graph.h b/graph.h\nindex 817862e..c1f6892 100644\n--- a/graph.h\n+++ b/graph.h\n@@ -4,11 +4,6 @@\n /* A graph is a pointer to this opaque structure */\n struct git_graph;\n \n-/* Defined in commit.h */\n-struct commit;\n-/* Defined in strbuf.h */\n-struct strbuf;\n-\n /*\n  * Create a new struct git_graph.\n  * The graph should be freed with graph_release() when no longer needed.\n-- \n1.5.3.6\n"},{"id":"73807","messageId":"1207555281-9362-2-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207555281-9362-1-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-07T08:01:19Z","receivedAt":"2008-04-07T08:01:19Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"Previously, the --graph option had problems when used together with\n--pretty=format.  The output was missing a newline at the end of each\nentry, before the next graph line.\n\nThis change updates the code to treat CMIT_FMT_USERFORMAT just like\nCMIT_FMT_ONELINE, even when --graph is not in use.  Like\nCMIT_FMT_ONELINE, the pretty_print_commit() output for\nCMIT_FMT_USERFORMAT lacks a terminating newline.  Similarly, there\nshould be no blank line between entries for CMIT_FMT_USERFORMAT.\n\nThe old code took care of these cases for CMIT_FMT_ONELINE, but not for\nCMIT_FMT_USERFORMAT.  For CMIT_FMT_USERFORMAT, show_log() left each\nentry without a terminating newline.  The next call to show_log() would\nthen try to print an extra blank line between entries.  However, since\nthe previous entry lacked a newline, the \"blank line\" simply added a\nnewline at the end of the previous entry.  For the most part, this made\nthe output look correct.  The very last entry in the output was always\nmissing a terminating newline, but piping the output through less would\nhide this fact.  (Running with --no-pager would clearly show the missing\nnewline at the end of the output.)\n\nI believe the old behavior was accidental, rather than intentional.  The\nnew code always prints a newline at the end of the last entry.\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n builtin-rev-list.c |   52 ++++++++++++++---------------\n graph.c            |   93 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n graph.h            |   20 ++++++++++-\n log-tree.c         |   61 ++++++++++++++-------------------\n 4 files changed, 163 insertions(+), 63 deletions(-)\n\ndiff --git a/builtin-rev-list.c b/builtin-rev-list.c\nindex 4427caa..ac6a6f9 100644\n--- a/builtin-rev-list.c\n+++ b/builtin-rev-list.c\n@@ -53,7 +53,6 @@ static struct rev_info revs;\n \n static int bisect_list;\n static int show_timestamp;\n-static int hdr_termination;\n static const char *header_prefix;\n \n static void finish_commit(struct commit *commit);\n@@ -103,34 +102,34 @@ static void show_commit(struct commit *commit)\n \t\tpretty_print_commit(revs.commit_format, commit,\n \t\t\t\t    &buf, revs.abbrev, NULL, NULL,\n \t\t\t\t    revs.date_mode, 0);\n-\t\tif (buf.len) {\n-\t\t\tif (revs.graph) {\n-\t\t\t\tgraph_show_strbuf(revs.graph, &buf);\n-\t\t\t\t if (revs.commit_format == CMIT_FMT_ONELINE) {\n-\t\t\t\t\t /*\n-\t\t\t\t\t  * For CMIT_FMT_ONELINE, the buffer\n-\t\t\t\t\t  * doesn't end in a newline, so add one\n-\t\t\t\t\t  * first if graph_show_remainder()\n-\t\t\t\t\t  * needs to print more lines.\n-\t\t\t\t\t  */\n-\t\t\t\t\tif (!graph_is_commit_finished(revs.graph)) {\n-\t\t\t\t\t\tputchar('\\n');\n-\t\t\t\t\t\tgraph_show_remainder(revs.graph);\n-\t\t\t\t\t}\n-\t\t\t\t} else {\n-\t\t\t\t\t/*\n-\t\t\t\t\t * For other formats, we want at least\n-\t\t\t\t\t * one line separating commits.  If\n-\t\t\t\t\t * graph_show_remainder() doesn't print\n-\t\t\t\t\t * anything, add a padding line.\n-\t\t\t\t\t */\n-\t\t\t\t\tif (!graph_show_remainder(revs.graph))\n-\t\t\t\t\t\tgraph_show_padding(revs.graph);\n-\t\t\t\t}\n+\t\tif (revs.graph) {\n+\t\t\t/*\n+\t\t\t * If revs.graph is non-NULL, we always need to print\n+\t\t\t * an extra newline, even if msgbuf is empty.\n+\t\t\t */\n+\t\t\tgraph_show_commit_msg(revs.graph, &buf,\n+\t\t\t\t\t      revs.commit_format);\n+\n+\t\t\t/*\n+\t\t\t * For CMIT_FMT_ONELINE and CMIT_FMT_USERFORMAT,\n+\t\t\t * we need to add a terminating newline to the\n+\t\t\t * output.\n+\t\t\t *\n+\t\t\t * For other formats, we want an extra padding line\n+\t\t\t * after the output.\n+\t\t\t */\n+\t\t\tif (revs.commit_format == CMIT_FMT_ONELINE ||\n+\t\t\t    revs.commit_format == CMIT_FMT_USERFORMAT) {\n+\t\t\t\tputchar('\\n');\n \t\t\t} else {\n+\t\t\t\tgraph_show_padding(revs.graph);\n+\t\t\t\tputchar('\\n');\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif (buf.len) {\n \t\t\t\tfwrite(buf.buf, sizeof(char), buf.len, stdout);\n+\t\t\t\tputchar('\\n');\n \t\t\t}\n-\t\t\tputchar(hdr_termination);\n \t\t}\n \t\tstrbuf_release(&buf);\n \t} else {\n@@ -630,7 +629,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t}\n \tif (revs.commit_format != CMIT_FMT_UNSPECIFIED) {\n \t\t/* The command line has a --pretty  */\n-\t\thdr_termination = '\\n';\n \t\tif (revs.commit_format == CMIT_FMT_ONELINE)\n \t\t\theader_prefix = \"\";\n \t\telse\ndiff --git a/graph.c b/graph.c\nindex 6f99063..3ecc378 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -857,3 +857,96 @@ void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \t\tp = next_p;\n \t}\n }\n+\n+void graph_show_commit_msg(struct git_graph *graph,\n+\t\t\t   struct strbuf const *sb,\n+\t\t\t   enum cmit_fmt commit_format)\n+{\n+\tif (!graph) {\n+\t\t/*\n+\t\t * If there's no graph, just print the message buffer.\n+\t\t *\n+\t\t * The message buffer for CMIT_FMT_ONELINE and\n+\t\t * CMIT_FMT_USERFORMAT are already missing a terminating\n+\t\t * newline.  All of the other formats should have it.\n+\t\t */\n+\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * If the message buffer is empty, just print the graph output.\n+\t * For example, this can happen with CMIT_FMT_USERFORMAT if the\n+\t * format is the empty string.\n+\t *\n+\t * We need a newline first before the next graph line.\n+\t */\n+\tif (!sb->len) {\n+\t\tif (!graph_is_commit_finished(graph)) {\n+\t\t\tputchar('\\n');\n+\t\t\tgraph_show_remainder(graph);\n+\t\t}\n+\t\tif (commit_format != CMIT_FMT_ONELINE &&\n+\t\t    commit_format != CMIT_FMT_USERFORMAT)\n+\t\t\tputchar('\\n');\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Show the commit message\n+\t */\n+\tgraph_show_strbuf(graph, sb);\n+\n+\t/*\n+\t * Show the remainder of the graph output,\n+\t * and make sure we terminate the output properly.\n+\t */\n+\tif (commit_format == CMIT_FMT_ONELINE ||\n+\t    commit_format == CMIT_FMT_USERFORMAT) {\n+\t\t/*\n+\t\t * For CMIT_FMT_ONELINE and CMIT_FMT_USERFORMAT, we need to\n+\t\t * make sure that the printed output still needs a\n+\t\t * terminating newline.\n+\t\t */\n+\t\tif (sb->buf[sb->len - 1] == '\\n') {\n+\t\t\t/*\n+\t\t\t * If the buf already ends in a newline (which can\n+\t\t\t * happen with CMIT_FMT_USERFORMAT if the format\n+\t\t\t * ends in \"%n\"), we need to print the graph output\n+\t\t\t * for the start of the new line after the newline.\n+\t\t\t *\n+\t\t\t * Call graph_show_remainder() to let it do this.\n+\t\t\t * If it doesn't print anything, call\n+\t\t\t * graph_show_oneline() to force an extra line to\n+\t\t\t * be printed.\n+\t\t\t */\n+\t\t\tif (!graph_show_remainder(graph))\n+\t\t\t\tgraph_show_oneline(graph);\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * The buffer output didn't end in a newline.\n+\t\t\t *\n+\t\t\t * If there is more output to print, add a newline\n+\t\t\t * before calling graph_show_remainder().\n+\t\t\t * Otherwise, we're done.\n+\t\t\t */\n+\t\t\tif (!graph_is_commit_finished(graph)) {\n+\t\t\t\tputchar('\\n');\n+\t\t\t\tgraph_show_remainder(graph);\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * For other formats, the output we print needs to end in a\n+\t\t * newline.  The message buffer should already end in a\n+\t\t * newline.\n+\t\t *\n+\t\t * Call graph_show_remainder() to print the rest of the\n+\t\t * graph.  If it prints anything out, we need to add a\n+\t\t * terminating newline at the end of its output.\n+\t\t */\n+\t\tassert(sb->buf[sb->len - 1] == '\\n');\n+\t\tif (graph_show_remainder(graph))\n+\t\t\tputchar('\\n');\n+\t}\n+}\ndiff --git a/graph.h b/graph.h\nindex c1f6892..aa02fca 100644\n--- a/graph.h\n+++ b/graph.h\n@@ -93,11 +93,29 @@ int graph_show_remainder(struct git_graph *graph);\n  * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the\n  * first will be prefixed with the graph output.\n  *\n- * Since the firat line will not include the graph ouput, the caller is\n+ * If the strbuf ends with a newline, the output will end after this\n+ * newline.  A new graph line will not be printed after the final newline.\n+ *\n+ * Since the first line will not include the graph ouput, the caller is\n  * responsible for printing this line's graph (perhaps via\n  * graph_show_commit() or graph_show_oneline()) before calling\n  * graph_show_strbuf().\n  */\n void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);\n \n+/*\n+ * Print a commit message strbuf and the remainder of the graph to stdout.\n+ *\n+ * This is similar to graph_show_strbuf(), but it always prints the\n+ * remainder of the graph, and it has additional logic necessary for use by\n+ * the \"log\" and \"rev-list\" commands.\n+ *\n+ * If cmt_format is CMIT_FMT_ONELINE or CMIT_FMT_USERFORMAT, the resulting\n+ * output will still need a terminating newline.  The caller is responsible\n+ * for adding this.  Otherwise, no terminating newline is needed.\n+ */\n+void graph_show_commit_msg(struct git_graph *graph,\n+\t\t\t   struct strbuf const *sb,\n+\t\t\t   enum cmit_fmt commit_format);\n+\n #endif /* GRAPH_H */\ndiff --git a/log-tree.c b/log-tree.c\nindex 5b78d1b..0d7e521 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -247,18 +247,24 @@ void show_log(struct rev_info *opt, const char *sep)\n \t}\n \n \t/*\n-\t * The \"oneline\" format has several special cases:\n-\t *  - The pretty-printed commit lacks a newline at the end\n-\t *    of the buffer, but we do want to make sure that we\n-\t *    have a newline there. If the separator isn't already\n-\t *    a newline, add an extra one.\n-\t *  - unlike other log messages, the one-line format does\n-\t *    not have an empty line between entries.\n+\t * The \"oneline\" and \"userformat\" formats have several special\n+\t * cases:\n+\t *  - The pretty-printed commit needs an additional a newline at\n+\t *    the end of the buffer.  (The \"oneline\" output never ends in a\n+\t *    newline.  The \"userformat\" output may, but only if the user's\n+\t *    format explicitly ends in \"%n\".)  If the separator isn't\n+\t *    already a newline, add an extra one.\n+\t *  - unlike other log messages, the one-line and userformat\n+\t *    formats do not have an empty line between entries.\n \t */\n \textra = \"\";\n-\tif (*sep != '\\n' && opt->commit_format == CMIT_FMT_ONELINE)\n+\tif (*sep != '\\n' &&\n+\t    (opt->commit_format == CMIT_FMT_ONELINE ||\n+\t     opt->commit_format == CMIT_FMT_USERFORMAT))\n \t\textra = \"\\n\";\n-\tif (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE) {\n+\tif (opt->shown_one &&\n+\t    (opt->commit_format != CMIT_FMT_ONELINE &&\n+\t     opt->commit_format != CMIT_FMT_USERFORMAT)) {\n \t\tgraph_show_padding(opt->graph);\n \t\tputchar(opt->diffopt.line_termination);\n \t}\n@@ -345,34 +351,19 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \n-\tif (msgbuf.len) {\n-\t\tif (opt->graph) {\n-\t\t\tgraph_show_strbuf(opt->graph, &msgbuf);\n-\t\t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n-\t\t\t\t/*\n-\t\t\t\t * For CMIT_FMT_ONELINE, the buffer doesn't\n-\t\t\t\t * end in a newline, so add one first if\n-\t\t\t\t * graph_show_remainder() needs to print\n-\t\t\t\t * more lines.\n-\t\t\t\t */\n-\t\t\t\tif (!graph_is_commit_finished(opt->graph)) {\n-\t\t\t\t\tputchar('\\n');\n-\t\t\t\t\tgraph_show_remainder(opt->graph);\n-\t\t\t\t}\n-\t\t\t} else {\n-\t\t\t\t/*\n-\t\t\t\t * For other formats, we want at least one\n-\t\t\t\t * line separating commits.  If\n-\t\t\t\t * graph_show_remainder() doesn't print\n-\t\t\t\t * anything, add a padding line.\n-\t\t\t\t */\n-\t\t\t\tif (graph_show_remainder(opt->graph))\n-\t\t\t\t\tputchar('\\n');\n-\t\t\t}\n-\t\t} else {\n+\tif (opt->graph) {\n+\t\t/*\n+\t\t * If opt->graph is non-NULL, we always need to print\n+\t\t * extra and sep, even if msgbuf is empty.\n+\t\t */\n+\t\tgraph_show_commit_msg(opt->graph, &msgbuf,\n+\t\t\t\t      opt->commit_format);\n+\t\tprintf(\"%s%s\", extra, sep);\n+\t} else {\n+\t\tif (msgbuf.len) {\n \t\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\t\tprintf(\"%s%s\", extra, sep);\n \t\t}\n-\t\tprintf(\"%s%s\", extra, sep);\n \t}\n \tstrbuf_release(&msgbuf);\n }\n-- \n1.5.3.6\n"},{"id":"73805","messageId":"1207555281-9362-3-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207555281-9362-2-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 3/4] log and rev-list: Fix --graph output with --pretty=email","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-07T08:01:20Z","receivedAt":"2008-04-07T08:01:20Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"As pointed out by Teemu Likonen, the initial line of\npretty_print_commit() output wasn't correctly prefixed by the graph\ninformation.\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n log-tree.c |    9 +++++++--\n 1 files changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 0d7e521..d2cb26a 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -166,11 +166,16 @@ void log_write_email_headers(struct rev_info *opt, const char *name,\n \t}\n \n \tprintf(\"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n-\tif (opt->message_id)\n+\tgraph_show_oneline(opt->graph);\n+\tif (opt->message_id) {\n \t\tprintf(\"Message-Id: <%s>\\n\", opt->message_id);\n-\tif (opt->ref_message_id)\n+\t\tgraph_show_oneline(opt->graph);\n+\t}\n+\tif (opt->ref_message_id) {\n \t\tprintf(\"In-Reply-To: <%s>\\nReferences: <%s>\\n\",\n \t\t       opt->ref_message_id, opt->ref_message_id);\n+\t\tgraph_show_oneline(opt->graph);\n+\t}\n \tif (opt->mime_boundary) {\n \t\tstatic char subject_buffer[1024];\n \t\tstatic char buffer[1024];\n-- \n1.5.3.6\n"},{"id":"73804","messageId":"1207555281-9362-4-git-send-email-adam@adamsimpkins.net","threadId":"13005","inReplyTo":"1207555281-9362-3-git-send-email-adam@adamsimpkins.net","subject":"[PATCH 4/4] log and rev-list: Improve --graph output when commits have been pruned","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-07T08:01:21Z","receivedAt":"2008-04-07T08:01:21Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"This change automatically enables parent rewriting when --graph is\nspecified.  This makes the graph output look much nicer when commit\npruning is enabled.\n\nThe existing rev_info \"parents\" flag has been split into\ntwo flags: \"rewrite_parents\" causes the parents to be rewritten, while\n\"print_parents\" causes the log and rev-list commands to print the\nparents.  The --parents option now enables both rewrite_parents and\nprint_parents, while --graph enables only rewrite_parents.\n\nSigned-off-by: Adam Simpkins <adam@adamsimpkins.net>\n---\n builtin-rev-list.c |    2 +-\n log-tree.c         |    4 ++--\n revision.c         |    8 +++++---\n revision.h         |    3 ++-\n 4 files changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin-rev-list.c b/builtin-rev-list.c\nindex ac6a6f9..f8103bd 100644\n--- a/builtin-rev-list.c\n+++ b/builtin-rev-list.c\n@@ -79,7 +79,7 @@ static void show_commit(struct commit *commit)\n \t\t      stdout);\n \telse\n \t\tfputs(sha1_to_hex(commit->object.sha1), stdout);\n-\tif (revs.parents) {\n+\tif (revs.print_parents) {\n \t\tstruct commit_list *parents = commit->parents;\n \t\twhile (parents) {\n \t\t\tprintf(\" %s\", sha1_to_hex(parents->item->object.sha1));\ndiff --git a/log-tree.c b/log-tree.c\nindex d2cb26a..4ad10af 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -240,7 +240,7 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\t\t\tputchar('>');\n \t\t}\n \t\tfputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);\n-\t\tif (opt->parents)\n+\t\tif (opt->print_parents)\n \t\t\tshow_parents(commit, abbrev_commit);\n \t\tshow_decorations(commit);\n \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n@@ -305,7 +305,7 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\t}\n \t\tfputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit),\n \t\t      stdout);\n-\t\tif (opt->parents)\n+\t\tif (opt->print_parents)\n \t\t\tshow_parents(commit, abbrev_commit);\n \t\tif (parent)\n \t\t\tprintf(\" (from %s)\",\ndiff --git a/revision.c b/revision.c\nindex 6a1f513..7d9d333 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1105,7 +1105,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t\t}\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--parents\")) {\n-\t\t\t\trevs->parents = 1;\n+\t\t\t\trevs->rewrite_parents = 1;\n+\t\t\t\trevs->print_parents = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--dense\")) {\n@@ -1204,6 +1205,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--graph\")) {\n \t\t\t\trevs->topo_order = 1;\n+\t\t\t\trevs->rewrite_parents = 1;\n \t\t\t\trevs->graph = graph_init();\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -1538,13 +1540,13 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)\n \t\t/* Commit without changes? */\n \t\tif (commit->object.flags & TREESAME) {\n \t\t\t/* drop merges unless we want parenthood */\n-\t\t\tif (!revs->parents)\n+\t\t\tif (!revs->rewrite_parents)\n \t\t\t\treturn commit_ignore;\n \t\t\t/* non-merge - always ignore it */\n \t\t\tif (!commit->parents || !commit->parents->next)\n \t\t\t\treturn commit_ignore;\n \t\t}\n-\t\tif (revs->parents && rewrite_parents(revs, commit) < 0)\n+\t\tif (revs->rewrite_parents && rewrite_parents(revs, commit) < 0)\n \t\t\treturn commit_error;\n \t}\n \treturn commit_show;\ndiff --git a/revision.h b/revision.h\nindex d06b991..170543d 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -46,7 +46,8 @@ struct rev_info {\n \t\t\tunpacked:1, /* see also ignore_packed below */\n \t\t\tboundary:2,\n \t\t\tleft_right:1,\n-\t\t\tparents:1,\n+\t\t\trewrite_parents:1,\n+\t\t\tprint_parents:1,\n \t\t\treverse:1,\n \t\t\tcherry_pick:1,\n \t\t\tfirst_parent_only:1;\n-- \n1.5.3.6\n"},{"id":"73808","messageId":"20080407080600.GA9298@adamsimpkins.net","threadId":"13005","inReplyTo":"20080407072629.GA5168@mithlond.arda.local","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-07T08:06:01Z","receivedAt":"2008-04-07T08:06:01Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Mon, Apr 07, 2008 at 10:26:29AM +0300, Teemu Likonen wrote:\n> \n> As I've spent some time in testing the --graph functionality I'm\n> spamming my discoveries here.\n> \n> When limiting the log output to a subdirectory or to a file the graph\n> becomes quite hard to understand. Probably the easiest way to\n> demonstrate my point is to compare side by side (for example)\n> \n>   git log --graph --pretty=oneline -- Documentation/\n>  \n> and\n> \n>   gitk -- Documentation/\n> \n> in the Git repository. gitk draws lines between commits even when they\n> are not in direct parent-child relationship (i.e. there is longer series\n> of commits between them). With log --graph it's hard to tell which\n> development line some commits come from.\n\nInteresting, I wasn't aware of this gitk behavior.  I took a look at\nthe gitk code, and they're able to do this by passing the \"--parents\"\noption to \"git log\".  This causes git to rewrite the parent\ninformation so that it lists the most recent ancestor that is in the\nresulting commit set, instead of the actual parent.\n\nIt was pretty easy to change \"git log --graph\" to do the same; I just\nsent out a new patch for it.\n\nThanks for all the testing!\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73812","messageId":"7vod8mqdlw.fsf@gitster.siamese.dyndns.org","threadId":"13005","inReplyTo":"1207555281-9362-2-git-send-email-adam@adamsimpkins.net","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-04-07T08:21:47Z","receivedAt":"2008-04-07T08:21:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Simpkins <adam@adamsimpkins.net> writes:\n\n> The old code took care of these cases for CMIT_FMT_ONELINE, but not for\n> CMIT_FMT_USERFORMAT.  For CMIT_FMT_USERFORMAT, show_log() left each\n> entry without a terminating newline.  The next call to show_log() would\n> then try to print an extra blank line between entries.\n\nThe log family traditionally defined LF (or NUL when -z is in effect) as\nseparator between each pair of entries, not as terminator after each\nentry.\n\nThis was because it would make much more sense to use separator semantics\nwhen \"git log -2\" and \"git log -1\" is asked for.  The former gives the\ntip, separator (typically an extra LF), and the second, while the latter\njust shows the tip.  Neither case give extra LF after the last entry.\nThis worked only because each entry were supposed to end with its own LF,\nso separator literally \"separated\" each entry with an extra blank line.\n\nONELINE however is special cased, because it makes no sense to give an\nextran blank line to separate each entry.  Compactness is the point of the\nformat.\n\nSwitching separator semantics to terminator semantics in USERFORMAT\nunconditionally is a bit problematic, because the userformat can be used\nto format multi-line entries and in that case you may want the usual\n\"extra blank to separate\" semantics, but it often is used to define an\nalternate oneline format, in which case you do want terminator semantics\ninstead.\n\nI suspect that --pretty=format: (i.e. userformat) should have a way to\nexplicitly tell which is wanted.  Perhaps we can keep the separator\nsemantics not to break existing users, and introduce a dummy expand item\n(say, '%_') and when it appears in the pattern it would ask for the\nterminator semantics instead?\n\nIn any case, I'm happy to see that somebody started looking into this, as\nthis \"separator vs terminator\" issue in userformat has been nagging me for\nquite a while.  It might be good idea to have the change independently\nfrom the graph extension first and then build the graph stuff on top of\nthe solidified base.  I dunno...\n"},{"id":"73813","messageId":"20080407083412.GA9764@adamsimpkins.net","threadId":"13005","inReplyTo":"200804070824.10764.tlikonen@iki.fi","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-07T08:34:12Z","receivedAt":"2008-04-07T08:34:12Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Mon, Apr 07, 2008 at 08:24:10AM +0300, Teemu Likonen wrote:\n> Adam Simpkins kirjoitti:\n> \n> > Actually, going back and testing this, it looks like I have a bug\n> > when handling --graph together with --pretty=format.  There's a\n> > missing newline after the user's format message and the next graph\n> > line.  I'll try to fix this and submit a patch later this evening.\n> \n> Also, the output is not indented for options that display some \n> additional information to commit message. Those include:\n> \n> --raw\n> --stat\n> --numstat\n> --shortstat\n> --summary\n> --name-only\n> --name-status\n> \n> I'm not sure if the diff output of -p, -u etc. should be \n> indented--probably not--but for different stat and summary options it \n> would be nice to not have their output displayed over the graph area. \n> Especially --name-status is funny since it displays \"M\" to column 1 to \n> indicate modified file while \"M\" also means merge commit in the graph.\n\nHmm.  This is a harder problem to fix.  All of the options you list\nabove are handled by the internal diff API.  The diff API doesn't have\nany knowledge about log and rev-list options, such as --graph.\n\nThe nicest way to fix this would probably be to write new diff API\nfunctions that output to a strbuf instead of printing directly to\nstdout.  Then the log code could prefix each line of the buffer with\nthe graph info before printing it.\n\nHowever, this would be a lot of work, and I'm not sure that it's\nreally worth the effort at the moment.  For now, I'm leaning towards\nchanging the code to just exit with an error if --graph is used with\nany of these options.\n\nAny opinions?  Alternative suggestions?\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73814","messageId":"7vwsnaoxlz.fsf@gitster.siamese.dyndns.org","threadId":"13005","inReplyTo":"7vod8mqdlw.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-04-07T08:52:40Z","receivedAt":"2008-04-07T08:52:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I suspect that --pretty=format: (i.e. userformat) should have a way to\n> explicitly tell which is wanted.  Perhaps we can keep the separator\n> semantics not to break existing users, and introduce a dummy expand item\n> (say, '%_') and when it appears in the pattern it would ask for the\n> terminator semantics instead?\n>\n> In any case, I'm happy to see that somebody started looking into this, as\n> this \"separator vs terminator\" issue in userformat has been nagging me for\n> quite a while.  It might be good idea to have the change independently\n> from the graph extension first and then build the graph stuff on top of\n> the solidified base.  I dunno...\n\nSome alternatives to specify terminator semantics I considered are:\n\n (1) Presence of %_ in \"--pretty=format:...\" triggers terminator\n     semantics and %_ itself interpolates an empty string; otherwise\n     separator semantics is used.\n\n (2) Presence of %n in \"--pretty=format:...\" means a multi-line output and\n     uses separator as before; lack of %n means it is a one-line format\n     and uses terminator.\n\n (3) A new option --pretty=tformat:... (i.e. tformat instead of format)\n     means LF (or NUL) is used as terminator instead of separator;\n\n (4) A new syntax --pretty=format/... (i.e. slash instead of the usual\n     colon) means LF (or NUL) is used as terminator instead of separator;\n\nThe first one is what I suggested in the message, but it feels somewhat\nhacky.  I suspect that the second one would catch 99% of the cases, but it\nis DWIM and it is known that DWIM can go wrong.  I favor design along\nthe lines of (3) or (4), which I think would be much cleaner.\n\nI however do not particularly like either \"tformat\" which is a non-word,\nnor \":\" vs \"/\" whose differences do not intuitively translate to\n\"separator vs terminator\" distinction.\n"},{"id":"73815","messageId":"20080407085653.GB5413@mithlond.arda.local","threadId":"13005","inReplyTo":"20080407083412.GA9764@adamsimpkins.net","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-07T08:56:53Z","receivedAt":"2008-04-07T08:56:53Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Adam Simpkins kirjoitti (7.4.2008 klo 1.34):\n\n> The nicest way to fix this would probably be to write new diff API\n> functions that output to a strbuf instead of printing directly to\n> stdout.  Then the log code could prefix each line of the buffer with\n> the graph info before printing it.\n> \n> However, this would be a lot of work, and I'm not sure that it's\n> really worth the effort at the moment.  For now, I'm leaning towards\n> changing the code to just exit with an error if --graph is used with\n> any of these options.\n> \n> Any opinions?  Alternative suggestions?\n\nOk, then I'd suggest that it's left as is. The --graph may be useful\nwith some stat options even when the graph area is parially broken by\nsome other output.\n"},{"id":"73820","messageId":"1207571147-4873-1-git-send-email-tlikonen@iki.fi","threadId":"13005","inReplyTo":"1207517128-9776-1-git-send-email-tlikonen@iki.fi","subject":"[PATCH v2] bash: Add more command line option completions for 'git log'","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2008-04-07T12:25:47Z","receivedAt":"2008-04-07T12:25:47Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"Signed-off-by: Teemu Likonen <tlikonen@iki.fi>\n---\n\nHmm, why not add some more useful long options? There are plenty of them of\ncourse but I feel these are one of the most common.\n\n\n contrib/completion/git-completion.bash |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 4d81963..02a5fd3 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -757,6 +757,8 @@ _git_log ()\n \t\t\t--pretty= --name-status --name-only --raw\n \t\t\t--not --all\n \t\t\t--left-right --cherry-pick\n+\t\t\t--graph --stat --numstat --shortstat\n+\t\t\t--decorate --diff-filter=\n \t\t\t\"\n \t\treturn\n \t\t;;\n-- \n1.5.5.rc3.24.g129ac\n"},{"id":"73822","messageId":"20080407131728.GA20319@sigill.intra.peff.net","threadId":"13005","inReplyTo":"7vwsnaoxlz.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-04-07T13:17:29Z","receivedAt":"2008-04-07T13:17:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Apr 07, 2008 at 01:52:40AM -0700, Junio C Hamano wrote:\n\n> Some alternatives to specify terminator semantics I considered are:\n> [...]\n> The first one is what I suggested in the message, but it feels somewhat\n> hacky.  I suspect that the second one would catch 99% of the cases, but it\n> is DWIM and it is known that DWIM can go wrong.  I favor design along\n> the lines of (3) or (4), which I think would be much cleaner.\n\nHow about:\n\n  (5) There is no automagic terminator or separator for user formats. %n\n      translates to a newline. %N translates to a newline, unless this\n      is the final record, in which case it translates to the empty\n      string.\n\nSo:\n    # oneline\n    git log --pretty=format:'%h %s%n'\n    # multiline\n    git log --pretty=format:'%h%nSubject: %s%n%N'\n\nThe main drawback is that dropping the automatic separator breaks\nexisting uses. We could work around this by automatically appending '%n'\nif there is no use of \"%n\" or \"%N\", but I think that DWIM won't work for\nformats which want a non-newline record separator. So maybe a %x/%X\nstring to mean \"separator / terminator\" instead of building on newline.\nIf one doesn't exist, we append \"%x\" to the format (to give oneline\nsemantics).\n\n-Peff\n"},{"id":"73823","messageId":"m3hcedu7kd.fsf@localhost.localdomain","threadId":"13005","inReplyTo":"7vwsnaoxlz.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-04-07T13:19:25Z","receivedAt":"2008-04-07T13:19:25Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > I suspect that --pretty=format: (i.e. userformat) should have a way to\n> > explicitly tell which is wanted.  Perhaps we can keep the separator\n> > semantics not to break existing users, and introduce a dummy expand item\n> > (say, '%_') and when it appears in the pattern it would ask for the\n> > terminator semantics instead?\n> >\n> > In any case, I'm happy to see that somebody started looking into this, as\n> > this \"separator vs terminator\" issue in userformat has been nagging me for\n> > quite a while.  It might be good idea to have the change independently\n> > from the graph extension first and then build the graph stuff on top of\n> > the solidified base.  I dunno...\n> \n> Some alternatives to specify terminator semantics I considered are:\n> \n>  (1) Presence of %_ in \"--pretty=format:...\" triggers terminator\n>      semantics and %_ itself interpolates an empty string; otherwise\n>      separator semantics is used.\n\nOr %_ might interpolate to _single_ separator, swallowing all\nseparators that follows it (something like collapsing whitespace).\nEither that, or %_ interpolate to separator value, and %*_ collapses\nseparators (terminators).\n \nBit less hacky, bit more geeky.\n\n>  (2) Presence of %n in \"--pretty=format:...\" means a multi-line output and\n>      uses separator as before; lack of %n means it is a one-line format\n>      and uses terminator.\n\nI guess that literal newline in format would also mean multi-line\noutput.  Also '%b' (body) should mean multi-line output.\n\n\nBTW. rpm uses [% ... ] to iterate over a set of (parallel) arrays\nin --queryformat, which is a bit similar to --pretty=format:<fmt>,\ne.g. 'rpm -q --queryformat \"[%-50{FILENAMES} %10{FILESIZES}\\n]'\n\nBTW2. git-for-each-ref uses _different_ kind of format, %(<name>) and\nnot %<char>.\n\n>  (3) A new option --pretty=tformat:... (i.e. tformat instead of format)\n>      means LF (or NUL) is used as terminator instead of separator;\n> \n>  (4) A new syntax --pretty=format/... (i.e. slash instead of the usual\n>      colon) means LF (or NUL) is used as terminator instead of separator;\n> \n> The first one is what I suggested in the message, but it feels somewhat\n> hacky.  I suspect that the second one would catch 99% of the cases, but it\n> is DWIM and it is known that DWIM can go wrong.  I favor design along\n> the lines of (3) or (4), which I think would be much cleaner.\n> \n> I however do not particularly like either \"tformat\" which is a non-word,\n> nor \":\" vs \"/\" whose differences do not intuitively translate to\n> \"separator vs terminator\" distinction.\n\n\"|\" instead of \":\" wouldn't be a good idea?\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"73826","messageId":"alpine.LFD.1.00.0804070905410.11277@woody.linux-foundation.org","threadId":"13005","inReplyTo":"alpine.DEB.1.00.0804062315090.12583@eeepc-johanness","subject":"Re: [PATCH 1/4] Add history graph API","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-04-07T16:15:27Z","receivedAt":"2008-04-07T16:15:27Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 6 Apr 2008, Johannes Schindelin wrote:\n> \n> AFAICT you do not even need them then.  Using \"struct strbuf *\" without \n> ever declaring struct strbuf before that is perfectly valid.\n\nIn traditional C, and inside structure declarations etc, yes.\n\nIn modern C, in other contexts, no.\n\nModern C considers a function declaration to be its own scope (it's the \nscope of the function definition, which in a declaration is obviously \njust the declaration). So if you use a \"struct xyzzy *\" in a function \ndeclaration, it will be a *different* \"struct xyzzy *\" from one declared \nlater.\n\nTry to compile something like this:\n\n\tint fn(struct xyzzy *);\n\tint fn(struct xyzzy *);\n\nwith a modern C compiler, and it will actually say something along the \nlines of \"conflicting types for ‘fn’\", because while the two declarations \nlook identical, they actually have two different (private) declarations of \n\"struct xyzzy\" going on.\n\nBut to make it even more interesting, you don't actually need a full \ndeclaration of \"struct xyzzy\" to make the compiler happy, you only need an \nimplicit one ahead of time. You can do that with the incomplete \ndeclaration, of course (like the --graph patch did), ie just a simple\n\n\tstruct xyzzy;\n\nbefore those declarations is sufficient, but so is the implicit \ndeclaration of just using the pointer to it in some non-private scope, ie \nit's equally valid to do\n\n\tstruct foobar {\n\t\tstruct xyzzy *ptr;\n\t};\n\nand this will already be enough to declare \"struct xyzzy\" in scope for the \nfunction declarations afterwards.\n\nIs this illogical? Somewhat. Why is it a private scope in a function \ndeclaration but not in a struct declaration? Why isn't the function scope \nlimited to the stuff *inside* the function? Somebody probably knows, but \nfor the rest of us the answer is just \"that's how it is, deal with it\".\n\n\t\t\tLinus\n"},{"id":"73830","messageId":"7vhcedpnma.fsf@gitster.siamese.dyndns.org","threadId":"13005","inReplyTo":"20080407131728.GA20319@sigill.intra.peff.net","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-04-07T17:43:09Z","receivedAt":"2008-04-07T17:43:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> The main drawback is that dropping the automatic separator breaks\n> existing uses. We could work around this by automatically appending '%n'\n> if there is no use of \"%n\" or \"%N\",...\n\nYeah, I agree that sounds quite bad, and that is why I favor more explicit\nand independent way to choose between separator and terminator (like the\n\"tformat\" thing).\n"},{"id":"73837","messageId":"20080407190140.GA21025@adamsimpkins.net","threadId":"13005","inReplyTo":"7vhcedpnma.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Adam Simpkins","fromEmail":"adam@adamsimpkins.net","sentAt":"2008-04-07T19:01:41Z","receivedAt":"2008-04-07T19:01:41Z","isPatch":true,"sender":{"key":"adam@adamsimpkins.net","avatar":"https://gravatar.com/avatar/d3fd2c0b3e2d2136b56e95726ee03227bee4eb562f627dcd3ebca0623fa05054?d=mp&s=160"},"body":"On Mon, Apr 07, 2008 at 10:43:09AM -0700, Junio C Hamano wrote:\n> Jeff King <peff@peff.net> writes:\n> \n> > The main drawback is that dropping the automatic separator breaks\n> > existing uses. We could work around this by automatically appending '%n'\n> > if there is no use of \"%n\" or \"%N\",...\n> \n> Yeah, I agree that sounds quite bad, and that is why I favor more explicit\n> and independent way to choose between separator and terminator (like the\n> \"tformat\" thing).\n\nHow about an extra option to explicitly toggle the separator on or\noff?  For example, how about \"--sep=<count>\"?  Using --sep=0 would put\nno separator between entries.  --sep=N would put N separators between\nentries.\n\nIf this option isn't explicitly specified, the default should be\n--sep=0 for ONELINE, and --sep=1 for everything else.  Using --sep=2\nwith USERFORMAT would achieve the behavior of getting an extra line of\npadding between entries.\n\n\nThis still leaves unresolved the fact that the USERFORMAT output ends\nwith a missing newline in most cases.  I can see the argument for\ndoing this when using -z, which is primarily aimed for programmatic\nconsumption.  However, without -z, I don't think this is the behavior\nmost users expect.  For example, try running\n\"git --no-pager log -1 --pretty=format:%H\".\n\nIf we were starting from scratch, and didn't have to worry about\nbreaking existing behavior, I would say that USERFORMAT should always\nterminate entries in a newline, and then default to --sep=0.  As it\nis, it might be easiest just to leave the current behavior, without a\nterminating newline.\n\n-- \nAdam Simpkins\nadam@adamsimpkins.net\n"},{"id":"73838","messageId":"7vmyo5nr2h.fsf@gitster.siamese.dyndns.org","threadId":"13005","inReplyTo":"7vwsnaoxlz.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-04-08T00:11:34Z","receivedAt":"2008-04-08T00:11:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>  (2) Presence of %n in \"--pretty=format:...\" means a multi-line output and\n>      uses separator as before; lack of %n means it is a one-line format\n>      and uses terminator.\n\nAfter thinking about this a bit more, I think a slight variant of the\nabove probably is the least intrusive both from code and semantics point\nof view, and would match end-user expectations pretty well.\n\nThis attached patch introduces a single bit \"use_terminator\" in \"struct\nrev_info\", which is normally false (i.e. most formats use separator\nsemantics) but by flipping it to true, you can ask for terminator\nsemantics just like oneline format does.\n\nThe function get_commit_format(), which is what parses \"--pretty=\" option,\nnow takes a pointer to \"struct rev_info\" and updates its commit_format and\nuse_terminator fields.  It used to return the value of type \"enum\ncmit_fmt\", but all the callers assigned it to rev->commit_format.\n\nThere are only two cases the code turns use_terminator on.  Obviously, the\ntraditional oneline format (--pretty=oneline) is one of them, and the new\ncase is --pretty=format:... that does not end with \"%n\".\n\nWhen the custom format \"--pretty=format:\" is:\n\n\t\"A: %an <%ae>%nC: %cn <%ce>%n\"\n\nit represents a record with two lines (author and committer), and these\nare output with an extra LF in between, just like the regular --pretty\nwithout customization will get an extra separator.  The custom format\n\n\t\"A: %an <%ae> C: %cn <%ce>\"\n\nwill give a one-line-per-commit output, each of which is terminated with\nLF.  And\n\n\t\"A: %an <%ae>%nC: %cn <%ce>\"\n\nwill give two line per commit without extra separator.\n\n---\n\n builtin-commit.c |    2 +-\n builtin-log.c    |    3 ++-\n commit.h         |    3 ++-\n log-tree.c       |    4 ++--\n pretty.c         |   25 ++++++++++++++++++-------\n revision.c       |    2 +-\n revision.h       |    3 ++-\n 7 files changed, 28 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 660a345..8bf3503 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -810,7 +810,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)\n \n \trev.verbose_header = 1;\n \trev.show_root_diff = 1;\n-\trev.commit_format = get_commit_format(\"format:%h: %s\");\n+\tget_commit_format(\"format:%h: %s\", &rev);\n \trev.always_show_header = 0;\n \trev.diffopt.detect_rename = 1;\n \trev.diffopt.rename_limit = 100;\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 5c00725..1670d0b 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -56,7 +56,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \trev->abbrev = DEFAULT_ABBREV;\n \trev->commit_format = CMIT_FMT_DEFAULT;\n \tif (fmt_pretty)\n-\t\trev->commit_format = get_commit_format(fmt_pretty);\n+\t\tget_commit_format(fmt_pretty, rev);\n \trev->verbose_header = 1;\n \tDIFF_OPT_SET(&rev->diffopt, RECURSIVE);\n \trev->show_root_diff = default_show_root;\n@@ -400,6 +400,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)\n \t * allow us to set a different default.\n \t */\n \trev.commit_format = CMIT_FMT_ONELINE;\n+\trev.use_terminator = 1;\n \trev.always_show_header = 1;\n \n \t/*\ndiff --git a/commit.h b/commit.h\nindex 2f63bc8..2d94d41 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -63,7 +63,8 @@ enum cmit_fmt {\n };\n \n extern int non_ascii(int);\n-extern enum cmit_fmt get_commit_format(const char *arg);\n+struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */\n+extern void get_commit_format(const char *arg, struct rev_info *);\n extern void format_commit_message(const struct commit *commit,\n                                   const void *format, struct strbuf *sb);\n extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,\ndiff --git a/log-tree.c b/log-tree.c\nindex 5b29639..d246147 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -249,9 +249,9 @@ void show_log(struct rev_info *opt, const char *sep)\n \t *    not have an empty line between entries.\n \t */\n \textra = \"\";\n-\tif (*sep != '\\n' && opt->commit_format == CMIT_FMT_ONELINE)\n+\tif (*sep != '\\n' && opt->use_terminator)\n \t\textra = \"\\n\";\n-\tif (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)\n+\tif (opt->shown_one && !opt->use_terminator)\n \t\tputchar(opt->diffopt.line_termination);\n \topt->shown_one = 1;\n \ndiff --git a/pretty.c b/pretty.c\nindex 16bfb86..365e387 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -21,23 +21,34 @@ static struct cmt_fmt_map {\n \n static char *user_format;\n \n-enum cmit_fmt get_commit_format(const char *arg)\n+void get_commit_format(const char *arg, struct rev_info *rev)\n {\n \tint i;\n \n-\tif (!arg || !*arg)\n-\t\treturn CMIT_FMT_DEFAULT;\n+\trev->use_terminator = 0;\n+\tif (!arg || !*arg) {\n+\t\trev->commit_format = CMIT_FMT_DEFAULT;\n+\t\treturn;\n+\t}\n \tif (*arg == '=')\n \t\targ++;\n \tif (!prefixcmp(arg, \"format:\")) {\n+\t\tsize_t len = strlen(arg + 7);\n \t\tfree(user_format);\n \t\tuser_format = xstrdup(arg + 7);\n-\t\treturn CMIT_FMT_USERFORMAT;\n+\t\tif (len < 2 || strcmp(user_format + len - 2, \"%n\"))\n+\t\t\trev->use_terminator = 1;\n+\t\trev->commit_format = CMIT_FMT_USERFORMAT;\n+\t\treturn;\n \t}\n-\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n+\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts) - 1; i++) {\n \t\tif (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&\n-\t\t    !strncmp(arg, cmt_fmts[i].n, strlen(arg)))\n-\t\t\treturn cmt_fmts[i].v;\n+\t\t    !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {\n+\t\t\tif (cmt_fmts[i].v == CMIT_FMT_ONELINE)\n+\t\t\t\trev->use_terminator = 0;\n+\t\t\trev->commit_format = cmt_fmts[i].v;\n+\t\t\treturn;\n+\t\t}\n \t}\n \n \tdie(\"invalid --pretty format: %s\", arg);\ndiff --git a/revision.c b/revision.c\nindex 196fedc..781c503 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1198,7 +1198,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--pretty\")) {\n \t\t\t\trevs->verbose_header = 1;\n-\t\t\t\trevs->commit_format = get_commit_format(arg+8);\n+\t\t\t\tget_commit_format(arg+8, revs);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--root\")) {\ndiff --git a/revision.h b/revision.h\nindex c8b3b94..31217f8 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -64,7 +64,8 @@ struct rev_info {\n \n \t/* Format info */\n \tunsigned int\tshown_one:1,\n-\t\t\tabbrev_commit:1;\n+\t\t\tabbrev_commit:1,\n+\t\t\tuse_terminator:1;\n \tenum date_mode date_mode;\n \n \tconst char **ignore_packed; /* pretend objects in these are unpacked */\n"},{"id":"73839","messageId":"5d46db230804071725r44694e76l3d6e63aabc67f372@mail.gmail.com","threadId":"13005","inReplyTo":"7vmyo5nr2h.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Govind Salinas","fromEmail":"blix@sophiasuchtig.com","sentAt":"2008-04-08T00:25:28Z","receivedAt":"2008-04-08T00:25:28Z","isPatch":true,"sender":{"key":"blix@sophiasuchtig.com","avatar":null},"body":"On Mon, Apr 7, 2008 at 7:11 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>  >  (2) Presence of %n in \"--pretty=format:...\" means a multi-line output and\n>  >      uses separator as before; lack of %n means it is a one-line format\n>  >      and uses terminator.\n>\n>  After thinking about this a bit more, I think a slight variant of the\n>  above probably is the least intrusive both from code and semantics point\n>  of view, and would match end-user expectations pretty well.\n>\n>  This attached patch introduces a single bit \"use_terminator\" in \"struct\n>  rev_info\", which is normally false (i.e. most formats use separator\n>  semantics) but by flipping it to true, you can ask for terminator\n>  semantics just like oneline format does.\n>\n>  The function get_commit_format(), which is what parses \"--pretty=\" option,\n>  now takes a pointer to \"struct rev_info\" and updates its commit_format and\n>  use_terminator fields.  It used to return the value of type \"enum\n>  cmit_fmt\", but all the callers assigned it to rev->commit_format.\n>\n>  There are only two cases the code turns use_terminator on.  Obviously, the\n>  traditional oneline format (--pretty=oneline) is one of them, and the new\n>  case is --pretty=format:... that does not end with \"%n\".\n>\n>  When the custom format \"--pretty=format:\" is:\n>\n>         \"A: %an <%ae>%nC: %cn <%ce>%n\"\n>\n>  it represents a record with two lines (author and committer), and these\n>  are output with an extra LF in between, just like the regular --pretty\n>  without customization will get an extra separator.  The custom format\n>\n>         \"A: %an <%ae> C: %cn <%ce>\"\n>\n>  will give a one-line-per-commit output, each of which is terminated with\n>  LF.  And\n>\n>         \"A: %an <%ae>%nC: %cn <%ce>\"\n>\n>  will give two line per commit without extra separator.\n>\n\nHi,\n\nShouldn't whatever option is used here respect line_terminator such\nthat it will use NULL bytes when -z is used?  Having watched this\ndiscussion, it seems like you really want an option --whatever that\nforces a record terminator between records and, I assume, at the end.\nI have not seen anyone suggest it, perhaps I missed it.\n\n-Govind\n"},{"id":"73840","messageId":"7vfxtxnovw.fsf@gitster.siamese.dyndns.org","threadId":"13005","inReplyTo":"5d46db230804071725r44694e76l3d6e63aabc67f372@mail.gmail.com","subject":"Re: [PATCH 2/4] log and rev-list: Fixed newline termination issues with --graph","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-04-08T00:58:43Z","receivedAt":"2008-04-08T00:58:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Govind Salinas\" <blix@sophiasuchtig.com> writes:\n\n> Shouldn't whatever option is used here respect line_terminator such\n> that it will use NULL bytes when -z is used?\n\nYes but it is orthogonal to what is being discussed.  What the patch shows\nis how to allow --pretty=format: to conditionally do what --pretty=oneline\nalready does.  If the current --pretty=oneline forces \"\\n\" under -z (I\nhaven't checked if that is the case) that might be something we may want\nto change.  Once you do so, you would get the same fix for --pretty=format\nfor free ;-)\n\nAlso when you talk about line_terminator, you need to be careful to\nrealize that it is _not_ really a \"line_terminator\".  For example, we do\nnot substitute 's/\\n/\\0/' the EOLN in multi-line log messages.  Neither do\nwe use NUL terminated lines in diff nor stat output when -p and --stat are\nin effect.  They are more like \"separator between parts of output\".\n"}]}