log and rev-list: add --graph option
This new option causes a text-based representation of the history to be printed to the left of the normal output. Signed-off-by: Adam Simpkins <adam@adamsimpkins.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Adam Simpkins committed
May 4, 2008 at 03:36 UTC
7fefda5cc7a5faf7962092367bedb321a634d54d
6 files changed
+161
-16
Documentation/rev-list-options.txt
+10
@@ -75,6 +75,16 @@ you would get an output line this:
75
-xxxxxxx... 1st on a
76
-----------------------------------------------------------------------
77
78
+--graph::
79
+
80
+ Draw a text-based graphical representation of the commit history
81
+ on the left hand side of the output. This may cause extra lines
82
+ to be printed in between commits, in order for the graph history
83
+ to be drawn properly.
84
++
85
+This implies the '--topo-order' option by default, but the
86
+'--date-order' option may also be specified.
87
+
88
Diff Formatting
89
~~~~~~~~~~~~~~~
90
Documentation/technical/api-history-graph.txt
+8
-5
@@ -74,14 +74,17 @@ state.
74
Calling sequence
75
----------------
76
77
-* Create a `struct git_graph` by calling `graph_init()`.
77
+* Create a `struct git_graph` by calling `graph_init()`. When using the
78
+ revision walking API, this is done automatically by `setup_revisions()` if
79
+ the '--graph' option is supplied.
80
81
* Use the revision walking API to walk through a group of contiguous commits.
82
+ The `get_revision()` function automatically calls `graph_update()` each time
83
+ it is invoked.
84
81
-* For each commit traversed, call `graph_update()` to move the graph to the
82
- next commit. Once `graph_update()` has been called, call `graph_next_line()`
83
- repeatedly, until `graph_is_commit_finished()` returns non-zero. Each call
84
- to `graph_next_line()` will output a single line of the graph. The resulting
85
+* For each commit, call `graph_next_line()` repeatedly, until
86
+ `graph_is_commit_finished()` returns non-zero. Each call go
87
+ `graph_next_line()` will output a single line of the graph. The resulting
88
lines will not contain any newlines. `graph_next_line()` returns 1 if the
89
resulting line contains the current commit, or 0 if this is merely a line
90
needed to adjust the graph before or after the current commit. This return
builtin-rev-list.c
+44
-2
@@ -10,6 +10,7 @@
10
#include "list-objects.h"
11
#include "builtin.h"
12
#include "log-tree.h"
13
+#include "graph.h"
14
15
/* bits #0-15 in revision.h */
16
@@ -58,6 +59,8 @@ static const char *header_prefix;
59
static void finish_commit(struct commit *commit);
60
static void show_commit(struct commit *commit)
61
{
62
+ graph_show_commit(revs.graph);
63
+
64
if (show_timestamp)
65
printf("%lu ", commit->date);
66
if (header_prefix)
@@ -96,9 +99,48 @@ static void show_commit(struct commit *commit)
99
pretty_print_commit(revs.commit_format, commit,
100
&buf, revs.abbrev, NULL, NULL,
101
revs.date_mode, 0);
99
- if (buf.len)
100
- printf("%s%c", buf.buf, hdr_termination);
102
+ if (revs.graph) {
103
+ if (buf.len) {
104
+ if (revs.commit_format != CMIT_FMT_ONELINE)
105
+ graph_show_oneline(revs.graph);
106
+
107
+ graph_show_commit_msg(revs.graph, &buf);
108
+
109
+ /*
110
+ * Add a newline after the commit message.
111
+ *
112
+ * Usually, this newline produces a blank
113
+ * padding line between entries, in which case
114
+ * we need to add graph padding on this line.
115
+ *
116
+ * However, the commit message may not end in a
117
+ * newline. In this case the newline simply
118
+ * ends the last line of the commit message,
119
+ * and we don't need any graph output. (This
120
+ * always happens with CMIT_FMT_ONELINE, and it
121
+ * happens with CMIT_FMT_USERFORMAT when the
122
+ * format doesn't explicitly end in a newline.)
123
+ */
124
+ if (buf.len && buf.buf[buf.len - 1] == '\n')
125
+ graph_show_padding(revs.graph);
126
+ putchar('\n');
127
+ } else {
128
+ /*
129
+ * If the message buffer is empty, just show
130
+ * the rest of the graph output for this
131
+ * commit.
132
+ */
133
+ if (graph_show_remainder(revs.graph))
134
+ putchar('\n');
135
+ }
136
+ } else {
137
+ if (buf.len)
138
+ printf("%s%c", buf.buf, hdr_termination);
139
+ }
140
strbuf_release(&buf);
141
+ } else {
142
+ if (graph_show_remainder(revs.graph))
143
+ putchar('\n');
144
}
145
maybe_flush_or_die(stdout, "stdout");
146
finish_commit(commit);
log-tree.c
+69
-7
@@ -1,6 +1,7 @@
1
#include "cache.h"
2
#include "diff.h"
3
#include "commit.h"
4
+#include "graph.h"
5
#include "log-tree.h"
6
#include "reflog-walk.h"
7
@@ -165,11 +166,16 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
166
}
167
168
printf("From %s Mon Sep 17 00:00:00 2001\n", name);
168
- if (opt->message_id)
169
+ graph_show_oneline(opt->graph);
170
+ if (opt->message_id) {
171
printf("Message-Id: <%s>\n", opt->message_id);
170
- if (opt->ref_message_id)
172
+ graph_show_oneline(opt->graph);
173
+ }
174
+ if (opt->ref_message_id) {
175
printf("In-Reply-To: <%s>\nReferences: <%s>\n",
176
opt->ref_message_id, opt->ref_message_id);
177
+ graph_show_oneline(opt->graph);
178
+ }
179
if (opt->mime_boundary) {
180
static char subject_buffer[1024];
181
static char buffer[1024];
@@ -220,6 +226,8 @@ void show_log(struct rev_info *opt)
226
227
opt->loginfo = NULL;
228
if (!opt->verbose_header) {
229
+ graph_show_commit(opt->graph);
230
+
231
if (commit->object.flags & BOUNDARY)
232
putchar('-');
233
else if (commit->object.flags & UNINTERESTING)
@@ -234,6 +242,10 @@ void show_log(struct rev_info *opt)
242
if (opt->print_parents)
243
show_parents(commit, abbrev_commit);
244
show_decorations(commit);
245
+ if (opt->graph && !graph_is_commit_finished(opt->graph)) {
246
+ putchar('\n');
247
+ graph_show_remainder(opt->graph);
248
+ }
249
putchar(opt->diffopt.line_termination);
250
return;
251
}
@@ -243,10 +255,32 @@ void show_log(struct rev_info *opt)
255
* Otherwise, add a diffopt.line_termination character before all
256
* entries but the first. (IOW, as a separator between entries)
257
*/
246
- if (opt->shown_one && !opt->use_terminator)
258
+ if (opt->shown_one && !opt->use_terminator) {
259
+ /*
260
+ * If entries are separated by a newline, the output
261
+ * should look human-readable. If the last entry ended
262
+ * with a newline, print the graph output before this
263
+ * newline. Otherwise it will end up as a completely blank
264
+ * line and will look like a gap in the graph.
265
+ *
266
+ * If the entry separator is not a newline, the output is
267
+ * primarily intended for programmatic consumption, and we
268
+ * never want the extra graph output before the entry
269
+ * separator.
270
+ */
271
+ if (opt->diffopt.line_termination == '\n' &&
272
+ !opt->missing_newline)
273
+ graph_show_padding(opt->graph);
274
putchar(opt->diffopt.line_termination);
275
+ }
276
opt->shown_one = 1;
277
278
+ /*
279
+ * If the history graph was requested,
280
+ * print the graph, up to this commit's line
281
+ */
282
+ graph_show_commit(opt->graph);
283
+
284
/*
285
* Print header line of header..
286
*/
@@ -279,8 +313,19 @@ void show_log(struct rev_info *opt)
313
abbrev_commit));
314
show_decorations(commit);
315
printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
282
- putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
316
+ if (opt->commit_format == CMIT_FMT_ONELINE) {
317
+ putchar(' ');
318
+ } else {
319
+ putchar('\n');
320
+ graph_show_oneline(opt->graph);
321
+ }
322
if (opt->reflog_info) {
323
+ /*
324
+ * setup_revisions() ensures that opt->reflog_info
325
+ * and opt->graph cannot both be set,
326
+ * so we don't need to worry about printing the
327
+ * graph info here.
328
+ */
329
show_reflog_message(opt->reflog_info,
330
opt->commit_format == CMIT_FMT_ONELINE,
331
opt->date_mode);
@@ -304,13 +349,30 @@ void show_log(struct rev_info *opt)
349
350
if (opt->add_signoff)
351
append_signoff(&msgbuf, opt->add_signoff);
307
- if (opt->show_log_size)
352
+ if (opt->show_log_size) {
353
printf("log size %i\n", (int)msgbuf.len);
354
+ graph_show_oneline(opt->graph);
355
+ }
356
310
- if (msgbuf.len)
357
+ /*
358
+ * Set opt->missing_newline if msgbuf doesn't
359
+ * end in a newline (including if it is empty)
360
+ */
361
+ if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n')
362
+ opt->missing_newline = 1;
363
+ else
364
+ opt->missing_newline = 0;
365
+
366
+ if (opt->graph)
367
+ graph_show_commit_msg(opt->graph, &msgbuf);
368
+ else
369
fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
312
- if (opt->use_terminator)
370
+ if (opt->use_terminator) {
371
+ if (!opt->missing_newline)
372
+ graph_show_padding(opt->graph);
373
putchar('\n');
374
+ }
375
+
376
strbuf_release(&msgbuf);
377
}
378
revision.c
+25
-1
@@ -6,6 +6,7 @@
6
#include "diff.h"
7
#include "refs.h"
8
#include "revision.h"
9
+#include "graph.h"
10
#include "grep.h"
11
#include "reflog-walk.h"
12
#include "patch-ids.h"
@@ -1203,6 +1204,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
1204
get_commit_format(arg+8, revs);
1205
continue;
1206
}
1207
+ if (!prefixcmp(arg, "--graph")) {
1208
+ revs->topo_order = 1;
1209
+ revs->rewrite_parents = 1;
1210
+ revs->graph = graph_init();
1211
+ continue;
1212
+ }
1213
if (!strcmp(arg, "--root")) {
1214
revs->show_root_diff = 1;
1215
continue;
@@ -1397,6 +1404,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
1404
if (revs->reverse && revs->reflog_info)
1405
die("cannot combine --reverse with --walk-reflogs");
1406
1407
+ /*
1408
+ * Limitations on the graph functionality
1409
+ */
1410
+ if (revs->reverse && revs->graph)
1411
+ die("cannot combine --reverse with --graph");
1412
+
1413
+ if (revs->reflog_info && revs->graph)
1414
+ die("cannot combine --walk-reflogs with --graph");
1415
+
1416
return left;
1417
}
1418
@@ -1598,7 +1614,7 @@ static void gc_boundary(struct object_array *array)
1614
}
1615
}
1616
1601
-struct commit *get_revision(struct rev_info *revs)
1617
+static struct commit *get_revision_internal(struct rev_info *revs)
1618
{
1619
struct commit *c = NULL;
1620
struct commit_list *l;
@@ -1705,3 +1721,11 @@ struct commit *get_revision(struct rev_info *revs)
1721
1722
return c;
1723
}
1724
+
1725
+struct commit *get_revision(struct rev_info *revs)
1726
+{
1727
+ struct commit *c = get_revision_internal(revs);
1728
+ if (c && revs->graph)
1729
+ graph_update(revs->graph, c);
1730
+ return c;
1731
+}
revision.h
+5
-1
@@ -66,7 +66,8 @@ struct rev_info {
66
/* Format info */
67
unsigned int shown_one:1,
68
abbrev_commit:1,
69
- use_terminator:1;
69
+ use_terminator:1,
70
+ missing_newline:1;
71
enum date_mode date_mode;
72
73
const char **ignore_packed; /* pretend objects in these are unpacked */
@@ -89,6 +90,9 @@ struct rev_info {
90
/* Filter by commit log message */
91
struct grep_opt *grep_filter;
92
93
+ /* Display history graph */
94
+ struct git_graph *graph;
95
+
96
/* special limits */
97
int skip_count;
98
int max_count;