log: add --show-linear-break to help see non-linear history

Option explanation is in rev-list-options.txt. The interaction with -z is left undecided. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Mar 25, 2014 at 20:23 UTC 1b32decefd840e0ca5d18eefd6472fd8624dd849
5 files changed +66 -5
Documentation/rev-list-options.txt
+7
@@ -750,6 +750,13 @@ This enables parent rewriting, see 'History Simplification' below.
750 This implies the `--topo-order` option by default, but the
751 `--date-order` option may also be specified.
752
753 +--show-linear-break[=<barrier>]::
754 + When --graph is not used, all history branches are flattened
755 + which can make it hard to see that the two consecutive commits
756 + do not belong to a linear branch. This option puts a barrier
757 + in between them in that case. If `<barrier>` is specified, it
758 + is the string that will be shown instead of the default one.
759 +
760 ifdef::git-rev-list[]
761 --count::
762 Print a number stating how many commits would have been
log-tree.c
+4
@@ -805,12 +805,16 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
805 if (opt->line_level_traverse)
806 return line_log_print(opt, commit);
807
808 + if (opt->track_linear && !opt->linear && !opt->reverse_output_stage)
809 + printf("\n%s\n", opt->break_bar);
810 shown = log_tree_diff(opt, commit, &log);
811 if (!shown && opt->loginfo && opt->always_show_header) {
812 log.parent = NULL;
813 show_log(opt);
814 shown = 1;
815 }
816 + if (opt->track_linear && !opt->linear && opt->reverse_output_stage)
817 + printf("\n%s\n", opt->break_bar);
818 opt->loginfo = NULL;
819 maybe_flush_or_die(stdout, "stdout");
820 return shown;
object.h
+1 -1
@@ -28,7 +28,7 @@ struct object_array {
28 #define TYPE_BITS 3
29 /*
30 * object flag allocation:
31 - * revision.h: 0---------10
31 + * revision.h: 0---------10 26
32 * fetch-pack.c: 0---4
33 * walker.c: 0-2
34 * upload-pack.c: 11----------------19
revision.c
+45 -3
@@ -1832,6 +1832,14 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
1832 revs->notes_opt.use_default_notes = 1;
1833 } else if (!strcmp(arg, "--show-signature")) {
1834 revs->show_signature = 1;
1835 + } else if (!strcmp(arg, "--show-linear-break") ||
1836 + starts_with(arg, "--show-linear-break=")) {
1837 + if (starts_with(arg, "--show-linear-break="))
1838 + revs->break_bar = xstrdup(arg + 20);
1839 + else
1840 + revs->break_bar = " ..........";
1841 + revs->track_linear = 1;
1842 + revs->track_first_time = 1;
1843 } else if (starts_with(arg, "--show-notes=") ||
1844 starts_with(arg, "--notes=")) {
1845 struct strbuf buf = STRBUF_INIT;
@@ -1955,6 +1963,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
1963 unkv[(*unkc)++] = arg;
1964 return opts;
1965 }
1966 + if (revs->graph && revs->track_linear)
1967 + die("--show-linear-break and --graph are incompatible");
1968
1969 return 1;
1970 }
@@ -2897,6 +2907,27 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
2907 return action;
2908 }
2909
2910 +static void track_linear(struct rev_info *revs, struct commit *commit)
2911 +{
2912 + if (revs->track_first_time) {
2913 + revs->linear = 1;
2914 + revs->track_first_time = 0;
2915 + } else {
2916 + struct commit_list *p;
2917 + for (p = revs->previous_parents; p; p = p->next)
2918 + if (p->item == NULL || /* first commit */
2919 + !hashcmp(p->item->object.sha1, commit->object.sha1))
2920 + break;
2921 + revs->linear = p != NULL;
2922 + }
2923 + if (revs->reverse) {
2924 + if (revs->linear)
2925 + commit->object.flags |= TRACK_LINEAR;
2926 + }
2927 + free_commit_list(revs->previous_parents);
2928 + revs->previous_parents = copy_commit_list(commit->parents);
2929 +}
2930 +
2931 static struct commit *get_revision_1(struct rev_info *revs)
2932 {
2933 if (!revs->commits)
@@ -2936,6 +2967,8 @@ static struct commit *get_revision_1(struct rev_info *revs)
2967 die("Failed to simplify parents of commit %s",
2968 sha1_to_hex(commit->object.sha1));
2969 default:
2970 + if (revs->track_linear)
2971 + track_linear(revs, commit);
2972 return commit;
2973 }
2974 } while (revs->commits);
@@ -3102,14 +3135,23 @@ struct commit *get_revision(struct rev_info *revs)
3135 revs->reverse_output_stage = 1;
3136 }
3137
3105 - if (revs->reverse_output_stage)
3106 - return pop_commit(&revs->commits);
3138 + if (revs->reverse_output_stage) {
3139 + c = pop_commit(&revs->commits);
3140 + if (revs->track_linear)
3141 + revs->linear = !!(c && c->object.flags & TRACK_LINEAR);
3142 + return c;
3143 + }
3144
3145 c = get_revision_internal(revs);
3146 if (c && revs->graph)
3147 graph_update(revs->graph, c);
3111 - if (!c)
3148 + if (!c) {
3149 free_saved_parents(revs);
3150 + if (revs->previous_parents) {
3151 + free_commit_list(revs->previous_parents);
3152 + revs->previous_parents = NULL;
3153 + }
3154 + }
3155 return c;
3156 }
3157
revision.h
+9 -1
@@ -19,7 +19,8 @@
19 #define SYMMETRIC_LEFT (1u<<8)
20 #define PATCHSAME (1u<<9)
21 #define BOTTOM (1u<<10)
22 -#define ALL_REV_FLAGS ((1u<<11)-1)
22 +#define TRACK_LINEAR (1u<<26)
23 +#define ALL_REV_FLAGS (((1u<<11)-1) | TRACK_LINEAR)
24
25 #define DECORATE_SHORT_REFS 1
26 #define DECORATE_FULL_REFS 2
@@ -138,6 +139,10 @@ struct rev_info {
139 preserve_subject:1;
140 unsigned int disable_stdin:1;
141 unsigned int leak_pending:1;
142 + /* --show-linear-break */
143 + unsigned int track_linear:1,
144 + track_first_time:1,
145 + linear:1;
146
147 enum date_mode date_mode;
148
@@ -196,6 +201,9 @@ struct rev_info {
201
202 /* copies of the parent lists, for --full-diff display */
203 struct saved_parents *saved_parents_slab;
204 +
205 + struct commit_list *previous_parents;
206 + const char *break_bar;
207 };
208
209 extern int ref_excluded(struct string_list *, const char *path);