1 diff --git a/TODO b/TODO
2 index adf9129..464e122 100644
5 @@ -24,10 +24,8 @@ Features that should be explored.
7 - Split sources of tig.c into multiple files.
9 - - Rewrite revgraph handling to use --parents and --boundary. The former
10 - should help to cleanup this messy part of the code, while the latter
11 - is nice for general repository inspection when revision limiters are
13 + - Rewrite revgraph handling to use --parents, which should help to
14 + cleanup this messy part of the code.
16 - Color the revgraph to make it easier to follow branches. Idea by
18 diff --git a/VERSION b/VERSION
19 index 5712157..91e2525 100644
25 diff --git a/configure.ac b/configure.ac
26 index 65b5af3..764190a 100644
29 @@ -12,12 +12,14 @@ AC_SEARCH_LIBS([wclear], [ncursesw ncurses curses], [],
33 -AC_CHECK_PROGS(GIT_CONFIG, [git-config git-repo-config])
34 -if test "${ac_cv_prog_GIT_CONFIG}" = ""; then
35 - AC_CHECK_PROGS(GIT, [git])
36 - GIT_CONFIG="${ac_cv_prog_GIT} config"
39 +AC_CHECK_PROG(GIT, [git], [git], [AC_ERROR([git not found])])
40 +AC_MSG_CHECKING([which config command git supports])
41 +GIT_CONFIG="git repo-config"
42 +git config --list >/dev/null && GIT_CONFIG="git config"
43 +AC_MSG_RESULT([$GIT_CONFIG])
44 AC_DEFINE_UNQUOTED(GIT_CONFIG,"$GIT_CONFIG",[git config program])
46 AC_CHECK_PROGS(ASCIIDOC, [asciidoc false])
47 AC_CHECK_PROGS(XMLTO, [xmlto false])
48 AC_CHECK_PROGS(DOCBOOK2PDF, [docbook2pdf false])
49 diff --git a/manual.txt b/manual.txt
50 index ca01561..fd466db 100644
53 @@ -347,7 +347,10 @@ z Stop all background loading. This can be useful if you use \
56 '.' Toggle line numbers on/off.
57 +D Toggle date display on/off.
58 +A Toggle author display on/off.
59 g Toggle revision graph visualization on/off.
60 +F Toggle reference display on/off (tag and branch names).
61 ':' Open prompt. This allows you to specify what git command \
62 to run. Example `:log -p`
63 u Update status of file. In the status view, this allows you to add an \
64 diff --git a/tig.c b/tig.c
65 index 1eb028b..36f03c2 100644
68 @@ -58,7 +58,7 @@ static void warn(const char *msg, ...);
69 static void report(const char *msg, ...);
70 static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t));
71 static void set_nonblocking_input(bool loading);
72 -static size_t utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed);
73 +static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve);
75 #define ABS(x) ((x) >= 0 ? (x) : -(x))
76 #define MIN(x, y) ((x) < (y) ? (x) : (y))
77 @@ -140,6 +140,7 @@ struct ref {
78 char *name; /* Ref name; tag or head names are shortened. */
79 char id[SIZEOF_REV]; /* Commit SHA1 ID */
80 unsigned int tag:1; /* Is it a tag? */
81 + unsigned int ltag:1; /* If so, is the tag local? */
82 unsigned int remote:1; /* Is it a remote ref? */
83 unsigned int next:1; /* For ref lists: are there more refs? */
85 @@ -354,7 +355,10 @@ sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src)
86 REQ_(SHOW_VERSION, "Show version information"), \
87 REQ_(STOP_LOADING, "Stop all loading views"), \
88 REQ_(TOGGLE_LINENO, "Toggle line numbers"), \
89 + REQ_(TOGGLE_DATE, "Toggle date display"), \
90 + REQ_(TOGGLE_AUTHOR, "Toggle author display"), \
91 REQ_(TOGGLE_REV_GRAPH, "Toggle revision graph visualization"), \
92 + REQ_(TOGGLE_REFS, "Toggle reference display (tags/branches)"), \
93 REQ_(STATUS_UPDATE, "Update file status"), \
94 REQ_(STATUS_MERGE, "Merge file using external tool"), \
95 REQ_(TREE_PARENT, "Switch to parent directory in tree view"), \
96 @@ -422,8 +426,11 @@ static const char usage[] =
97 " -h, --help Show help message and exit\n";
99 /* Option and state variables. */
100 +static bool opt_date = TRUE;
101 +static bool opt_author = TRUE;
102 static bool opt_line_number = FALSE;
103 static bool opt_rev_graph = FALSE;
104 +static bool opt_show_refs = TRUE;
105 static int opt_num_interval = NUMBER_INTERVAL;
106 static int opt_tab_size = TABSIZE;
107 static enum request opt_request = REQ_VIEW_MAIN;
108 @@ -598,9 +605,6 @@ parse_options(int argc, char *argv[])
109 opt_cmd[buf_size] = 0;
112 - if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
118 @@ -648,6 +652,7 @@ LINE(MAIN_AUTHOR, "", COLOR_GREEN, COLOR_DEFAULT, 0), \
119 LINE(MAIN_COMMIT, "", COLOR_DEFAULT, COLOR_DEFAULT, 0), \
120 LINE(MAIN_DELIM, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
121 LINE(MAIN_TAG, "", COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD), \
122 +LINE(MAIN_LOCAL_TAG,"", COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD), \
123 LINE(MAIN_REMOTE, "", COLOR_YELLOW, COLOR_DEFAULT, A_BOLD), \
124 LINE(MAIN_REF, "", COLOR_CYAN, COLOR_DEFAULT, A_BOLD), \
125 LINE(MAIN_REVGRAPH,"", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
126 @@ -808,7 +813,10 @@ static struct keybinding default_keybindings[] = {
127 { 'v', REQ_SHOW_VERSION },
128 { 'r', REQ_SCREEN_REDRAW },
129 { '.', REQ_TOGGLE_LINENO },
130 + { 'D', REQ_TOGGLE_DATE },
131 + { 'A', REQ_TOGGLE_AUTHOR },
132 { 'g', REQ_TOGGLE_REV_GRAPH },
133 + { 'F', REQ_TOGGLE_REFS },
135 { 'u', REQ_STATUS_UPDATE },
136 { 'M', REQ_STATUS_MERGE },
137 @@ -1115,6 +1123,12 @@ option_color_command(int argc, char *argv[])
141 +static bool parse_bool(const char *s)
143 + return (!strcmp(s, "1") || !strcmp(s, "true") ||
144 + !strcmp(s, "yes")) ? TRUE : FALSE;
147 /* Wants: name = value */
149 option_set_command(int argc, char *argv[])
150 @@ -1129,10 +1143,28 @@ option_set_command(int argc, char *argv[])
154 + if (!strcmp(argv[0], "show-author")) {
155 + opt_author = parse_bool(argv[2]);
159 + if (!strcmp(argv[0], "show-date")) {
160 + opt_date = parse_bool(argv[2]);
164 if (!strcmp(argv[0], "show-rev-graph")) {
165 - opt_rev_graph = (!strcmp(argv[2], "1") ||
166 - !strcmp(argv[2], "true") ||
167 - !strcmp(argv[2], "yes"));
168 + opt_rev_graph = parse_bool(argv[2]);
172 + if (!strcmp(argv[0], "show-refs")) {
173 + opt_show_refs = parse_bool(argv[2]);
177 + if (!strcmp(argv[0], "show-line-numbers")) {
178 + opt_line_number = parse_bool(argv[2]);
182 @@ -1394,9 +1426,10 @@ struct view {
186 - unsigned long lines; /* Total number of lines */
187 + size_t lines; /* Total number of lines */
188 struct line *line; /* Line index */
189 - unsigned long line_size;/* Total number of allocated lines */
190 + size_t line_alloc; /* Total number of allocated lines */
191 + size_t line_size; /* Total number of used lines */
192 unsigned int digits; /* Number of digits in the lines member. */
195 @@ -1460,44 +1493,34 @@ static int
196 draw_text(struct view *view, const char *string, int max_len, int col,
197 bool use_tilde, int tilde_attr)
204 - int trimmed = FALSE;
206 + int trimmed = FALSE;
213 - len = utf8_length(string, max_len, &pad, &trimmed);
214 - if (trimmed && use_tilde) {
216 + len = utf8_length(string, max_len, &trimmed, use_tilde);
218 + len = strlen(string);
219 + if (len > max_len) {
223 - string, max_len, &pad, &trimmed);
227 - len = strlen(string);
228 - if (len > max_len) {
237 - waddnstr(view->win, string, n);
238 - if (trimmed && use_tilde) {
239 - if (tilde_attr != -1)
240 - wattrset(view->win, tilde_attr);
241 - waddch(view->win, '~');
249 + waddnstr(view->win, string, len);
250 + if (trimmed && use_tilde) {
251 + if (tilde_attr != -1)
252 + wattrset(view->win, tilde_attr);
253 + waddch(view->win, '~');
261 @@ -2076,15 +2099,33 @@ begin_update(struct view *view)
265 +#define ITEM_CHUNK_SIZE 256
267 +realloc_items(void *mem, size_t *size, size_t new_size, size_t item_size)
269 + size_t num_chunks = *size / ITEM_CHUNK_SIZE;
270 + size_t num_chunks_new = (new_size + ITEM_CHUNK_SIZE - 1) / ITEM_CHUNK_SIZE;
272 + if (mem == NULL || num_chunks != num_chunks_new) {
273 + *size = num_chunks_new * ITEM_CHUNK_SIZE;
274 + mem = realloc(mem, *size * item_size);
281 realloc_lines(struct view *view, size_t line_size)
283 - struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
284 + size_t alloc = view->line_alloc;
285 + struct line *tmp = realloc_items(view->line, &alloc, line_size,
286 + sizeof(*view->line));
292 + view->line_alloc = alloc;
293 view->line_size = line_size;
296 @@ -2550,11 +2591,26 @@ view_driver(struct view *view, enum request request)
300 + case REQ_TOGGLE_DATE:
301 + opt_date = !opt_date;
305 + case REQ_TOGGLE_AUTHOR:
306 + opt_author = !opt_author;
310 case REQ_TOGGLE_REV_GRAPH:
311 opt_rev_graph = !opt_rev_graph;
315 + case REQ_TOGGLE_REFS:
316 + opt_show_refs = !opt_show_refs;
321 /* Always reload^Wrerun commands from the prompt. */
322 open_view(view, opt_request, OPEN_RELOAD);
323 @@ -3366,7 +3422,7 @@ error_out:
325 /* Don't show unmerged entries in the staged section. */
326 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --diff-filter=ACDMRTXB --cached -M HEAD"
327 -#define STATUS_DIFF_FILES_CMD "git update-index -q --refresh && git diff-files -z"
328 +#define STATUS_DIFF_FILES_CMD "git diff-files -z"
329 #define STATUS_LIST_OTHER_CMD \
330 "git ls-files -z --others --exclude-per-directory=.gitignore"
332 @@ -3391,7 +3447,7 @@ status_open(struct view *view)
333 for (i = 0; i < view->lines; i++)
334 free(view->line[i].data);
336 - view->lines = view->line_size = view->lineno = 0;
337 + view->lines = view->line_alloc = view->line_size = view->lineno = 0;
340 if (!realloc_lines(view, view->line_size + 6))
341 @@ -3410,6 +3466,8 @@ status_open(struct view *view)
345 + system("git update-index -q --refresh");
347 if (!status_run(view, STATUS_DIFF_INDEX_CMD, TRUE, LINE_STAT_STAGED) ||
348 !status_run(view, STATUS_DIFF_FILES_CMD, TRUE, LINE_STAT_UNSTAGED) ||
349 !status_run(view, cmd, FALSE, LINE_STAT_UNTRACKED))
350 @@ -4183,7 +4241,7 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select
351 tilde_attr = get_line_attr(LINE_MAIN_DELIM);
358 timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
359 @@ -4201,7 +4259,7 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select
360 if (type != LINE_CURSOR)
361 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
367 max_len = view->width - col;
368 @@ -4238,12 +4296,14 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select
370 wmove(view->win, lineno, col);
372 - if (commit->refs) {
373 + if (opt_show_refs && commit->refs) {
377 if (type == LINE_CURSOR)
379 + else if (commit->refs[i]->ltag)
380 + wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
381 else if (commit->refs[i]->tag)
382 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
383 else if (commit->refs[i]->remote)
384 @@ -4559,19 +4619,16 @@ utf8_to_unicode(const char *string, size_t length)
386 /* Calculates how much of string can be shown within the given maximum width
387 * and sets trimmed parameter to non-zero value if all of string could not be
390 - * Additionally, adds to coloffset how many many columns to move to align with
391 - * the expected position. Takes into account how multi-byte and double-width
392 - * characters will effect the cursor position.
393 + * shown. If the reserve flag is TRUE, it will reserve at least one
394 + * trailing character, which can be useful when drawing a delimiter.
396 * Returns the number of bytes to output from string to satisfy max_width. */
398 -utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
399 +utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve)
401 const char *start = string;
402 const char *end = strchr(string, '\0');
403 - size_t mbwidth = 0;
404 + unsigned char last_bytes = 0;
408 @@ -4597,27 +4654,16 @@ utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
410 if (width > max_width) {
412 + if (reserve && width - ucwidth == max_width) {
413 + string -= last_bytes;
418 - /* The column offset collects the differences between the
419 - * number of bytes encoding a character and the number of
420 - * columns will be used for rendering said character.
422 - * So if some character A is encoded in 2 bytes, but will be
423 - * represented on the screen using only 1 byte this will and up
424 - * adding 1 to the multi-byte column offset.
426 - * Assumes that no double-width character can be encoding in
427 - * less than two bytes. */
428 - if (bytes > ucwidth)
429 - mbwidth += bytes - ucwidth;
432 + last_bytes = bytes;
435 - *coloffset += mbwidth;
437 return string - start;
440 @@ -4800,18 +4846,21 @@ read_prompt(const char *prompt)
441 * Repository references
444 -static struct ref *refs;
445 -static size_t refs_size;
446 +static struct ref *refs = NULL;
447 +static size_t refs_alloc = 0;
448 +static size_t refs_size = 0;
450 /* Id <-> ref store */
451 -static struct ref ***id_refs;
452 -static size_t id_refs_size;
453 +static struct ref ***id_refs = NULL;
454 +static size_t id_refs_alloc = 0;
455 +static size_t id_refs_size = 0;
460 struct ref ***tmp_id_refs;
461 struct ref **ref_list = NULL;
462 + size_t ref_list_alloc = 0;
463 size_t ref_list_size = 0;
466 @@ -4819,7 +4868,8 @@ get_refs(char *id)
467 if (!strcmp(id, id_refs[i][0]->id))
470 - tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
471 + tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
476 @@ -4831,7 +4881,8 @@ get_refs(char *id)
477 if (strcmp(id, refs[i].id))
480 - tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
481 + tmp = realloc_items(ref_list, &ref_list_alloc,
482 + ref_list_size + 1, sizeof(*ref_list));
486 @@ -4861,15 +4912,19 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
492 + bool check_replace = FALSE;
494 if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
495 - /* Commits referenced by tags has "^{}" appended. */
496 - if (name[namelen - 1] != '}')
499 - while (namelen > 0 && name[namelen] != '^')
501 + if (!strcmp(name + namelen - 3, "^{}")) {
504 + if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
505 + check_replace = TRUE;
511 namelen -= STRING_SIZE("refs/tags/");
512 @@ -4888,7 +4943,17 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
516 - refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
517 + if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
518 + /* it's an annotated tag, replace the previous sha1 with the
519 + * resolved commit id; relies on the fact git-ls-remote lists
520 + * the commit id of an annotated tag right beofre the commit id
522 + refs[refs_size - 1].ltag = ltag;
523 + string_copy_rev(refs[refs_size - 1].id, id);
527 + refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
531 @@ -4900,6 +4965,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
532 strncpy(ref->name, name, namelen);
533 ref->name[namelen] = 0;
536 ref->remote = remote;
537 string_copy_rev(ref->id, id);
539 @@ -5079,6 +5145,9 @@ main(int argc, char *argv[])
541 die("Not a git repository");
543 + if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
546 if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
547 opt_iconv = iconv_open(opt_codeset, opt_encoding);
548 if (opt_iconv == ICONV_NONE)
549 diff --git a/tigrc.5.txt b/tigrc.5.txt
550 index f7d7bed..bed3116 100644
553 @@ -42,7 +42,11 @@ is:
556 --------------------------------------------------------------------------
557 +set show-author = yes # Show author?
558 +set show-date = yes # Show commit date?
559 set show-rev-graph = yes # Show revision graph?
560 +set show-refs = yes # Show references?
561 +set show-line-numbers = no # Show line numbers?
562 set line-number-interval = 5 # Interval between line numbers
563 set tab-size = 8 # Number of spaces per tab
564 set encoding = "UTF-8" # Commit encoding
565 @@ -68,10 +72,14 @@ Variables
567 The following variables can be set:
569 +'show-author' (bool)::
570 +'show-date' (bool)::
571 'show-rev-graph' (bool)::
572 +'show-refs' (bool)::
574 - Show revision graph in the main view on start-up. Can be toggled with
576 + Whether to show author, date, revision graph, and references
577 + (branches, tags, and remotes) in the main view on start-up. Can all be
580 'line-number-interval' (int)::
582 @@ -243,7 +251,10 @@ screen-resize Resize the screen
583 show-version Show version information
584 stop-loading Stop all loading views
585 toggle-lineno Toggle line numbers
586 +toggle-date Toggle date display
587 +toggle-author Toggle author display
588 toggle-rev-graph Toggle revision graph visualization
589 +toggle-refs Toggle reference display
590 status-update Update file status
591 status-merge Resolve unmerged file
592 tree-parent Switch to parent directory in tree view
593 @@ -336,7 +347,7 @@ Appearance of the various columns in the main view, including the '~' used for
594 delimiting long author names and labels for tag and branch references.
596 *main-date*, *main-author*, *main-commit*, *main-delim*, *main-tag*,
597 -*main-ref*, *main-remote*, *main-revgraph*
598 +*main-local-tag*, *main-ref*, *main-remote*, *main-revgraph*