X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/ab7a48a1c2dcfc89bc89f3bf28674d724bb58ee3..8b3475e63853b53144e06ee9f808f434ad1239d3:/tig.c?ds=sidebyside diff --git a/tig.c b/tig.c index a54e57a..6ec8a25 100644 --- a/tig.c +++ b/tig.c @@ -143,11 +143,12 @@ static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bo struct ref { char *name; /* Ref name; tag or head names are shortened. */ char id[SIZEOF_REV]; /* Commit SHA1 ID */ + unsigned int head:1; /* Is it the current HEAD? */ 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 tracked:1; /* Is it the remote for the current HEAD? */ 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); @@ -446,6 +447,7 @@ 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 char opt_remote[SIZEOF_REF] = ""; static bool opt_no_head = TRUE; static FILE *opt_pipe = NULL; static char opt_encoding[20] = "UTF-8"; @@ -466,6 +468,12 @@ parse_options(int argc, char *argv[]) bool seen_dashdash = FALSE; int i; + if (!isatty(STDIN_FILENO)) { + opt_request = REQ_VIEW_PAGER; + opt_pipe = stdin; + return TRUE; + } + if (argc <= 1) return TRUE; @@ -534,12 +542,6 @@ parse_options(int argc, char *argv[]) die("command too long"); } - if (!isatty(STDIN_FILENO)) { - opt_request = REQ_VIEW_PAGER; - opt_pipe = stdin; - buf_size = 0; - } - opt_cmd[buf_size] = 0; return TRUE; @@ -590,7 +592,8 @@ LINE(MAIN_AUTHOR, "", COLOR_GREEN, COLOR_DEFAULT, 0), \ LINE(MAIN_COMMIT, "", COLOR_DEFAULT, 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_REMOTE, "", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(MAIN_TRACKED, "", 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), \ @@ -1357,6 +1360,7 @@ struct view { struct view_ops *ops; /* View operations */ enum keymap keymap; /* What keymap does this view have */ + bool git_dir; /* Whether the view requires a git directory. */ char cmd[SIZEOF_STR]; /* Command buffer */ char ref[SIZEOF_REF]; /* Hovered commit reference */ @@ -1416,24 +1420,24 @@ static struct view_ops help_ops; static struct view_ops status_ops; static struct view_ops stage_ops; -#define VIEW_STR(name, cmd, env, ref, ops, map) \ - { name, cmd, #env, ref, ops, map} +#define VIEW_STR(name, cmd, env, ref, ops, map, git) \ + { name, cmd, #env, ref, ops, map, git } -#define VIEW_(id, name, ops, ref) \ - VIEW_STR(name, TIG_##id##_CMD, TIG_##id##_CMD, ref, ops, KEYMAP_##id) +#define VIEW_(id, name, ops, git, ref) \ + VIEW_STR(name, TIG_##id##_CMD, TIG_##id##_CMD, ref, ops, KEYMAP_##id, git) static struct view views[] = { - VIEW_(MAIN, "main", &main_ops, ref_head), - VIEW_(DIFF, "diff", &pager_ops, ref_commit), - VIEW_(LOG, "log", &pager_ops, ref_head), - VIEW_(TREE, "tree", &tree_ops, ref_commit), - VIEW_(BLOB, "blob", &blob_ops, ref_blob), - VIEW_(BLAME, "blame", &blame_ops, ref_commit), - VIEW_(HELP, "help", &help_ops, ""), - VIEW_(PAGER, "pager", &pager_ops, "stdin"), - VIEW_(STATUS, "status", &status_ops, ""), - VIEW_(STAGE, "stage", &stage_ops, ""), + VIEW_(MAIN, "main", &main_ops, TRUE, ref_head), + VIEW_(DIFF, "diff", &pager_ops, TRUE, ref_commit), + VIEW_(LOG, "log", &pager_ops, TRUE, ref_head), + VIEW_(TREE, "tree", &tree_ops, TRUE, ref_commit), + VIEW_(BLOB, "blob", &blob_ops, TRUE, ref_blob), + VIEW_(BLAME, "blame", &blame_ops, TRUE, ref_commit), + VIEW_(HELP, "help", &help_ops, FALSE, ""), + VIEW_(PAGER, "pager", &pager_ops, FALSE, "stdin"), + VIEW_(STATUS, "status", &status_ops, TRUE, ""), + VIEW_(STAGE, "stage", &stage_ops, TRUE, ""), }; #define VIEW(req) (&views[(req) - REQ_OFFSET - 1]) @@ -2278,15 +2282,8 @@ open_view(struct view *prev, enum request request, enum open_flags flags) return; } - if (view->ops->open) { - if (!view->ops->open(view)) { - report("Failed to load %s view", view->name); - return; - } - - } else if ((reload || strcmp(view->vid, view->id)) && - !begin_update(view)) { - report("Failed to load %s view", view->name); + if (view->git_dir && !opt_git_dir[0]) { + report("The %s view is disabled in pager view", view->name); return; } @@ -2307,6 +2304,18 @@ open_view(struct view *prev, enum request request, enum open_flags flags) (nviews == 1 && base_view != display[0])) resize_display(); + if (view->ops->open) { + if (!view->ops->open(view)) { + report("Failed to load %s view", view->name); + return; + } + + } else if ((reload || strcmp(view->vid, view->id)) && + !begin_update(view)) { + report("Failed to load %s view", view->name); + return; + } + if (split && prev->lineno - prev->offset >= prev->height) { /* Take the title line into account. */ int lines = prev->lineno - prev->offset - prev->height + 1; @@ -3414,9 +3423,9 @@ parse_blame_commit(struct view *view, char *text, int *blamed) blame = line->data; blame->commit = commit; + blame->header = !group; line->dirty = 1; } - blame->header = 1; return commit; } @@ -3964,6 +3973,8 @@ status_open(struct view *view) prev_lineno = view->lines - 1; while (prev_lineno < view->lines && !view->line[prev_lineno].data) prev_lineno++; + while (prev_lineno > 0 && !view->line[prev_lineno].data) + prev_lineno--; /* If the above fails, always skip the "On branch" line. */ if (prev_lineno < view->lines) @@ -3971,6 +3982,11 @@ status_open(struct view *view) else view->lineno = 1; + if (view->lineno < view->offset) + view->offset = view->lineno; + else if (view->offset + view->height <= view->lineno) + view->offset = view->lineno - view->height + 1; + return TRUE; } @@ -4136,61 +4152,110 @@ status_enter(struct view *view, struct line *line) } -static bool -status_update_file(struct view *view, struct status *status, enum line_type type) +static FILE * +status_update_prepare(enum line_type type) { char cmd[SIZEOF_STR]; - char buf[SIZEOF_STR]; size_t cmdsize = 0; - size_t bufsize = 0; - size_t written = 0; - FILE *pipe; if (opt_cdup[0] && type != LINE_STAT_UNTRACKED && !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup)) - return FALSE; + return NULL; + + switch (type) { + case LINE_STAT_STAGED: + string_add(cmd, cmdsize, "git update-index -z --index-info"); + break; + + case LINE_STAT_UNSTAGED: + case LINE_STAT_UNTRACKED: + string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin"); + break; + + default: + die("line type %d not handled in switch", type); + } + + return popen(cmd, "w"); +} + +static bool +status_update_write(FILE *pipe, struct status *status, enum line_type type) +{ + char buf[SIZEOF_STR]; + size_t bufsize = 0; + size_t written = 0; switch (type) { case LINE_STAT_STAGED: if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c", - status->old.mode, + status->old.mode, status->old.rev, status->old.name, 0)) return FALSE; - - string_add(cmd, cmdsize, "git update-index -z --index-info"); break; case LINE_STAT_UNSTAGED: case LINE_STAT_UNTRACKED: if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0)) return FALSE; - - 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); } - pipe = popen(cmd, "w"); - if (!pipe) - return FALSE; - while (!ferror(pipe) && written < bufsize) { written += fwrite(buf + written, 1, bufsize - written, pipe); } + return written == bufsize; +} + +static bool +status_update_file(struct status *status, enum line_type type) +{ + FILE *pipe = status_update_prepare(type); + bool result; + + if (!pipe) + return FALSE; + + result = status_update_write(pipe, status, type); pclose(pipe); + return result; +} + +static bool +status_update_files(struct view *view, struct line *line) +{ + FILE *pipe = status_update_prepare(line->type); + bool result = TRUE; + struct line *pos = view->line + view->lines; + int files = 0; + int file, done; - if (written != bufsize) + if (!pipe) return FALSE; - return TRUE; + for (pos = line; pos < view->line + view->lines && pos->data; pos++) + files++; + + for (file = 0, done = 0; result && file < files; line++, file++) { + int almost_done = file * 100 / files; + + if (almost_done > done) { + done = almost_done; + string_format(view->ref, "updating file %u of %u (%d%% done)", + file, files, done); + update_view_title(view); + } + result = status_update_write(pipe, line->data, line->type); + } + + pclose(pipe); + return result; } static bool @@ -4201,17 +4266,16 @@ status_update(struct view *view) assert(view->lines); if (!line->data) { - while (++line < view->line + view->lines && line->data) { - if (!status_update_file(view, line->data, line->type)) - report("Failed to update file status"); - } - - if (!line[-1].data) { + /* This should work even for the "On branch" line. */ + if (line < view->line + view->lines && !line[1].data) { report("Nothing to update"); return FALSE; } - } else if (!status_update_file(view, line->data, line->type)) { + if (!status_update_files(view, line + 1)) + report("Failed to update file status"); + + } else if (!status_update_file(line->data, line->type)) { report("Failed to update file status"); } @@ -4415,7 +4479,7 @@ stage_update_chunk(struct view *view, struct line *line) return FALSE; if (!string_format_from(cmd, &cmdsize, - "git apply --cached %s - && " + "git apply --whitespace=nowarn --cached %s - && " "git update-index -q --unmerged --refresh 2>/dev/null", stage_line_type == LINE_STAT_STAGED ? "-R" : "")) return FALSE; @@ -4477,7 +4541,7 @@ stage_update(struct view *view, struct line *line) return; } - } else if (!status_update_file(view, &stage_status, stage_line_type)) { + } else if (!status_update_file(&stage_status, stage_line_type)) { report("Failed to update file"); return; } @@ -4831,6 +4895,8 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select 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]->tracked) + wattrset(view->win, get_line_attr(LINE_MAIN_TRACKED)); else if (commit->refs[i]->remote) wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE)); else @@ -5438,6 +5504,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) bool tag = FALSE; bool ltag = FALSE; bool remote = FALSE; + bool tracked = FALSE; bool check_replace = FALSE; bool head = FALSE; @@ -5459,6 +5526,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) remote = TRUE; namelen -= STRING_SIZE("refs/remotes/"); name += STRING_SIZE("refs/remotes/"); + tracked = !strcmp(opt_remote, name); } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) { namelen -= STRING_SIZE("refs/heads/"); @@ -5491,10 +5559,11 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) strncpy(ref->name, name, namelen); ref->name[namelen] = 0; + ref->head = head; ref->tag = tag; ref->ltag = ltag; ref->remote = remote; - ref->head = head; + ref->tracked = tracked; string_copy_rev(ref->id, id); return OK; @@ -5518,6 +5587,28 @@ read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen if (!strcmp(name, "core.editor")) string_ncopy(opt_editor, value, valuelen); + /* branch..remote */ + if (*opt_head && + !strncmp(name, "branch.", 7) && + !strncmp(name + 7, opt_head, strlen(opt_head)) && + !strcmp(name + 7 + strlen(opt_head), ".remote")) + string_ncopy(opt_remote, value, valuelen); + + if (*opt_head && *opt_remote && + !strncmp(name, "branch.", 7) && + !strncmp(name + 7, opt_head, strlen(opt_head)) && + !strcmp(name + 7 + strlen(opt_head), ".merge")) { + size_t from = strlen(opt_remote); + + if (!strncmp(value, "refs/heads/", STRING_SIZE("refs/heads/"))) { + value += STRING_SIZE("refs/heads/"); + valuelen -= STRING_SIZE("refs/heads/"); + } + + if (!string_format_from(opt_remote, &from, "/%s", value)) + opt_remote[0] = 0; + } + return OK; } @@ -5686,7 +5777,7 @@ main(int argc, char *argv[]) return 0; /* Require a git repository unless when running in pager mode. */ - if (!opt_git_dir[0]) + if (!opt_git_dir[0] && opt_request != REQ_VIEW_PAGER) die("Not a git repository"); if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))