Code

Tree view: improve to look less like plumbing
authorJonas Fonseca <fonseca@diku.dk>
Mon, 26 Jan 2009 11:51:40 +0000 (12:51 +0100)
committerJonas Fonseca <fonseca@diku.dk>
Tue, 27 Jan 2009 10:18:25 +0000 (11:18 +0100)
It is now more like `ls` by annotating the output of ls-tree with commit
information.

NEWS
tig.c

diff --git a/NEWS b/NEWS
index a46737471308c0bd4ff10cc9f29b05d32ec6ac45..04a1c8305abca00f2bdcac883f247c05f55889f6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,7 @@ Incompatibilities:
 Improvements:
 
  - Tree view: avoid flickering when updating.
+ - Tree view: annotate entries with commit information.
  - Tree & blob view: open any blob in an editor.
  - Stage & main view: restore view position when reloading.
 
diff --git a/tig.c b/tig.c
index e8ef8df5c0da63be434303c9bf0fe524169e4fbd..6bbe2920ffda6167c4e5388c4fbbc07eff45ee27 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -797,6 +797,7 @@ static char opt_codeset[20]         = "UTF-8";
 static iconv_t opt_iconv               = ICONV_NONE;
 static char opt_search[SIZEOF_STR]     = "";
 static char opt_cdup[SIZEOF_STR]       = "";
+static char opt_prefix[SIZEOF_STR]     = "";
 static char opt_git_dir[SIZEOF_STR]    = "";
 static signed char opt_is_inside_work_tree     = -1; /* set to TRUE or FALSE */
 static char opt_editor[SIZEOF_STR]     = "";
@@ -939,7 +940,9 @@ LINE(MAIN_TRACKED, "",                      COLOR_YELLOW,   COLOR_DEFAULT,  A_BOLD), \
 LINE(MAIN_REF,     "",                 COLOR_CYAN,     COLOR_DEFAULT,  0), \
 LINE(MAIN_HEAD,    "",                 COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
 LINE(MAIN_REVGRAPH,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
-LINE(TREE_DIR,     "",                 COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
+LINE(TREE_PARENT,  "",                 COLOR_DEFAULT,  COLOR_DEFAULT,  A_BOLD), \
+LINE(TREE_MODE,    "",                 COLOR_CYAN,     COLOR_DEFAULT,  0), \
+LINE(TREE_DIR,     "",                 COLOR_YELLOW,   COLOR_DEFAULT,  A_NORMAL), \
 LINE(TREE_FILE,    "",                 COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
 LINE(STAT_HEAD,    "",                 COLOR_YELLOW,   COLOR_DEFAULT,  0), \
 LINE(STAT_SECTION, "",                 COLOR_CYAN,     COLOR_DEFAULT,  0), \
@@ -3673,16 +3676,29 @@ push_tree_stack_entry(const char *name, unsigned long lineno)
 #define SIZEOF_TREE_ATTR \
        STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
 
+#define SIZEOF_TREE_PERM \
+       STRING_SIZE("100644 ")
+
+#define TREE_ID_OFFSET \
+       STRING_SIZE("100644 blob ")
+
 #define TREE_UP_FORMAT "040000 tree %s\t.."
 
+struct tree_entry {
+       char id[SIZEOF_REV];
+       mode_t mode;
+       struct tm time;                 /* Date from the author ident. */
+       char author[75];                /* Author of the commit. */
+       char name[1];
+};
+
 static const char *
 tree_path(struct line *line)
 {
-       const char *path = line->data;
-
-       return path + SIZEOF_TREE_ATTR;
+       return ((struct tree_entry *) line->data)->name;
 }
 
+
 static int
 tree_compare_entry(struct line *line1, struct line *line2)
 {
@@ -3691,30 +3707,120 @@ tree_compare_entry(struct line *line1, struct line *line2)
        return strcmp(tree_path(line1), tree_path(line2));
 }
 
+static struct line *
+tree_entry(struct view *view, enum line_type type, const char *path,
+          const char *mode, const char *id)
+{
+       struct tree_entry *entry = calloc(1, sizeof(*entry) + strlen(path));
+       struct line *line = entry ? add_line_data(view, entry, type) : NULL;
+
+       if (!entry || !line) {
+               free(entry);
+               return NULL;
+       }
+
+       strncpy(entry->name, path, strlen(path));
+       if (mode)
+               entry->mode = strtoul(mode, NULL, 8);
+       if (id)
+               string_copy_rev(entry->id, id);
+
+       return line;
+}
+
+static bool
+tree_read_date(struct view *view, char *text, bool *read_date)
+{
+       static char author_name[SIZEOF_STR];
+       static struct tm author_time;
+
+       if (!text && *read_date) {
+               *read_date = FALSE;
+               return TRUE;
+
+       } else if (!text) {
+               char *path = *opt_path ? opt_path : ".";
+               /* Find next entry to process */
+               const char *log_file[] = {
+                       "git", "log", "--no-color", "--pretty=raw",
+                               "--cc", "--raw", view->id, "--", path, NULL
+               };
+               struct io io = {};
+
+               if (!run_io_rd(&io, log_file, FORMAT_NONE)) {
+                       report("Failed to load tree data");
+                       return TRUE;
+               }
+
+               done_io(view->pipe);
+               view->io = io;
+               *read_date = TRUE;
+               return FALSE;
+
+       } else if (*text == 'a' && get_line_type(text) == LINE_AUTHOR) {
+               parse_author_line(text + STRING_SIZE("author "),
+                                 author_name, sizeof(author_name), &author_time);
+
+       } else if (*text == ':') {
+               char *pos;
+               size_t annotated = 1;
+               size_t i;
+
+               pos = strchr(text, '\t');
+               if (!pos)
+                       return TRUE;
+               text = pos + 1;
+               if (*opt_prefix && !strncmp(text, opt_prefix, strlen(opt_prefix)))
+                       text += strlen(opt_prefix);
+               if (*opt_path && !strncmp(text, opt_path, strlen(opt_path)))
+                       text += strlen(opt_path);
+               pos = strchr(text, '/');
+               if (pos)
+                       *pos = 0;
+
+               for (i = 1; i < view->lines; i++) {
+                       struct line *line = &view->line[i];
+                       struct tree_entry *entry = line->data;
+
+                       annotated += !!*entry->author;
+                       if (*entry->author || strcmp(entry->name, text))
+                               continue;
+
+                       string_copy(entry->author, author_name);
+                       memcpy(&entry->time, &author_time, sizeof(entry->time));
+                       line->dirty = 1;
+                       break;
+               }
+
+               if (annotated == view->lines)
+                       kill_io(view->pipe);
+       }
+       return TRUE;
+}
+
 static bool
 tree_read(struct view *view, char *text)
 {
-       size_t textlen = text ? strlen(text) : 0;
+       static bool read_date = FALSE;
+       struct tree_entry *data;
        struct line *entry, *line;
        enum line_type type;
+       size_t textlen = text ? strlen(text) : 0;
+       char *path = text + SIZEOF_TREE_ATTR;
+
+       if (read_date || !text)
+               return tree_read_date(view, text, &read_date);
 
-       if (!text)
-               return TRUE;
        if (textlen <= SIZEOF_TREE_ATTR)
                return FALSE;
-
-       type = text[STRING_SIZE("100644 ")] == 't'
-            ? LINE_TREE_DIR : LINE_TREE_FILE;
-
        if (view->lines == 0 &&
-           !add_line_format(view, LINE_DEFAULT, "Directory path /%s", opt_path))
+           !tree_entry(view, LINE_TREE_PARENT, opt_path, NULL, NULL))
                return FALSE;
 
        /* Strip the path part ... */
        if (*opt_path) {
                size_t pathlen = textlen - SIZEOF_TREE_ATTR;
-               size_t striplen = strlen(opt_path);
-               char *path = text + SIZEOF_TREE_ATTR;
+                size_t striplen = strlen(opt_path);
 
                if (pathlen > striplen)
                        memmove(path, path + striplen,
@@ -3722,14 +3828,17 @@ tree_read(struct view *view, char *text)
 
                /* Insert "link" to parent directory. */
                if (view->lines == 1 &&
-                   !add_line_format(view, LINE_TREE_DIR, TREE_UP_FORMAT, view->ref))
+                   !tree_entry(view, LINE_TREE_DIR, "..", "040000", view->ref))
                        return FALSE;
        }
 
-       entry = add_line_text(view, text, type);
+       type = text[STRING_SIZE("100644 ")] == 't'
+            ? LINE_TREE_DIR : LINE_TREE_FILE;
+
+       entry = tree_entry(view, type, path, text, text + TREE_ID_OFFSET);
        if (!entry)
                return FALSE;
-       text = entry->data;
+       data = entry->data;
 
        /* Skip "Directory ..." and ".." line. */
        for (line = &view->line[1 + !!*opt_path]; line < entry; line++) {
@@ -3738,7 +3847,7 @@ tree_read(struct view *view, char *text)
 
                memmove(line + 1, line, (entry - line) * sizeof(*entry));
 
-               line->data = text;
+               line->data = data;
                line->type = type;
                for (; line <= entry; line++)
                        line->dirty = line->cleareol = 1;
@@ -3753,6 +3862,46 @@ tree_read(struct view *view, char *text)
        return TRUE;
 }
 
+static bool
+tree_draw(struct view *view, struct line *line, unsigned int lineno)
+{
+       struct tree_entry *entry = line->data;
+
+       if (line->type == LINE_TREE_PARENT) {
+               if (draw_text(view, line->type, "Directory path /", TRUE))
+                       return TRUE;
+       } else {
+               char mode[11] = "-r--r--r--";
+
+               if (S_ISDIR(entry->mode)) {
+                       mode[3] = mode[6] = mode[9] = 'x';
+                       mode[0] = 'd';
+               }
+               if (S_ISLNK(entry->mode))
+                       mode[0] = 'l';
+               if (entry->mode & S_IWUSR)
+                       mode[2] = 'w';
+               if (entry->mode & S_IXUSR)
+                       mode[3] = 'x';
+               if (entry->mode & S_IXGRP)
+                       mode[6] = 'x';
+               if (entry->mode & S_IXOTH)
+                       mode[9] = 'x';
+               if (draw_field(view, LINE_TREE_MODE, mode, 11, TRUE))
+                       return TRUE;
+
+               if (opt_author &&
+                   draw_field(view, LINE_MAIN_AUTHOR, entry->author, opt_author_cols, TRUE))
+                       return TRUE;
+
+               if (opt_date && draw_date(view, *entry->author ? &entry->time : NULL))
+                       return TRUE;
+       }
+       if (draw_text(view, line->type, entry->name, TRUE))
+               return TRUE;
+       return TRUE;
+}
+
 static void
 open_blob_editor()
 {
@@ -3853,17 +4002,17 @@ tree_request(struct view *view, enum request request, struct line *line)
 static void
 tree_select(struct view *view, struct line *line)
 {
-       char *text = (char *)line->data + STRING_SIZE("100644 blob ");
+       struct tree_entry *entry = line->data;
 
        if (line->type == LINE_TREE_FILE) {
-               string_copy_rev(ref_blob, text);
+               string_copy_rev(ref_blob, entry->id);
                string_format(opt_file, "%s%s", opt_path, tree_path(line));
 
        } else if (line->type != LINE_TREE_DIR) {
                return;
        }
 
-       string_copy_rev(view->ref, text);
+       string_copy_rev(view->ref, entry->id);
 }
 
 static const char *tree_argv[SIZEOF_ARG] = {
@@ -3875,7 +4024,7 @@ static struct view_ops tree_ops = {
        tree_argv,
        NULL,
        tree_read,
-       pager_draw,
+       tree_draw,
        tree_request,
        pager_grep,
        tree_select,
@@ -6384,8 +6533,12 @@ read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
                 * the option else either "true" or "false" is printed.
                 * Default to true for the unknown case. */
                opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
-       } else {
+
+       } else if (*name == '.') {
                string_ncopy(opt_cdup, name, namelen);
+
+       } else {
+               string_ncopy(opt_prefix, name, namelen);
        }
 
        return OK;
@@ -6399,7 +6552,7 @@ load_repo_info(void)
        };
        const char *rev_parse_argv[] = {
                "git", "rev-parse", "--git-dir", "--is-inside-work-tree",
-                       "--show-cdup", NULL
+                       "--show-cdup", "--show-prefix", NULL
        };
 
        if (run_io_buf(head_argv, opt_head, sizeof(opt_head))) {