@matheusbsbs / matheus-bagliano / commits / 427fc5b888
Fork 0

graph API: Added logic for colored edges

Modified the graph drawing logic to colorize edges based on parent-child relationships similiarly to gitk. Signed-off-by: Allan Caffee <allan.caffee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Allan Caffee committed Apr 13, 2009 at 15:53 UTC 427fc5b8885d269359c8eff187925e3d49f103b4
2 files changed +199 -48
color.h
+1
@@ -11,6 +11,7 @@
11 #define GIT_COLOR_GREEN "\033[32m"
12 #define GIT_COLOR_YELLOW "\033[33m"
13 #define GIT_COLOR_BLUE "\033[34m"
14 +#define GIT_COLOR_MAGENTA "\033[35m"
15 #define GIT_COLOR_CYAN "\033[36m"
16 #define GIT_COLOR_BG_RED "\033[41m"
17
graph.c
+198 -48
@@ -1,5 +1,6 @@
1 #include "cache.h"
2 #include "commit.h"
3 +#include "color.h"
4 #include "graph.h"
5 #include "diff.h"
6 #include "revision.h"
@@ -43,10 +44,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
44
45 /*
46 * TODO:
46 - * - Add colors to the graph.
47 - * Pick a color for each column, and print all characters
48 - * in that column with the specified color.
49 - *
47 * - Limit the number of columns, similar to the way gitk does.
48 * If we reach more than a specified number of columns, omit
49 * sections of some columns.
@@ -72,9 +69,10 @@ struct column {
69 */
70 struct commit *commit;
71 /*
75 - * XXX: Once we add support for colors, struct column could also
76 - * contain the color of its branch line.
72 + * The color to (optionally) print this column in. This is an
73 + * index into column_colors.
74 */
75 + unsigned short color;
76 };
77
78 enum graph_state {
@@ -86,6 +84,41 @@ enum graph_state {
84 GRAPH_COLLAPSING
85 };
86
87 +/*
88 + * The list of available column colors.
89 + */
90 +static char column_colors[][COLOR_MAXLEN] = {
91 + GIT_COLOR_RED,
92 + GIT_COLOR_GREEN,
93 + GIT_COLOR_YELLOW,
94 + GIT_COLOR_BLUE,
95 + GIT_COLOR_MAGENTA,
96 + GIT_COLOR_CYAN,
97 + GIT_COLOR_BOLD GIT_COLOR_RED,
98 + GIT_COLOR_BOLD GIT_COLOR_GREEN,
99 + GIT_COLOR_BOLD GIT_COLOR_YELLOW,
100 + GIT_COLOR_BOLD GIT_COLOR_BLUE,
101 + GIT_COLOR_BOLD GIT_COLOR_MAGENTA,
102 + GIT_COLOR_BOLD GIT_COLOR_CYAN,
103 +};
104 +
105 +#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
106 +
107 +static const char *column_get_color_code(const struct column *c)
108 +{
109 + return column_colors[c->color];
110 +}
111 +
112 +static void strbuf_write_column(struct strbuf *sb, const struct column *c,
113 + char col_char)
114 +{
115 + if (c->color < COLUMN_COLORS_MAX)
116 + strbuf_addstr(sb, column_get_color_code(c));
117 + strbuf_addch(sb, col_char);
118 + if (c->color < COLUMN_COLORS_MAX)
119 + strbuf_addstr(sb, GIT_COLOR_RESET);
120 +}
121 +
122 struct git_graph {
123 /*
124 * The commit currently being processed
@@ -185,6 +218,11 @@ struct git_graph {
218 * temporary array each time we have to output a collapsing line.
219 */
220 int *new_mapping;
221 + /*
222 + * The current default column color being used. This is
223 + * stored as an index into the array column_colors.
224 + */
225 + unsigned short default_column_color;
226 };
227
228 struct git_graph *graph_init(struct rev_info *opt)
@@ -201,6 +239,7 @@ struct git_graph *graph_init(struct rev_info *opt)
239 graph->num_columns = 0;
240 graph->num_new_columns = 0;
241 graph->mapping_size = 0;
242 + graph->default_column_color = 0;
243
244 /*
245 * Allocate a reasonably large default number of columns
@@ -312,6 +351,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph)
351 return next_interesting_parent(graph, parents);
352 }
353
354 +static unsigned short graph_get_current_column_color(const struct git_graph *graph)
355 +{
356 + if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
357 + return COLUMN_COLORS_MAX;
358 + return graph->default_column_color;
359 +}
360 +
361 +/*
362 + * Update the graph's default column color.
363 + */
364 +static void graph_increment_column_color(struct git_graph *graph)
365 +{
366 + graph->default_column_color = (graph->default_column_color + 1) %
367 + COLUMN_COLORS_MAX;
368 +}
369 +
370 +static unsigned short graph_find_commit_color(const struct git_graph *graph,
371 + const struct commit *commit)
372 +{
373 + int i;
374 + for (i = 0; i < graph->num_columns; i++) {
375 + if (graph->columns[i].commit == commit)
376 + return graph->columns[i].color;
377 + }
378 + return graph_get_current_column_color(graph);
379 +}
380 +
381 static void graph_insert_into_new_columns(struct git_graph *graph,
382 struct commit *commit,
383 int *mapping_index)
@@ -334,6 +400,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph,
400 * This commit isn't already in new_columns. Add it.
401 */
402 graph->new_columns[graph->num_new_columns].commit = commit;
403 + graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit);
404 graph->mapping[*mapping_index] = graph->num_new_columns;
405 *mapping_index += 2;
406 graph->num_new_columns++;
@@ -445,6 +512,12 @@ static void graph_update_columns(struct git_graph *graph)
512 for (parent = first_interesting_parent(graph);
513 parent;
514 parent = next_interesting_parent(graph, parent)) {
515 + /*
516 + * If this is a merge increment the current
517 + * color.
518 + */
519 + if (graph->num_parents > 1)
520 + graph_increment_column_color(graph);
521 graph_insert_into_new_columns(graph,
522 parent->item,
523 &mapping_idx);
@@ -560,7 +633,8 @@ static int graph_is_mapping_correct(struct git_graph *graph)
633 return 1;
634 }
635
563 -static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)
636 +static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb,
637 + int chars_written)
638 {
639 /*
640 * Add additional spaces to the end of the strbuf, so that all
@@ -570,10 +644,10 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)
644 * aligned for the entire commit.
645 */
646 int extra;
573 - if (sb->len >= graph->width)
647 + if (chars_written >= graph->width)
648 return;
649
576 - extra = graph->width - sb->len;
650 + extra = graph->width - chars_written;
651 strbuf_addf(sb, "%*s", (int) extra, "");
652 }
653
@@ -596,10 +670,11 @@ static void graph_output_padding_line(struct git_graph *graph,
670 * Output a padding row, that leaves all branch lines unchanged
671 */
672 for (i = 0; i < graph->num_new_columns; i++) {
599 - strbuf_addstr(sb, "| ");
673 + strbuf_write_column(sb, &graph->new_columns[i], '|');
674 + strbuf_addch(sb, ' ');
675 }
676
602 - graph_pad_horizontally(graph, sb);
677 + graph_pad_horizontally(graph, sb, graph->num_new_columns * 2);
678 }
679
680 static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
@@ -609,7 +684,7 @@ static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
684 * of the graph is missing.
685 */
686 strbuf_addstr(sb, "...");
612 - graph_pad_horizontally(graph, sb);
687 + graph_pad_horizontally(graph, sb, 3);
688
689 if (graph->num_parents >= 3 &&
690 graph->commit_index < (graph->num_columns - 1))
@@ -623,6 +698,7 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
698 {
699 int num_expansion_rows;
700 int i, seen_this;
701 + int chars_written;
702
703 /*
704 * This function formats a row that increases the space around a commit
@@ -645,11 +721,14 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
721 * Output the row
722 */
723 seen_this = 0;
724 + chars_written = 0;
725 for (i = 0; i < graph->num_columns; i++) {
726 struct column *col = &graph->columns[i];
727 if (col->commit == graph->commit) {
728 seen_this = 1;
652 - strbuf_addf(sb, "| %*s", graph->expansion_row, "");
729 + strbuf_write_column(sb, col, '|');
730 + strbuf_addf(sb, " %*s", graph->expansion_row, "");
731 + chars_written += 2 + graph->expansion_row;
732 } else if (seen_this && (graph->expansion_row == 0)) {
733 /*
734 * This is the first line of the pre-commit output.
@@ -662,17 +741,22 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
741 */
742 if (graph->prev_state == GRAPH_POST_MERGE &&
743 graph->prev_commit_index < i)
665 - strbuf_addstr(sb, "\\ ");
744 + strbuf_write_column(sb, col, '\\');
745 else
667 - strbuf_addstr(sb, "| ");
746 + strbuf_write_column(sb, col, '|');
747 + chars_written++;
748 } else if (seen_this && (graph->expansion_row > 0)) {
669 - strbuf_addstr(sb, "\\ ");
749 + strbuf_write_column(sb, col, '\\');
750 + chars_written++;
751 } else {
671 - strbuf_addstr(sb, "| ");
752 + strbuf_write_column(sb, col, '|');
753 + chars_written++;
754 }
755 + strbuf_addch(sb, ' ');
756 + chars_written++;
757 }
758
675 - graph_pad_horizontally(graph, sb);
759 + graph_pad_horizontally(graph, sb, chars_written);
760
761 /*
762 * Increment graph->expansion_row,
@@ -714,10 +798,34 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
798 strbuf_addch(sb, '*');
799 }
800
801 +/*
802 + * Draw an octopus merge and return the number of characters written.
803 + */
804 +static int graph_draw_octopus_merge(struct git_graph *graph,
805 + struct strbuf *sb)
806 +{
807 + /*
808 + * Here dashless_commits represents the number of parents
809 + * which don't need to have dashes (because their edges fit
810 + * neatly under the commit).
811 + */
812 + const int dashless_commits = 2;
813 + int col_num, i;
814 + int num_dashes =
815 + ((graph->num_parents - dashless_commits) * 2) - 1;
816 + for (i = 0; i < num_dashes; i++) {
817 + col_num = (i / 2) + dashless_commits;
818 + strbuf_write_column(sb, &graph->new_columns[col_num], '-');
819 + }
820 + col_num = (i / 2) + dashless_commits;
821 + strbuf_write_column(sb, &graph->new_columns[col_num], '.');
822 + return num_dashes + 1;
823 +}
824 +
825 static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
826 {
827 int seen_this = 0;
720 - int i, j;
828 + int i, chars_written;
829
830 /*
831 * Output the row containing this commit
@@ -727,7 +835,9 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
835 * children that we have already processed.)
836 */
837 seen_this = 0;
838 + chars_written = 0;
839 for (i = 0; i <= graph->num_columns; i++) {
840 + struct column *col = &graph->columns[i];
841 struct commit *col_commit;
842 if (i == graph->num_columns) {
843 if (seen_this)
@@ -740,18 +850,14 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
850 if (col_commit == graph->commit) {
851 seen_this = 1;
852 graph_output_commit_char(graph, sb);
853 + chars_written++;
854
744 - if (graph->num_parents < 3)
745 - strbuf_addch(sb, ' ');
746 - else {
747 - int num_dashes =
748 - ((graph->num_parents - 2) * 2) - 1;
749 - for (j = 0; j < num_dashes; j++)
750 - strbuf_addch(sb, '-');
751 - strbuf_addstr(sb, ". ");
752 - }
855 + if (graph->num_parents > 3)
856 + chars_written += graph_draw_octopus_merge(graph,
857 + sb);
858 } else if (seen_this && (graph->num_parents > 2)) {
754 - strbuf_addstr(sb, "\\ ");
859 + strbuf_write_column(sb, col, '\\');
860 + chars_written++;
861 } else if (seen_this && (graph->num_parents == 2)) {
862 /*
863 * This is a 2-way merge commit.
@@ -768,15 +874,19 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
874 */
875 if (graph->prev_state == GRAPH_POST_MERGE &&
876 graph->prev_commit_index < i)
771 - strbuf_addstr(sb, "\\ ");
877 + strbuf_write_column(sb, col, '\\');
878 else
773 - strbuf_addstr(sb, "| ");
879 + strbuf_write_column(sb, col, '|');
880 + chars_written++;
881 } else {
775 - strbuf_addstr(sb, "| ");
882 + strbuf_write_column(sb, col, '|');
883 + chars_written++;
884 }
885 + strbuf_addch(sb, ' ');
886 + chars_written++;
887 }
888
779 - graph_pad_horizontally(graph, sb);
889 + graph_pad_horizontally(graph, sb, chars_written);
890
891 /*
892 * Update graph->state
@@ -789,37 +899,75 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
899 graph_update_state(graph, GRAPH_COLLAPSING);
900 }
901
902 +static struct column *find_new_column_by_commit(struct git_graph *graph,
903 + struct commit *commit)
904 +{
905 + int i;
906 + for (i = 0; i < graph->num_new_columns; i++) {
907 + if (graph->new_columns[i].commit == commit)
908 + return &graph->new_columns[i];
909 + }
910 + return 0;
911 +}
912 +
913 static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
914 {
915 int seen_this = 0;
795 - int i, j;
916 + int i, j, chars_written;
917
918 /*
919 * Output the post-merge row
920 */
921 + chars_written = 0;
922 for (i = 0; i <= graph->num_columns; i++) {
923 + struct column *col = &graph->columns[i];
924 struct commit *col_commit;
925 if (i == graph->num_columns) {
926 if (seen_this)
927 break;
928 col_commit = graph->commit;
929 } else {
807 - col_commit = graph->columns[i].commit;
930 + col_commit = col->commit;
931 }
932
933 if (col_commit == graph->commit) {
934 + /*
935 + * Since the current commit is a merge find
936 + * the columns for the parent commits in
937 + * new_columns and use those to format the
938 + * edges.
939 + */
940 + struct commit_list *parents = NULL;
941 + struct column *par_column;
942 seen_this = 1;
812 - strbuf_addch(sb, '|');
813 - for (j = 0; j < graph->num_parents - 1; j++)
814 - strbuf_addstr(sb, "\\ ");
943 + parents = first_interesting_parent(graph);
944 + assert(parents);
945 + par_column = find_new_column_by_commit(graph, parents->item);
946 + assert(par_column);
947 +
948 + strbuf_write_column(sb, par_column, '|');
949 + chars_written++;
950 + for (j = 0; j < graph->num_parents - 1; j++) {
951 + parents = next_interesting_parent(graph, parents);
952 + assert(parents);
953 + par_column = find_new_column_by_commit(graph, parents->item);
954 + assert(par_column);
955 + strbuf_write_column(sb, par_column, '\\');
956 + strbuf_addch(sb, ' ');
957 + }
958 + chars_written += j * 2;
959 } else if (seen_this) {
816 - strbuf_addstr(sb, "\\ ");
960 + strbuf_write_column(sb, col, '\\');
961 + strbuf_addch(sb, ' ');
962 + chars_written += 2;
963 } else {
818 - strbuf_addstr(sb, "| ");
964 + strbuf_write_column(sb, col, '|');
965 + strbuf_addch(sb, ' ');
966 + chars_written += 2;
967 }
968 }
969
822 - graph_pad_horizontally(graph, sb);
970 + graph_pad_horizontally(graph, sb, chars_written);
971
972 /*
973 * Update graph->state
@@ -912,12 +1060,12 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
1060 if (target < 0)
1061 strbuf_addch(sb, ' ');
1062 else if (target * 2 == i)
915 - strbuf_addch(sb, '|');
1063 + strbuf_write_column(sb, &graph->new_columns[target], '|');
1064 else
917 - strbuf_addch(sb, '/');
1065 + strbuf_write_column(sb, &graph->new_columns[target], '/');
1066 }
1067
920 - graph_pad_horizontally(graph, sb);
1068 + graph_pad_horizontally(graph, sb, graph->mapping_size);
1069
1070 /*
1071 * Swap mapping and new_mapping
@@ -979,9 +1127,10 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
1127 * children that we have already processed.)
1128 */
1129 for (i = 0; i < graph->num_columns; i++) {
982 - struct commit *col_commit = graph->columns[i].commit;
1130 + struct column *col = &graph->columns[i];
1131 + struct commit *col_commit = col->commit;
1132 if (col_commit == graph->commit) {
984 - strbuf_addch(sb, '|');
1133 + strbuf_write_column(sb, col, '|');
1134
1135 if (graph->num_parents < 3)
1136 strbuf_addch(sb, ' ');
@@ -991,11 +1140,12 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
1140 strbuf_addch(sb, ' ');
1141 }
1142 } else {
994 - strbuf_addstr(sb, "| ");
1143 + strbuf_write_column(sb, col, '|');
1144 + strbuf_addch(sb, ' ');
1145 }
1146 }
1147
998 - graph_pad_horizontally(graph, sb);
1148 + graph_pad_horizontally(graph, sb, graph->num_columns);
1149
1150 /*
1151 * Update graph->prev_state since we have output a padding line