X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/810f007894cd333324da29f962a9d5eadbc60150..dec7b4378b549a214eb5feea77a0855d16893db1:/tig.c diff --git a/tig.c b/tig.c index 4150696..f7e9714 100644 --- a/tig.c +++ b/tig.c @@ -336,7 +336,8 @@ 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_REV_GRAPH, "Toggle revision graph visualization") + REQ_(TOGGLE_REV_GRAPH, "Toggle revision graph visualization"), \ + REQ_(STATUS_UPDATE, "Update file status") \ /* User action requests. */ @@ -499,7 +500,7 @@ parse_options(int argc, char *argv[]) if (!strcmp(opt, "-S")) { opt_request = REQ_VIEW_STATUS; - break; + continue; } if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) { @@ -765,6 +766,7 @@ static struct keybinding default_keybindings[] = { { '.', REQ_TOGGLE_LINENO }, { 'g', REQ_TOGGLE_REV_GRAPH }, { ':', REQ_PROMPT }, + { 'u', REQ_STATUS_UPDATE }, /* Using the ncurses SIGWINCH handler. */ { KEY_RESIZE, REQ_SCREEN_RESIZE }, @@ -1153,7 +1155,7 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) if (status == ERR) { fprintf(stderr, "Error on line %d, near '%.*s': %s\n", - config_lineno, optlen, opt, config_msg); + config_lineno, (int) optlen, opt, config_msg); config_errors = TRUE; } @@ -1261,8 +1263,8 @@ struct view_ops { bool (*read)(struct view *view, char *data); /* Draw one line; @lineno must be < view->height. */ bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected); - /* Depending on view, change display based on current line. */ - bool (*enter)(struct view *view, struct line *line); + /* Depending on view handle a special requests. */ + enum request (*request)(struct view *view, enum request request, struct line *line); /* Search for regex in a line. */ bool (*grep)(struct view *view, struct line *line); /* Select line */ @@ -1289,8 +1291,8 @@ static struct view views[] = { VIEW_(LOG, "log", &pager_ops, ref_head), VIEW_(TREE, "tree", &tree_ops, ref_commit), VIEW_(BLOB, "blob", &blob_ops, ref_blob), - VIEW_(HELP, "help", &help_ops, ""), - VIEW_(PAGER, "pager", &pager_ops, ""), + VIEW_(HELP, "help", &help_ops, ""), + VIEW_(PAGER, "pager", &pager_ops, "stdin"), VIEW_(STATUS, "status", &status_ops, ""), }; @@ -1366,7 +1368,7 @@ update_view_title(struct view *view) assert(view_is_displayed(view)); - if (view->lines || view->pipe) { + if (!VIEW(REQ_VIEW_STATUS) && (view->lines || view->pipe)) { unsigned int view_lines = view->offset + view->height; unsigned int lines = view->lines ? MIN(view_lines, view->lines) * 100 / view->lines @@ -1394,7 +1396,7 @@ update_view_title(struct view *view) if (minsize < view->width) refsize = view->width - minsize + 7; - string_format_from(buf, &bufpos, " %.*s", refsize, view->ref); + string_format_from(buf, &bufpos, " %.*s", (int) refsize, view->ref); } if (statelen && bufpos < view->width) { @@ -2125,6 +2127,12 @@ view_driver(struct view *view, enum request request) { int i; + if (view && view->lines) { + request = view->ops->request(view, request, &view->line[view->lineno]); + if (request == REQ_NONE) + return TRUE; + } + switch (request) { case REQ_MOVE_UP: case REQ_MOVE_DOWN: @@ -2152,7 +2160,7 @@ view_driver(struct view *view, enum request request) break; case REQ_VIEW_PAGER: - if (!VIEW(REQ_VIEW_PAGER)->lines) { + if (!opt_pipe && !VIEW(REQ_VIEW_PAGER)->lines) { report("No pager content, press %s to run command from prompt", get_key(REQ_PROMPT)); break; @@ -2177,22 +2185,21 @@ view_driver(struct view *view, enum request request) view->parent == VIEW(REQ_VIEW_MAIN)) || (view == VIEW(REQ_VIEW_BLOB) && view->parent == VIEW(REQ_VIEW_TREE))) { + int line; + view = view->parent; + line = view->lineno; move_view(view, request); if (view_is_displayed(view)) update_view_title(view); + if (line == view->lineno) + break; } else { move_view(view, request); break; } /* Fall-through */ - case REQ_ENTER: - if (!view->lines) { - report("Nothing to enter"); - break; - } - return view->ops->enter(view, &view->line[view->lineno]); case REQ_VIEW_NEXT: { @@ -2255,6 +2262,7 @@ view_driver(struct view *view, enum request request) redraw_display(); break; + case REQ_NONE: doupdate(); return TRUE; @@ -2456,11 +2464,14 @@ pager_read(struct view *view, char *data) return TRUE; } -static bool -pager_enter(struct view *view, struct line *line) +static enum request +pager_request(struct view *view, enum request request, struct line *line) { int split = 0; + if (request != REQ_ENTER) + return request; + if (line->type == LINE_COMMIT && (view == VIEW(REQ_VIEW_LOG) || view == VIEW(REQ_VIEW_PAGER))) { @@ -2479,7 +2490,7 @@ pager_enter(struct view *view, struct line *line) if (split) update_view_title(view); - return TRUE; + return REQ_NONE; } static bool @@ -2514,7 +2525,7 @@ static struct view_ops pager_ops = { NULL, pager_read, pager_draw, - pager_enter, + pager_request, pager_grep, pager_select, }; @@ -2568,7 +2579,7 @@ static struct view_ops help_ops = { help_open, NULL, pager_draw, - pager_enter, + pager_request, pager_grep, pager_select, }; @@ -2681,11 +2692,13 @@ tree_read(struct view *view, char *text) return TRUE; } -static bool -tree_enter(struct view *view, struct line *line) +static enum request +tree_request(struct view *view, enum request request, struct line *line) { enum open_flags flags; - enum request request; + + if (request != REQ_ENTER) + return request; switch (line->type) { case LINE_TREE_DIR: @@ -2729,7 +2742,7 @@ tree_enter(struct view *view, struct line *line) open_view(view, request, flags); - return TRUE; + return REQ_NONE; } static void @@ -2752,7 +2765,7 @@ static struct view_ops tree_ops = { NULL, tree_read, pager_draw, - tree_enter, + tree_request, pager_grep, tree_select, }; @@ -2768,7 +2781,7 @@ static struct view_ops blob_ops = { NULL, blob_read, pager_draw, - pager_enter, + pager_request, pager_grep, pager_select, }; @@ -2907,6 +2920,9 @@ error_out: #define STATUS_LIST_OTHER_CMD \ "git ls-files -z --others --exclude-per-directory=.gitignore" +#define STATUS_DIFF_SHOW_CMD \ + "git diff --root --patch-with-stat --find-copies-harder -B -C %s -- %s 2>/dev/null" + /* First parse staged info using git-diff-index(1), then parse unstaged * info using git-diff-files(1), and finally untracked files using * git-ls-files(1). */ @@ -3004,10 +3020,88 @@ status_draw(struct view *view, struct line *line, unsigned int lineno, bool sele return TRUE; } -static bool -status_enter(struct view *view, struct line *line) +static void status_update(struct view *view); + +static enum request +status_request(struct view *view, enum request request, struct line *line) { struct status *status = line->data; + char path[SIZEOF_STR] = ""; + char *info; + size_t cmdsize = 0; + + if (request == REQ_STATUS_UPDATE) { + status_update(view); + return REQ_NONE; + } + + if (request != REQ_ENTER) + return request; + + if (line->type == LINE_STAT_NONE || + (!status && line[1].type == LINE_STAT_NONE)) { + report("No file to diff"); + return REQ_NONE; + } + + if (status && sq_quote(path, 0, status->name) >= sizeof(path)) + return REQ_QUIT; + + if (opt_cdup[0] && + line->type != LINE_STAT_UNTRACKED && + !string_format_from(opt_cmd, &cmdsize, "cd %s;", opt_cdup)) + return REQ_QUIT; + + switch (line->type) { + case LINE_STAT_STAGED: + if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD, + "--cached", path)) + return REQ_QUIT; + if (status) + info = "Staged changes to %s"; + else + info = "Staged changes"; + break; + + case LINE_STAT_UNSTAGED: + if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD, + "", path)) + return REQ_QUIT; + if (status) + info = "Unstaged changes to %s"; + else + info = "Unstaged changes"; + break; + + case LINE_STAT_UNTRACKED: + if (opt_pipe) + return REQ_QUIT; + + + if (!status) { + report("No file to show"); + return REQ_NONE; + } + + opt_pipe = fopen(status->name, "r"); + info = "Untracked file %s"; + break; + + default: + die("w00t"); + } + + open_view(view, REQ_VIEW_DIFF, OPEN_RELOAD | OPEN_SPLIT); + if (view_is_displayed(VIEW(REQ_VIEW_DIFF))) { + string_format(VIEW(REQ_VIEW_DIFF)->ref, info, status->name); + } + + return REQ_NONE; +} + +static bool +status_update_file(struct view *view, struct status *status, enum line_type type) +{ char cmd[SIZEOF_STR]; char buf[SIZEOF_STR]; size_t cmdsize = 0; @@ -3015,15 +3109,12 @@ status_enter(struct view *view, struct line *line) size_t written = 0; FILE *pipe; - if (!status) - return TRUE; - if (opt_cdup[0] && - line->type != LINE_STAT_UNTRACKED && + type != LINE_STAT_UNTRACKED && !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup)) return FALSE; - switch (line->type) { + switch (type) { case LINE_STAT_STAGED: if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c", status->old.mode, @@ -3059,36 +3150,69 @@ status_enter(struct view *view, struct line *line) if (written != bufsize) return FALSE; - open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD); return TRUE; } static void +status_update(struct view *view) +{ + struct line *line = &view->line[view->lineno]; + + 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) { + report("Nothing to update"); + return; + } + + } else if (!status_update_file(view, line->data, line->type)) { + report("Failed to update file status"); + } + + open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD); +} + +static void status_select(struct view *view, struct line *line) { + struct status *status = line->data; + char file[SIZEOF_STR] = "all files"; char *text; + if (status && !string_format(file, "'%s'", status->name)) + return; + + if (!status && line[1].type == LINE_STAT_NONE) + line++; + switch (line->type) { case LINE_STAT_STAGED: - text = "Press Enter to unstage file for commit"; + text = "Press %s to unstage %s for commit"; break; case LINE_STAT_UNSTAGED: - text = "Press Enter to stage file for commit "; + text = "Press %s to stage %s for commit"; break; case LINE_STAT_UNTRACKED: - text = "Press Enter to stage file for addition"; + text = "Press %s to stage %s for addition"; break; case LINE_STAT_NONE: - return; + text = "Nothing to update"; + break; default: die("w00t"); } - string_ncopy(view->ref, text, strlen(text)); + string_format(view->ref, text, get_key(REQ_STATUS_UPDATE), file); } static bool @@ -3128,7 +3252,7 @@ static struct view_ops status_ops = { status_open, NULL, status_draw, - status_enter, + status_request, status_grep, status_select, }; @@ -3560,13 +3684,17 @@ main_read(struct view *view, char *line) return TRUE; } -static bool -main_enter(struct view *view, struct line *line) +static enum request +main_request(struct view *view, enum request request, struct line *line) { enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; - open_view(view, REQ_VIEW_DIFF, flags); - return TRUE; + if (request == REQ_ENTER) + open_view(view, REQ_VIEW_DIFF, flags); + else + return request; + + return REQ_NONE; } static bool @@ -3614,7 +3742,7 @@ static struct view_ops main_ops = { NULL, main_read, main_draw, - main_enter, + main_request, main_grep, main_select, }; @@ -4089,7 +4217,7 @@ read_repo_info(char *name, size_t namelen, char *value, size_t valuelen) static int load_repo_info(void) { - return read_properties(popen("git rev-parse --git-dir --show-cdup", "r"), + return read_properties(popen("git rev-parse --git-dir --show-cdup 2>/dev/null", "r"), "=", read_repo_info); } @@ -4178,6 +4306,13 @@ main(int argc, char *argv[]) string_ncopy(opt_codeset, codeset, strlen(codeset)); } + if (load_repo_info() == ERR) + die("Failed to load repo info."); + + /* Require a git repository unless when running in pager mode. */ + if (!opt_git_dir[0]) + die("Not a git repository"); + if (load_options() == ERR) die("Failed to load user config."); @@ -4186,9 +4321,6 @@ main(int argc, char *argv[]) if (load_repo_config() == ERR) die("Failed to load repo config."); - if (load_repo_info() == ERR) - die("Failed to load repo info."); - if (!parse_options(argc, argv)) return 0; @@ -4201,10 +4333,6 @@ main(int argc, char *argv[]) if (load_refs() == ERR) die("Failed to load refs."); - /* Require a git repository unless when running in pager mode. */ - if (refs_size == 0 && opt_request != REQ_VIEW_PAGER) - die("Not a git repository"); - for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++) view->cmd_env = getenv(view->cmd_env);