-xxxxxxx... 1st on a
-----------------------------------------------------------------------
+--graph::
+
+ Draw a text-based graphical representation of the commit history
+ on the left hand side of the output. This may cause extra lines
+ to be printed in between commits, in order for the graph history
+ to be drawn properly.
++
+This implies the '--topo-order' option by default, but the
+'--date-order' option may also be specified.
+
Diff Formatting
~~~~~~~~~~~~~~~
Calling sequence
----------------
-* Create a `struct git_graph` by calling `graph_init()`.
+* Create a `struct git_graph` by calling `graph_init()`. When using the
+ revision walking API, this is done automatically by `setup_revisions()` if
+ the '--graph' option is supplied.
* Use the revision walking API to walk through a group of contiguous commits.
+ The `get_revision()` function automatically calls `graph_update()` each time
+ it is invoked.
-* For each commit traversed, call `graph_update()` to move the graph to the
- next commit. Once `graph_update()` has been called, call `graph_next_line()`
- repeatedly, until `graph_is_commit_finished()` returns non-zero. Each call
- to `graph_next_line()` will output a single line of the graph. The resulting
+* For each commit, call `graph_next_line()` repeatedly, until
+ `graph_is_commit_finished()` returns non-zero. Each call go
+ `graph_next_line()` will output a single line of the graph. The resulting
lines will not contain any newlines. `graph_next_line()` returns 1 if the
resulting line contains the current commit, or 0 if this is merely a line
needed to adjust the graph before or after the current commit. This return
#include "list-objects.h"
#include "builtin.h"
#include "log-tree.h"
+#include "graph.h"
/* bits #0-15 in revision.h */
static void finish_commit(struct commit *commit);
static void show_commit(struct commit *commit)
{
+ graph_show_commit(revs.graph);
+
if (show_timestamp)
printf("%lu ", commit->date);
if (header_prefix)
pretty_print_commit(revs.commit_format, commit,
&buf, revs.abbrev, NULL, NULL,
revs.date_mode, 0);
- if (buf.len)
- printf("%s%c", buf.buf, hdr_termination);
+ if (revs.graph) {
+ if (buf.len) {
+ if (revs.commit_format != CMIT_FMT_ONELINE)
+ graph_show_oneline(revs.graph);
+
+ graph_show_commit_msg(revs.graph, &buf);
+
+ /*
+ * Add a newline after the commit message.
+ *
+ * Usually, this newline produces a blank
+ * padding line between entries, in which case
+ * we need to add graph padding on this line.
+ *
+ * However, the commit message may not end in a
+ * newline. In this case the newline simply
+ * ends the last line of the commit message,
+ * and we don't need any graph output. (This
+ * always happens with CMIT_FMT_ONELINE, and it
+ * happens with CMIT_FMT_USERFORMAT when the
+ * format doesn't explicitly end in a newline.)
+ */
+ if (buf.len && buf.buf[buf.len - 1] == '\n')
+ graph_show_padding(revs.graph);
+ putchar('\n');
+ } else {
+ /*
+ * If the message buffer is empty, just show
+ * the rest of the graph output for this
+ * commit.
+ */
+ if (graph_show_remainder(revs.graph))
+ putchar('\n');
+ }
+ } else {
+ if (buf.len)
+ printf("%s%c", buf.buf, hdr_termination);
+ }
strbuf_release(&buf);
+ } else {
+ if (graph_show_remainder(revs.graph))
+ putchar('\n');
}
maybe_flush_or_die(stdout, "stdout");
finish_commit(commit);
#include "cache.h"
#include "diff.h"
#include "commit.h"
+#include "graph.h"
#include "log-tree.h"
#include "reflog-walk.h"
}
printf("From %s Mon Sep 17 00:00:00 2001\n", name);
- if (opt->message_id)
+ graph_show_oneline(opt->graph);
+ if (opt->message_id) {
printf("Message-Id: <%s>\n", opt->message_id);
- if (opt->ref_message_id)
+ graph_show_oneline(opt->graph);
+ }
+ if (opt->ref_message_id) {
printf("In-Reply-To: <%s>\nReferences: <%s>\n",
opt->ref_message_id, opt->ref_message_id);
+ graph_show_oneline(opt->graph);
+ }
if (opt->mime_boundary) {
static char subject_buffer[1024];
static char buffer[1024];
opt->loginfo = NULL;
if (!opt->verbose_header) {
+ graph_show_commit(opt->graph);
+
if (commit->object.flags & BOUNDARY)
putchar('-');
else if (commit->object.flags & UNINTERESTING)
if (opt->print_parents)
show_parents(commit, abbrev_commit);
show_decorations(commit);
+ if (opt->graph && !graph_is_commit_finished(opt->graph)) {
+ putchar('\n');
+ graph_show_remainder(opt->graph);
+ }
putchar(opt->diffopt.line_termination);
return;
}
* Otherwise, add a diffopt.line_termination character before all
* entries but the first. (IOW, as a separator between entries)
*/
- if (opt->shown_one && !opt->use_terminator)
+ if (opt->shown_one && !opt->use_terminator) {
+ /*
+ * If entries are separated by a newline, the output
+ * should look human-readable. If the last entry ended
+ * with a newline, print the graph output before this
+ * newline. Otherwise it will end up as a completely blank
+ * line and will look like a gap in the graph.
+ *
+ * If the entry separator is not a newline, the output is
+ * primarily intended for programmatic consumption, and we
+ * never want the extra graph output before the entry
+ * separator.
+ */
+ if (opt->diffopt.line_termination == '\n' &&
+ !opt->missing_newline)
+ graph_show_padding(opt->graph);
putchar(opt->diffopt.line_termination);
+ }
opt->shown_one = 1;
+ /*
+ * If the history graph was requested,
+ * print the graph, up to this commit's line
+ */
+ graph_show_commit(opt->graph);
+
/*
* Print header line of header..
*/
abbrev_commit));
show_decorations(commit);
printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
- putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+ if (opt->commit_format == CMIT_FMT_ONELINE) {
+ putchar(' ');
+ } else {
+ putchar('\n');
+ graph_show_oneline(opt->graph);
+ }
if (opt->reflog_info) {
+ /*
+ * setup_revisions() ensures that opt->reflog_info
+ * and opt->graph cannot both be set,
+ * so we don't need to worry about printing the
+ * graph info here.
+ */
show_reflog_message(opt->reflog_info,
opt->commit_format == CMIT_FMT_ONELINE,
opt->date_mode);
if (opt->add_signoff)
append_signoff(&msgbuf, opt->add_signoff);
- if (opt->show_log_size)
+ if (opt->show_log_size) {
printf("log size %i\n", (int)msgbuf.len);
+ graph_show_oneline(opt->graph);
+ }
- if (msgbuf.len)
+ /*
+ * Set opt->missing_newline if msgbuf doesn't
+ * end in a newline (including if it is empty)
+ */
+ if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n')
+ opt->missing_newline = 1;
+ else
+ opt->missing_newline = 0;
+
+ if (opt->graph)
+ graph_show_commit_msg(opt->graph, &msgbuf);
+ else
fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
- if (opt->use_terminator)
+ if (opt->use_terminator) {
+ if (!opt->missing_newline)
+ graph_show_padding(opt->graph);
putchar('\n');
+ }
+
strbuf_release(&msgbuf);
}
#include "diff.h"
#include "refs.h"
#include "revision.h"
+#include "graph.h"
#include "grep.h"
#include "reflog-walk.h"
#include "patch-ids.h"
get_commit_format(arg+8, revs);
continue;
}
+ if (!prefixcmp(arg, "--graph")) {
+ revs->topo_order = 1;
+ revs->rewrite_parents = 1;
+ revs->graph = graph_init();
+ continue;
+ }
if (!strcmp(arg, "--root")) {
revs->show_root_diff = 1;
continue;
if (revs->reverse && revs->reflog_info)
die("cannot combine --reverse with --walk-reflogs");
+ /*
+ * Limitations on the graph functionality
+ */
+ if (revs->reverse && revs->graph)
+ die("cannot combine --reverse with --graph");
+
+ if (revs->reflog_info && revs->graph)
+ die("cannot combine --walk-reflogs with --graph");
+
return left;
}
}
}
-struct commit *get_revision(struct rev_info *revs)
+static struct commit *get_revision_internal(struct rev_info *revs)
{
struct commit *c = NULL;
struct commit_list *l;
return c;
}
+
+struct commit *get_revision(struct rev_info *revs)
+{
+ struct commit *c = get_revision_internal(revs);
+ if (c && revs->graph)
+ graph_update(revs->graph, c);
+ return c;
+}
/* Format info */
unsigned int shown_one:1,
abbrev_commit:1,
- use_terminator:1;
+ use_terminator:1,
+ missing_newline:1;
enum date_mode date_mode;
const char **ignore_packed; /* pretend objects in these are unpacked */
/* Filter by commit log message */
struct grep_opt *grep_filter;
+ /* Display history graph */
+ struct git_graph *graph;
+
/* special limits */
int skip_count;
int max_count;