Call realloc() less often because it is potentially slow.
[tig] / tig.c
diff --git a/tig.c b/tig.c
index 756ce47..b3a4e8d 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -140,6 +140,7 @@ struct ref {
        char *name;             /* Ref name; tag or head names are shortened. */
        char id[SIZEOF_REV];    /* Commit SHA1 ID */
        unsigned int tag:1;     /* Is it a tag? */
+       unsigned int ltag:1;    /* If so, is the tag local? */
        unsigned int remote:1;  /* Is it a remote ref? */
        unsigned int next:1;    /* For ref lists: are there more refs? */
 };
@@ -354,7 +355,10 @@ sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src)
        REQ_(SHOW_VERSION,      "Show version information"), \
        REQ_(STOP_LOADING,      "Stop all loading views"), \
        REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
+       REQ_(TOGGLE_DATE,       "Toggle date display"), \
+       REQ_(TOGGLE_AUTHOR,     "Toggle author display"), \
        REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
+       REQ_(TOGGLE_REFS,       "Toggle reference display (tags/branches)"), \
        REQ_(STATUS_UPDATE,     "Update file status"), \
        REQ_(STATUS_MERGE,      "Merge file using external tool"), \
        REQ_(TREE_PARENT,       "Switch to parent directory in tree view"), \
@@ -422,8 +426,11 @@ static const char usage[] =
 "  -h, --help      Show help message and exit\n";
 
 /* Option and state variables. */
+static bool opt_date                   = TRUE;
+static bool opt_author                 = TRUE;
 static bool opt_line_number            = FALSE;
 static bool opt_rev_graph              = FALSE;
+static bool opt_show_refs              = TRUE;
 static int opt_num_interval            = NUMBER_INTERVAL;
 static int opt_tab_size                        = TABSIZE;
 static enum request opt_request                = REQ_VIEW_MAIN;
@@ -648,6 +655,7 @@ LINE(MAIN_AUTHOR,  "",                      COLOR_GREEN,    COLOR_DEFAULT,  0), \
 LINE(MAIN_COMMIT,  "",                 COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
 LINE(MAIN_DELIM,   "",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
 LINE(MAIN_TAG,     "",                 COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
+LINE(MAIN_LOCAL_TAG,"",                        COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
 LINE(MAIN_REMOTE,  "",                 COLOR_YELLOW,   COLOR_DEFAULT,  A_BOLD), \
 LINE(MAIN_REF,     "",                 COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
 LINE(MAIN_REVGRAPH,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
@@ -808,7 +816,10 @@ static struct keybinding default_keybindings[] = {
        { 'v',          REQ_SHOW_VERSION },
        { 'r',          REQ_SCREEN_REDRAW },
        { '.',          REQ_TOGGLE_LINENO },
+       { 'D',          REQ_TOGGLE_DATE },
+       { 'A',          REQ_TOGGLE_AUTHOR },
        { 'g',          REQ_TOGGLE_REV_GRAPH },
+       { 'F',          REQ_TOGGLE_REFS },
        { ':',          REQ_PROMPT },
        { 'u',          REQ_STATUS_UPDATE },
        { 'M',          REQ_STATUS_MERGE },
@@ -1115,6 +1126,12 @@ option_color_command(int argc, char *argv[])
        return OK;
 }
 
+static bool parse_bool(const char *s)
+{
+       return (!strcmp(s, "1") || !strcmp(s, "true") ||
+               !strcmp(s, "yes")) ? TRUE : FALSE;
+}
+
 /* Wants: name = value */
 static int
 option_set_command(int argc, char *argv[])
@@ -1129,10 +1146,28 @@ option_set_command(int argc, char *argv[])
                return ERR;
        }
 
+       if (!strcmp(argv[0], "show-author")) {
+               opt_author = parse_bool(argv[2]);
+               return OK;
+       }
+
+       if (!strcmp(argv[0], "show-date")) {
+               opt_date = parse_bool(argv[2]);
+               return OK;
+       }
+
        if (!strcmp(argv[0], "show-rev-graph")) {
-               opt_rev_graph = (!strcmp(argv[2], "1") ||
-                                !strcmp(argv[2], "true") ||
-                                !strcmp(argv[2], "yes"));
+               opt_rev_graph = parse_bool(argv[2]);
+               return OK;
+       }
+
+       if (!strcmp(argv[0], "show-refs")) {
+               opt_show_refs = parse_bool(argv[2]);
+               return OK;
+       }
+
+       if (!strcmp(argv[0], "show-line-numbers")) {
+               opt_line_number = parse_bool(argv[2]);
                return OK;
        }
 
@@ -1394,9 +1429,10 @@ struct view {
        struct view *parent;
 
        /* Buffering */
-       unsigned long lines;    /* Total number of lines */
+       size_t lines;           /* Total number of lines */
        struct line *line;      /* Line index */
-       unsigned long line_size;/* Total number of allocated lines */
+       size_t line_alloc;      /* Total number of allocated lines */
+       size_t line_size;       /* Total number of used lines */
        unsigned int digits;    /* Number of digits in the lines member. */
 
        /* Loading */
@@ -1461,29 +1497,30 @@ draw_text(struct view *view, const char *string, int max_len, int col,
          bool use_tilde, int tilde_attr)
 {
        int len = 0;
+       int trimmed = FALSE;
 
-       if (max_len > 0) {
-               int trimmed = FALSE;
+       if (max_len <= 0)
+               return 0;
 
-               if (opt_utf8) {
-                       len = utf8_length(string, max_len, &trimmed, use_tilde);
-               } else {
-                       len = strlen(string);
-                       if (len > max_len) {
-                               if (use_tilde) {
-                                       max_len -= 1;
-                               }
-                               len = max_len;
-                               trimmed = TRUE;
+       if (opt_utf8) {
+               len = utf8_length(string, max_len, &trimmed, use_tilde);
+       } else {
+               len = strlen(string);
+               if (len > max_len) {
+                       if (use_tilde) {
+                               max_len -= 1;
                        }
+                       len = max_len;
+                       trimmed = TRUE;
                }
-               waddnstr(view->win, string, len);
-               if (trimmed && use_tilde) {
-                       if (tilde_attr != -1)
-                               wattrset(view->win, tilde_attr);
-                       waddch(view->win, '~');
-                       len++;
-               }
+       }
+
+       waddnstr(view->win, string, len);
+       if (trimmed && use_tilde) {
+               if (tilde_attr != -1)
+                       wattrset(view->win, tilde_attr);
+               waddch(view->win, '~');
+               len++;
        }
 
        return len;
@@ -2065,15 +2102,33 @@ begin_update(struct view *view)
        return TRUE;
 }
 
+#define ITEM_CHUNK_SIZE 256
+static void *
+realloc_items(void *mem, size_t *size, size_t new_size, size_t item_size)
+{
+       size_t num_chunks = *size / ITEM_CHUNK_SIZE;
+       size_t num_chunks_new = (new_size + ITEM_CHUNK_SIZE - 1) / ITEM_CHUNK_SIZE;
+
+       if (mem == NULL || num_chunks != num_chunks_new) {
+               *size = num_chunks_new * ITEM_CHUNK_SIZE;
+               mem = realloc(mem, *size * item_size);
+       }
+
+       return mem;
+}
+
 static struct line *
 realloc_lines(struct view *view, size_t line_size)
 {
-       struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
+       size_t alloc = view->line_alloc;
+       struct line *tmp = realloc_items(view->line, &alloc, line_size,
+                                        sizeof(*view->line));
 
        if (!tmp)
                return NULL;
 
        view->line = tmp;
+       view->line_alloc = alloc;
        view->line_size = line_size;
        return view->line;
 }
@@ -2539,11 +2594,26 @@ view_driver(struct view *view, enum request request)
                redraw_display();
                break;
 
+       case REQ_TOGGLE_DATE:
+               opt_date = !opt_date;
+               redraw_display();
+               break;
+
+       case REQ_TOGGLE_AUTHOR:
+               opt_author = !opt_author;
+               redraw_display();
+               break;
+
        case REQ_TOGGLE_REV_GRAPH:
                opt_rev_graph = !opt_rev_graph;
                redraw_display();
                break;
 
+       case REQ_TOGGLE_REFS:
+               opt_show_refs = !opt_show_refs;
+               redraw_display();
+               break;
+
        case REQ_PROMPT:
                /* Always reload^Wrerun commands from the prompt. */
                open_view(view, opt_request, OPEN_RELOAD);
@@ -3355,7 +3425,7 @@ error_out:
 
 /* Don't show unmerged entries in the staged section. */
 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --diff-filter=ACDMRTXB --cached -M HEAD"
-#define STATUS_DIFF_FILES_CMD "git update-index -q --refresh && git diff-files -z"
+#define STATUS_DIFF_FILES_CMD "git diff-files -z"
 #define STATUS_LIST_OTHER_CMD \
        "git ls-files -z --others --exclude-per-directory=.gitignore"
 
@@ -3380,7 +3450,7 @@ status_open(struct view *view)
        for (i = 0; i < view->lines; i++)
                free(view->line[i].data);
        free(view->line);
-       view->lines = view->line_size = view->lineno = 0;
+       view->lines = view->line_alloc = view->line_size = view->lineno = 0;
        view->line = NULL;
 
        if (!realloc_lines(view, view->line_size + 6))
@@ -3399,6 +3469,8 @@ status_open(struct view *view)
                        return FALSE;
        }
 
+       system("git update-index -q --refresh");
+
        if (!status_run(view, STATUS_DIFF_INDEX_CMD, TRUE, LINE_STAT_STAGED) ||
            !status_run(view, STATUS_DIFF_FILES_CMD, TRUE, LINE_STAT_UNSTAGED) ||
            !status_run(view, cmd, FALSE, LINE_STAT_UNTRACKED))
@@ -4172,7 +4244,7 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select
                tilde_attr = get_line_attr(LINE_MAIN_DELIM);
        }
 
-       {
+       if (opt_date) {
                int n;
 
                timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
@@ -4190,7 +4262,7 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select
        if (type != LINE_CURSOR)
                wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
 
-       {
+       if (opt_author) {
                int max_len;
 
                max_len = view->width - col;
@@ -4227,12 +4299,14 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select
 
        wmove(view->win, lineno, col);
 
-       if (commit->refs) {
+       if (opt_show_refs && commit->refs) {
                size_t i = 0;
 
                do {
                        if (type == LINE_CURSOR)
                                ;
+                       else if (commit->refs[i]->ltag)
+                               wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
                        else if (commit->refs[i]->tag)
                                wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
                        else if (commit->refs[i]->remote)
@@ -4775,18 +4849,21 @@ read_prompt(const char *prompt)
  * Repository references
  */
 
-static struct ref *refs;
-static size_t refs_size;
+static struct ref *refs = NULL;
+static size_t refs_alloc = 0;
+static size_t refs_size = 0;
 
 /* Id <-> ref store */
-static struct ref ***id_refs;
-static size_t id_refs_size;
+static struct ref ***id_refs = NULL;
+static size_t id_refs_alloc = 0;
+static size_t id_refs_size = 0;
 
 static struct ref **
 get_refs(char *id)
 {
        struct ref ***tmp_id_refs;
        struct ref **ref_list = NULL;
+       size_t ref_list_alloc = 0;
        size_t ref_list_size = 0;
        size_t i;
 
@@ -4794,7 +4871,8 @@ get_refs(char *id)
                if (!strcmp(id, id_refs[i][0]->id))
                        return id_refs[i];
 
-       tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
+       tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
+                                   sizeof(*id_refs));
        if (!tmp_id_refs)
                return NULL;
 
@@ -4806,7 +4884,8 @@ get_refs(char *id)
                if (strcmp(id, refs[i].id))
                        continue;
 
-               tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
+               tmp = realloc_items(ref_list, &ref_list_alloc,
+                                   ref_list_size + 1, sizeof(*ref_list));
                if (!tmp) {
                        if (ref_list)
                                free(ref_list);
@@ -4836,15 +4915,19 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
 {
        struct ref *ref;
        bool tag = FALSE;
+       bool ltag = FALSE;
        bool remote = FALSE;
+       bool check_replace = FALSE;
 
        if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
-               /* Commits referenced by tags has "^{}" appended. */
-               if (name[namelen - 1] != '}')
-                       return OK;
-
-               while (namelen > 0 && name[namelen] != '^')
-                       namelen--;
+               if (!strcmp(name + namelen - 3, "^{}")) {
+                       namelen -= 3;
+                       name[namelen] = 0;
+                       if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
+                               check_replace = TRUE;
+               } else {
+                       ltag = TRUE;
+               }
 
                tag = TRUE;
                namelen -= STRING_SIZE("refs/tags/");
@@ -4863,7 +4946,17 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
                return OK;
        }
 
-       refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
+       if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
+               /* it's an annotated tag, replace the previous sha1 with the
+                * resolved commit id; relies on the fact git-ls-remote lists
+                * the commit id of an annotated tag right beofre the commit id
+                * it points to. */
+               refs[refs_size - 1].ltag = ltag;
+               string_copy_rev(refs[refs_size - 1].id, id);
+
+               return OK;
+       }
+       refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
        if (!refs)
                return ERR;
 
@@ -4875,6 +4968,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
        strncpy(ref->name, name, namelen);
        ref->name[namelen] = 0;
        ref->tag = tag;
+       ref->ltag = ltag;
        ref->remote = remote;
        string_copy_rev(ref->id, id);