X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/8a6809884863eb60c9c0f8727ff77722d6b85414..f5a5e640f1fc42e3e95c51b827dff738e7dbf1ec:/tig.c diff --git a/tig.c b/tig.c index 8db304e..b5e6988 100644 --- a/tig.c +++ b/tig.c @@ -145,6 +145,7 @@ struct ref { 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? */ + unsigned int head:1; /* Is it the current HEAD? */ }; static struct ref **get_refs(char *id); @@ -440,7 +441,9 @@ static int opt_tab_size = TABSIZE; static enum request opt_request = REQ_VIEW_MAIN; static char opt_cmd[SIZEOF_STR] = ""; static char opt_path[SIZEOF_STR] = ""; +static char opt_file[SIZEOF_STR] = ""; static char opt_ref[SIZEOF_REF] = ""; +static char opt_head[SIZEOF_REF] = ""; static FILE *opt_pipe = NULL; static char opt_encoding[20] = "UTF-8"; static bool opt_utf8 = TRUE; @@ -452,64 +455,22 @@ 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] = ""; -enum option_type { - OPT_NONE, - OPT_INT, -}; - -static bool -check_option(char *opt, char short_name, char *name, enum option_type type, ...) -{ - va_list args; - char *value = ""; - int *number; - - if (opt[0] != '-') - return FALSE; - - if (opt[1] == '-') { - int namelen = strlen(name); - - opt += 2; - - if (strncmp(opt, name, namelen)) - return FALSE; - - if (opt[namelen] == '=') - value = opt + namelen + 1; - - } else { - if (!short_name || opt[1] != short_name) - return FALSE; - value = opt + 2; - } - - va_start(args, type); - if (type == OPT_INT) { - number = va_arg(args, int *); - if (isdigit(*value)) - *number = atoi(value); - } - va_end(args); - - return TRUE; -} - -/* Returns the index of log or diff command or -1 to exit. */ static bool parse_options(int argc, char *argv[]) { - char *altargv[1024]; - int altargc = 0; + size_t buf_size; char *subcommand; + bool seen_dashdash = FALSE; int i; if (argc <= 1) return TRUE; subcommand = argv[1]; - if (!strcmp(subcommand, "status")) { + if (!strcmp(subcommand, "status") || !strcmp(subcommand, "-S")) { opt_request = REQ_VIEW_STATUS; + if (!strcmp(subcommand, "-S")) + warn("`-S' has been deprecated; use `tig status' instead"); if (argc > 2) warn("ignoring arguments after `%s'", subcommand); return TRUE; @@ -525,7 +486,7 @@ parse_options(int argc, char *argv[]) i++; } - string_ncopy(opt_path, argv[i], strlen(argv[i])); + string_ncopy(opt_file, argv[i], strlen(argv[i])); return TRUE; } else if (!strcmp(subcommand, "show")) { @@ -540,83 +501,44 @@ parse_options(int argc, char *argv[]) subcommand = NULL; } + if (!subcommand) + /* XXX: This is vulnerable to the user overriding + * options required for the main view parser. */ + string_copy(opt_cmd, "git log --no-color --pretty=raw --boundary --parents"); + else + string_format(opt_cmd, "git %s", subcommand); + + buf_size = strlen(opt_cmd); + for (i = 1 + !!subcommand; i < argc; i++) { char *opt = argv[i]; - if (opt[0] && opt[0] != '-') - break; + if (seen_dashdash || !strcmp(opt, "--")) { + seen_dashdash = TRUE; - if (!strcmp(opt, "--")) { - i++; - break; - } - - if (check_option(opt, 'v', "version", OPT_NONE)) { + } else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) { printf("tig version %s\n", TIG_VERSION); return FALSE; - } - if (check_option(opt, 'h', "help", OPT_NONE)) { + } else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) { printf(usage); return FALSE; } - if (!strcmp(opt, "-S")) { - warn("`%s' has been deprecated; use `tig status' instead", opt); - opt_request = REQ_VIEW_STATUS; - continue; - } - - if (!strcmp(opt, "-l")) { - opt_request = REQ_VIEW_LOG; - } else if (!strcmp(opt, "-d")) { - opt_request = REQ_VIEW_DIFF; - } else if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) { - opt_line_number = TRUE; - } else if (check_option(opt, 'b', "tab-size", OPT_INT, &opt_tab_size)) { - opt_tab_size = MIN(opt_tab_size, TABSIZE); - } else { - if (altargc >= ARRAY_SIZE(altargv)) - die("maximum number of arguments exceeded"); - altargv[altargc++] = opt; - continue; - } - - warn("`%s' has been deprecated", opt); + opt_cmd[buf_size++] = ' '; + buf_size = sq_quote(opt_cmd, buf_size, opt); + if (buf_size >= sizeof(opt_cmd)) + die("command too long"); } if (!isatty(STDIN_FILENO)) { opt_request = REQ_VIEW_PAGER; opt_pipe = stdin; - - } else if (i < argc || altargc > 0) { - int alti = 0; - size_t buf_size; - - if (opt_request == REQ_VIEW_MAIN) - /* XXX: This is vulnerable to the user overriding - * options required for the main view parser. */ - string_copy(opt_cmd, "git log --no-color --pretty=raw --boundary --parents"); - else - string_format(opt_cmd, "git %s", subcommand); - buf_size = strlen(opt_cmd); - - while (buf_size < sizeof(opt_cmd) && alti < altargc) { - opt_cmd[buf_size++] = ' '; - buf_size = sq_quote(opt_cmd, buf_size, altargv[alti++]); - } - - while (buf_size < sizeof(opt_cmd) && i < argc) { - opt_cmd[buf_size++] = ' '; - buf_size = sq_quote(opt_cmd, buf_size, argv[i++]); - } - - if (buf_size >= sizeof(opt_cmd)) - die("command too long"); - - opt_cmd[buf_size] = 0; + buf_size = 0; } + opt_cmd[buf_size] = 0; + return TRUE; } @@ -667,9 +589,11 @@ 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_HEAD, "", COLOR_RED, COLOR_DEFAULT, A_BOLD), \ LINE(MAIN_REVGRAPH,"", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(TREE_DIR, "", COLOR_DEFAULT, 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), \ LINE(STAT_NONE, "", COLOR_DEFAULT, COLOR_DEFAULT, 0), \ LINE(STAT_STAGED, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ @@ -2252,7 +2176,6 @@ update_view(struct view *view) * might have rearranged things. */ redraw_view(view); - } else if (redraw_from >= 0) { /* If this is an incremental update, redraw the previous line * since for commits some members could have changed when @@ -2550,16 +2473,11 @@ view_driver(struct view *view, enum request request) break; case REQ_VIEW_BLAME: - if (!opt_path[0]) { + if (!opt_file[0]) { report("No file chosen, press %s to open tree view", get_key(REQ_VIEW_TREE)); break; } - if (opt_path[strlen(opt_path) - 1] == '/') { - report("Cannot show blame for directory %s", opt_path); - break; - } - string_copy(opt_ref, ref_commit); open_view(view, request, OPEN_DEFAULT); break; @@ -3143,6 +3061,14 @@ tree_compare_entry(enum line_type type1, char *name1, return strcmp(name1, name2); } +static char * +tree_path(struct line *line) +{ + char *path = line->data; + + return path + SIZEOF_TREE_ATTR; +} + static bool tree_read(struct view *view, char *text) { @@ -3190,7 +3116,7 @@ tree_read(struct view *view, char *text) /* Skip "Directory ..." and ".." line. */ for (pos = 1 + !!*opt_path; pos < view->lines; pos++) { struct line *line = &view->line[pos]; - char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR; + char *path1 = tree_path(line); char *path2 = text + SIZEOF_TREE_ATTR; int cmp = tree_compare_entry(line->type, path1, type, path2); @@ -3228,6 +3154,18 @@ tree_request(struct view *view, enum request request, struct line *line) { enum open_flags flags; + if (request == REQ_VIEW_BLAME) { + char *filename = tree_path(line); + + if (line->type == LINE_TREE_DIR) { + report("Cannot show blame for directory %s", opt_path); + return REQ_NONE; + } + + string_copy(opt_ref, view->vid); + string_format(opt_file, "%s%s", opt_path, filename); + return request; + } if (request == REQ_TREE_PARENT) { if (*opt_path) { /* fake 'cd ..' */ @@ -3253,8 +3191,7 @@ tree_request(struct view *view, enum request request, struct line *line) pop_tree_stack_entry(); } else { - char *data = line->data; - char *basename = data + SIZEOF_TREE_ATTR; + char *basename = tree_path(line); push_tree_stack_entry(basename, view->lineno); } @@ -3310,6 +3247,8 @@ static struct view_ops tree_ops = { static bool blob_read(struct view *view, char *line) { + if (!line) + return TRUE; return add_line_text(view, line, LINE_DEFAULT) != NULL; } @@ -3328,7 +3267,7 @@ static struct view_ops blob_ops = { * * Loading the blame view is a two phase job: * - * 1. File content is read either using opt_path from the + * 1. File content is read either using opt_file from the * filesystem or using git-cat-file. * 2. Then blame information is incrementally added by * reading output from git-blame. @@ -3357,7 +3296,7 @@ blame_open(struct view *view) char path[SIZEOF_STR]; char ref[SIZEOF_STR] = ""; - if (sq_quote(path, 0, opt_path) >= sizeof(path)) + if (sq_quote(path, 0, opt_file) >= sizeof(path)) return FALSE; if (*opt_ref && sq_quote(ref, 0, opt_ref) >= sizeof(ref)) @@ -3367,7 +3306,7 @@ blame_open(struct view *view) if (!string_format(view->cmd, BLAME_CAT_FILE_CMD, ref, path)) return FALSE; } else { - view->pipe = fopen(opt_path, "r"); + view->pipe = fopen(opt_file, "r"); if (!view->pipe && !string_format(view->cmd, BLAME_CAT_FILE_CMD, "HEAD", path)) return FALSE; @@ -3381,8 +3320,8 @@ blame_open(struct view *view) if (!string_format(view->cmd, BLAME_INCREMENTAL_CMD, ref, path)) return FALSE; - string_format(view->ref, "%s ...", opt_path); - string_copy_rev(view->vid, opt_path); + string_format(view->ref, "%s ...", opt_file); + string_copy_rev(view->vid, opt_file); set_nonblocking_input(TRUE); if (view->line) { @@ -3397,6 +3336,8 @@ blame_open(struct view *view) view->offset = view->lines = view->lineno = 0; view->line = NULL; view->start_time = time(NULL); + + return TRUE; } static struct blame_commit * @@ -3448,7 +3389,6 @@ parse_blame_commit(struct view *view, char *text, int *blamed) char *pos = text + SIZEOF_REV - 1; size_t lineno; size_t group; - struct line *line; if (strlen(text) <= SIZEOF_REV || *pos != ' ') return NULL; @@ -3784,6 +3724,7 @@ struct status { } new; }; +static char status_onbranch[SIZEOF_STR]; static struct status stage_status; static enum line_type stage_line_type; @@ -3962,7 +3903,13 @@ status_open(struct view *view) view->lines = view->line_alloc = view->line_size = view->lineno = 0; view->line = NULL; - if (!realloc_lines(view, view->line_size + 6)) + if (!realloc_lines(view, view->line_size + 7)) + return FALSE; + + add_line_data(view, NULL, LINE_STAT_HEAD); + if (!*opt_head) + string_copy(status_onbranch, "Not currently on any branch"); + else if (!string_format(status_onbranch, "On branch %s", opt_head)) return FALSE; if (!string_format(exclude, "%s/info/exclude", opt_git_dir)) @@ -3986,11 +3933,18 @@ status_open(struct view *view) return FALSE; /* If all went well restore the previous line number to stay in - * the context. */ + * the context or select a line with something that can be + * updated. */ + if (prev_lineno >= view->lines) + prev_lineno = view->lines - 1; + while (prev_lineno < view->lines && !view->line[prev_lineno].data) + prev_lineno++; + + /* If the above fails, always skip the "On branch" line. */ if (prev_lineno < view->lines) view->lineno = prev_lineno; else - view->lineno = view->lines - 1; + view->lineno = 1; return TRUE; } @@ -4008,6 +3962,10 @@ status_draw(struct view *view, struct line *line, unsigned int lineno, bool sele wchgat(view->win, -1, 0, LINE_CURSOR, NULL); tilde_attr = -1; + } else if (line->type == LINE_STAT_HEAD) { + wattrset(view->win, get_line_attr(LINE_STAT_HEAD)); + wchgat(view->win, -1, 0, LINE_STAT_HEAD, NULL); + } else if (!status && line->type != LINE_STAT_NONE) { wattrset(view->win, get_line_attr(LINE_STAT_SECTION)); wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL); @@ -4036,6 +3994,10 @@ status_draw(struct view *view, struct line *line, unsigned int lineno, bool sele text = " (no files)"; break; + case LINE_STAT_HEAD: + text = status_onbranch; + break; + default: return FALSE; } @@ -4120,6 +4082,9 @@ status_enter(struct view *view, struct line *line) info = "Untracked file %s"; break; + case LINE_STAT_HEAD: + return REQ_NONE; + default: die("line type %d not handled in switch", line->type); } @@ -4174,6 +4139,9 @@ status_update_file(struct view *view, struct status *status, enum line_type type string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin"); break; + case LINE_STAT_HEAD: + return TRUE; + default: die("line type %d not handled in switch", type); } @@ -4244,7 +4212,7 @@ status_request(struct view *view, enum request request, struct line *line) case REQ_VIEW_BLAME: if (status) { - string_copy(opt_path, status->new.name); + string_copy(opt_file, status->new.name); opt_ref[0] = 0; } return request; @@ -4296,6 +4264,7 @@ status_select(struct view *view, struct line *line) text = "Press %s to stage %s for addition"; break; + case LINE_STAT_HEAD: case LINE_STAT_NONE: text = "Nothing to update"; break; @@ -4503,7 +4472,7 @@ stage_request(struct view *view, enum request request, struct line *line) case REQ_VIEW_BLAME: if (stage_status.new.name[0]) { - string_copy(opt_path, stage_status.new.name); + string_copy(opt_file, stage_status.new.name); opt_ref[0] = 0; } return request; @@ -4825,6 +4794,8 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select do { if (type == LINE_CURSOR) ; + else if (commit->refs[i]->head) + wattrset(view->win, get_line_attr(LINE_MAIN_HEAD)); else if (commit->refs[i]->ltag) wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG)); else if (commit->refs[i]->tag) @@ -5437,6 +5408,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) bool ltag = FALSE; bool remote = FALSE; bool check_replace = FALSE; + bool head = FALSE; if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) { if (!strcmp(name + namelen - 3, "^{}")) { @@ -5460,6 +5432,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) { namelen -= STRING_SIZE("refs/heads/"); name += STRING_SIZE("refs/heads/"); + head = !strncmp(opt_head, name, namelen); } else if (!strcmp(name, "HEAD")) { return OK; @@ -5489,6 +5462,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) ref->tag = tag; ref->ltag = ltag; ref->remote = remote; + ref->head = head; string_copy_rev(ref->id, id); return OK; @@ -5536,20 +5510,36 @@ read_repo_info(char *name, size_t namelen, char *value, size_t valuelen) * Default to true for the unknown case. */ opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE; - } else { + } else if (opt_cdup[0] == ' ') { string_ncopy(opt_cdup, name, namelen); + } else { + if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) { + namelen -= STRING_SIZE("refs/heads/"); + name += STRING_SIZE("refs/heads/"); + string_ncopy(opt_head, name, namelen); + } } return OK; } -/* XXX: The line outputted by "--show-cdup" can be empty so the option - * must be the last one! */ static int load_repo_info(void) { - return read_properties(popen("git rev-parse --git-dir --is-inside-work-tree --show-cdup 2>/dev/null", "r"), - "=", read_repo_info); + int result; + FILE *pipe = popen("git rev-parse --git-dir --is-inside-work-tree " + " --show-cdup --symbolic-full-name HEAD 2>/dev/null", "r"); + + /* XXX: The line outputted by "--show-cdup" can be empty so + * initialize it to something invalid to make it possible to + * detect whether it has been set or not. */ + opt_cdup[0] = ' '; + + result = read_properties(pipe, "=", read_repo_info); + if (opt_cdup[0] == ' ') + opt_cdup[0] = 0; + + return result; } static int