diff options
Diffstat (limited to 'wt-status.c')
-rw-r--r-- | wt-status.c | 297 |
1 files changed, 212 insertions, 85 deletions
diff --git a/wt-status.c b/wt-status.c index 29666d0dba..1ea2ebe4c0 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1,5 +1,4 @@ #include "cache.h" -#include "pathspec.h" #include "wt-status.h" #include "object.h" #include "dir.h" @@ -585,6 +584,8 @@ static void wt_status_collect_untracked(struct wt_status *s) DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; if (s->show_ignored_files) dir.flags |= DIR_SHOW_IGNORED_TOO; + else + dir.untracked = the_index.untracked; setup_standard_excludes(&dir); fill_directory(&dir, &s->pathspec); @@ -729,7 +730,6 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt struct strbuf cmd_stdout = STRBUF_INIT; struct strbuf summary = STRBUF_INIT; char *summary_content; - size_t len; argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s", s->index_file); @@ -745,15 +745,11 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt sm_summary.git_cmd = 1; sm_summary.no_stdin = 1; - fflush(s->fp); - sm_summary.out = -1; - - run_command(&sm_summary); - len = strbuf_read(&cmd_stdout, sm_summary.out, 1024); + capture_command(&sm_summary, &cmd_stdout, 1024); /* prepend header, only if there's an actual output */ - if (len) { + if (cmd_stdout.len) { if (uncommitted) strbuf_addstr(&summary, _("Submodules changed but not updated:")); else @@ -764,6 +760,7 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt strbuf_release(&cmd_stdout); if (s->display_comment_prefix) { + size_t len; summary_content = strbuf_detach(&summary, &len); strbuf_add_commented_lines(&summary, summary_content, len); free(summary_content); @@ -827,10 +824,11 @@ void wt_status_truncate_message_at_cut_line(struct strbuf *buf) const char *p; struct strbuf pattern = STRBUF_INIT; - strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line); - p = strstr(buf->buf, pattern.buf); - if (p && (p == buf->buf || p[-1] == '\n')) - strbuf_setlen(buf, p - buf->buf); + strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line); + if (starts_with(buf->buf, pattern.buf + 1)) + strbuf_setlen(buf, 0); + else if ((p = strstr(buf->buf, pattern.buf))) + strbuf_setlen(buf, p - buf->buf + 1); strbuf_release(&pattern); } @@ -849,6 +847,8 @@ static void wt_status_print_verbose(struct wt_status *s) { struct rev_info rev; struct setup_revision_opt opt; + int dirty_submodules; + const char *c = color(WT_STATUS_HEADER, s); init_revisions(&rev, NULL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); @@ -873,21 +873,39 @@ static void wt_status_print_verbose(struct wt_status *s) rev.diffopt.use_color = 0; wt_status_add_cut_line(s->fp); } + if (s->verbose > 1 && s->commitable) { + /* print_updated() printed a header, so do we */ + if (s->fp != stdout) + wt_status_print_trailer(s); + status_printf_ln(s, c, _("Changes to be committed:")); + rev.diffopt.a_prefix = "c/"; + rev.diffopt.b_prefix = "i/"; + } /* else use prefix as per user config */ run_diff_index(&rev, 1); + if (s->verbose > 1 && + wt_status_check_worktree_changes(s, &dirty_submodules)) { + status_printf_ln(s, c, + "--------------------------------------------------"); + status_printf_ln(s, c, _("Changes not staged for commit:")); + setup_work_tree(); + rev.diffopt.a_prefix = "i/"; + rev.diffopt.b_prefix = "w/"; + run_diff_files(&rev, 0); + } } static void wt_status_print_tracking(struct wt_status *s) { struct strbuf sb = STRBUF_INIT; - const char *cp, *ep; + const char *cp, *ep, *branch_name; struct branch *branch; char comment_line_string[3]; int i; assert(s->branch && !s->is_initial); - if (!starts_with(s->branch, "refs/heads/")) + if (!skip_prefix(s->branch, "refs/heads/", &branch_name)) return; - branch = branch_get(s->branch + 11); + branch = branch_get(branch_name); if (!format_tracking_info(branch, &sb)) return; @@ -970,7 +988,7 @@ static char *read_line_from_git_path(const char *filename) strbuf_release(&buf); return NULL; } - strbuf_getline(&buf, fp, '\n'); + strbuf_getline_lf(&buf, fp); if (!fclose(fp)) { return strbuf_detach(&buf, NULL); } else { @@ -1008,21 +1026,140 @@ static int split_commit_in_progress(struct wt_status *s) return split_in_progress; } +/* + * Turn + * "pick d6a2f0303e897ec257dd0e0a39a5ccb709bc2047 some message" + * into + * "pick d6a2f03 some message" + * + * The function assumes that the line does not contain useless spaces + * before or after the command. + */ +static void abbrev_sha1_in_line(struct strbuf *line) +{ + struct strbuf **split; + int i; + + if (starts_with(line->buf, "exec ") || + starts_with(line->buf, "x ")) + return; + + split = strbuf_split_max(line, ' ', 3); + if (split[0] && split[1]) { + unsigned char sha1[20]; + const char *abbrev; + + /* + * strbuf_split_max left a space. Trim it and re-add + * it after abbreviation. + */ + strbuf_trim(split[1]); + if (!get_sha1(split[1]->buf, sha1)) { + abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV); + strbuf_reset(split[1]); + strbuf_addf(split[1], "%s ", abbrev); + strbuf_reset(line); + for (i = 0; split[i]; i++) + strbuf_addf(line, "%s", split[i]->buf); + } + } + strbuf_list_free(split); +} + +static void read_rebase_todolist(const char *fname, struct string_list *lines) +{ + struct strbuf line = STRBUF_INIT; + FILE *f = fopen(git_path("%s", fname), "r"); + + if (!f) + die_errno("Could not open file %s for reading", + git_path("%s", fname)); + while (!strbuf_getline_lf(&line, f)) { + if (line.len && line.buf[0] == comment_line_char) + continue; + strbuf_trim(&line); + if (!line.len) + continue; + abbrev_sha1_in_line(&line); + string_list_append(lines, line.buf); + } +} + +static void show_rebase_information(struct wt_status *s, + struct wt_status_state *state, + const char *color) +{ + if (state->rebase_interactive_in_progress) { + int i; + int nr_lines_to_show = 2; + + struct string_list have_done = STRING_LIST_INIT_DUP; + struct string_list yet_to_do = STRING_LIST_INIT_DUP; + + read_rebase_todolist("rebase-merge/done", &have_done); + read_rebase_todolist("rebase-merge/git-rebase-todo", &yet_to_do); + + if (have_done.nr == 0) + status_printf_ln(s, color, _("No commands done.")); + else { + status_printf_ln(s, color, + Q_("Last command done (%d command done):", + "Last commands done (%d commands done):", + have_done.nr), + have_done.nr); + for (i = (have_done.nr > nr_lines_to_show) + ? have_done.nr - nr_lines_to_show : 0; + i < have_done.nr; + i++) + status_printf_ln(s, color, " %s", have_done.items[i].string); + if (have_done.nr > nr_lines_to_show && s->hints) + status_printf_ln(s, color, + _(" (see more in file %s)"), git_path("rebase-merge/done")); + } + + if (yet_to_do.nr == 0) + status_printf_ln(s, color, + _("No commands remaining.")); + else { + status_printf_ln(s, color, + Q_("Next command to do (%d remaining command):", + "Next commands to do (%d remaining commands):", + yet_to_do.nr), + yet_to_do.nr); + for (i = 0; i < nr_lines_to_show && i < yet_to_do.nr; i++) + status_printf_ln(s, color, " %s", yet_to_do.items[i].string); + if (s->hints) + status_printf_ln(s, color, + _(" (use \"git rebase --edit-todo\" to view and edit)")); + } + string_list_clear(&yet_to_do, 0); + string_list_clear(&have_done, 0); + } +} + +static void print_rebase_state(struct wt_status *s, + struct wt_status_state *state, + const char *color) +{ + if (state->branch) + status_printf_ln(s, color, + _("You are currently rebasing branch '%s' on '%s'."), + state->branch, + state->onto); + else + status_printf_ln(s, color, + _("You are currently rebasing.")); +} + static void show_rebase_in_progress(struct wt_status *s, struct wt_status_state *state, const char *color) { struct stat st; + show_rebase_information(s, state, color); if (has_unmerged(s)) { - if (state->branch) - status_printf_ln(s, color, - _("You are currently rebasing branch '%s' on '%s'."), - state->branch, - state->onto); - else - status_printf_ln(s, color, - _("You are currently rebasing.")); + print_rebase_state(s, state, color); if (s->hints) { status_printf_ln(s, color, _(" (fix conflicts and then run \"git rebase --continue\")")); @@ -1031,15 +1168,8 @@ static void show_rebase_in_progress(struct wt_status *s, status_printf_ln(s, color, _(" (use \"git rebase --abort\" to check out the original branch)")); } - } else if (state->rebase_in_progress || !stat(git_path("MERGE_MSG"), &st)) { - if (state->branch) - status_printf_ln(s, color, - _("You are currently rebasing branch '%s' on '%s'."), - state->branch, - state->onto); - else - status_printf_ln(s, color, - _("You are currently rebasing.")); + } else if (state->rebase_in_progress || !stat(git_path_merge_msg(), &st)) { + print_rebase_state(s, state, color); if (s->hints) status_printf_ln(s, color, _(" (all conflicts fixed: run \"git rebase --continue\")")); @@ -1136,6 +1266,7 @@ static char *read_and_strip_branch(const char *path) { struct strbuf sb = STRBUF_INIT; unsigned char sha1[20]; + const char *branch_name; if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0) goto got_nothing; @@ -1144,8 +1275,8 @@ static char *read_and_strip_branch(const char *path) strbuf_setlen(&sb, sb.len - 1); if (!sb.len) goto got_nothing; - if (starts_with(sb.buf, "refs/heads/")) - strbuf_remove(&sb,0, strlen("refs/heads/")); + if (skip_prefix(sb.buf, "refs/heads/", &branch_name)) + strbuf_remove(&sb, 0, branch_name - sb.buf); else if (starts_with(sb.buf, "refs/")) ; else if (!get_sha1_hex(sb.buf, sha1)) { @@ -1176,18 +1307,22 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, struct grab_1st_switch_cbdata *cb = cb_data; const char *target = NULL, *end; - if (!starts_with(message, "checkout: moving from ")) + if (!skip_prefix(message, "checkout: moving from ", &message)) return 0; - message += strlen("checkout: moving from "); target = strstr(message, " to "); if (!target) return 0; target += strlen(" to "); strbuf_reset(&cb->buf); hashcpy(cb->nsha1, nsha1); - for (end = target; *end && *end != '\n'; end++) - ; + end = strchrnul(target, '\n'); strbuf_add(&cb->buf, target, end - target); + if (!strcmp(cb->buf.buf, "HEAD")) { + /* HEAD is relative. Resolve it to the right reflog entry. */ + strbuf_reset(&cb->buf); + strbuf_addstr(&cb->buf, + find_unique_abbrev(nsha1, DEFAULT_ABBREV)); + } return 1; } @@ -1209,19 +1344,17 @@ static void wt_status_get_detached_from(struct wt_status_state *state) (!hashcmp(cb.nsha1, sha1) || /* perhaps sha1 is a tag, try to dereference to a commit */ ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL && - !hashcmp(cb.nsha1, commit->object.sha1)))) { - int ofs; - if (starts_with(ref, "refs/tags/")) - ofs = strlen("refs/tags/"); - else if (starts_with(ref, "refs/remotes/")) - ofs = strlen("refs/remotes/"); - else - ofs = 0; - state->detached_from = xstrdup(ref + ofs); + !hashcmp(cb.nsha1, commit->object.oid.hash)))) { + const char *from = ref; + if (!skip_prefix(from, "refs/tags/", &from)) + skip_prefix(from, "refs/remotes/", &from); + state->detached_from = xstrdup(from); } else state->detached_from = xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV)); hashcpy(state->detached_sha1, cb.nsha1); + state->detached_at = !get_sha1("HEAD", sha1) && + !hashcmp(sha1, state->detached_sha1); free(ref); strbuf_release(&cb.buf); @@ -1233,7 +1366,7 @@ void wt_status_get_state(struct wt_status_state *state, struct stat st; unsigned char sha1[20]; - if (!stat(git_path("MERGE_HEAD"), &st)) { + if (!stat(git_path_merge_head(), &st)) { state->merge_in_progress = 1; } else if (!stat(git_path("rebase-apply"), &st)) { if (!stat(git_path("rebase-apply/applying"), &st)) { @@ -1252,7 +1385,7 @@ void wt_status_get_state(struct wt_status_state *state, state->rebase_in_progress = 1; state->branch = read_and_strip_branch("rebase-merge/head-name"); state->onto = read_and_strip_branch("rebase-merge/onto"); - } else if (!stat(git_path("CHERRY_PICK_HEAD"), &st) && + } else if (!stat(git_path_cherry_pick_head(), &st) && !get_sha1("CHERRY_PICK_HEAD", sha1)) { state->cherry_pick_in_progress = 1; hashcpy(state->cherry_pick_head_sha1, sha1); @@ -1261,7 +1394,7 @@ void wt_status_get_state(struct wt_status_state *state, state->bisect_in_progress = 1; state->branch = read_and_strip_branch("BISECT_START"); } - if (!stat(git_path("REVERT_HEAD"), &st) && + if (!stat(git_path_revert_head(), &st) && !get_sha1("REVERT_HEAD", sha1)) { state->revert_in_progress = 1; hashcpy(state->revert_head_sha1, sha1); @@ -1302,18 +1435,17 @@ void wt_status_print(struct wt_status *s) if (s->branch) { const char *on_what = _("On branch "); const char *branch_name = s->branch; - if (starts_with(branch_name, "refs/heads/")) - branch_name += 11; - else if (!strcmp(branch_name, "HEAD")) { + if (!strcmp(branch_name, "HEAD")) { branch_status_color = color(WT_STATUS_NOBRANCH, s); if (state.rebase_in_progress || state.rebase_interactive_in_progress) { - on_what = _("rebase in progress; onto "); + if (state.rebase_interactive_in_progress) + on_what = _("interactive rebase in progress; onto "); + else + on_what = _("rebase in progress; onto "); branch_name = state.onto; } else if (state.detached_from) { - unsigned char sha1[20]; branch_name = state.detached_from; - if (!get_sha1("HEAD", sha1) && - !hashcmp(sha1, state.detached_sha1)) + if (state.detached_at) on_what = _("HEAD detached at "); else on_what = _("HEAD detached from "); @@ -1321,7 +1453,8 @@ void wt_status_print(struct wt_status *s) branch_name = ""; on_what = _("Not currently on any branch."); } - } + } else + skip_prefix(branch_name, "refs/heads/", &branch_name); status_printf(s, color(WT_STATUS_HEADER, s), "%s", ""); status_printf_more(s, branch_status_color, "%s", on_what); status_printf_more(s, branch_color, "%s\n", branch_name); @@ -1503,42 +1636,35 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) return; branch_name = s->branch; - if (starts_with(branch_name, "refs/heads/")) - branch_name += 11; - else if (!strcmp(branch_name, "HEAD")) { - branch_name = _("HEAD (no branch)"); - branch_color_local = color(WT_STATUS_NOBRANCH, s); - } - - branch = branch_get(s->branch + 11); if (s->is_initial) color_fprintf(s->fp, header_color, _("Initial commit on ")); + if (!strcmp(s->branch, "HEAD")) { + color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s", + _("HEAD (no branch)")); + goto conclude; + } + + skip_prefix(branch_name, "refs/heads/", &branch_name); + + branch = branch_get(branch_name); + color_fprintf(s->fp, branch_color_local, "%s", branch_name); - switch (stat_tracking_info(branch, &num_ours, &num_theirs)) { - case 0: - /* no base */ - fputc(s->null_termination ? '\0' : '\n', s->fp); - return; - case -1: - /* with "gone" base */ + if (stat_tracking_info(branch, &num_ours, &num_theirs, &base) < 0) { + if (!base) + goto conclude; + upstream_is_gone = 1; - break; - default: - /* with base */ - break; } - base = branch->merge[0]->dst; base = shorten_unambiguous_ref(base, 0); color_fprintf(s->fp, header_color, "..."); color_fprintf(s->fp, branch_color_remote, "%s", base); + free((char *)base); - if (!upstream_is_gone && !num_ours && !num_theirs) { - fputc(s->null_termination ? '\0' : '\n', s->fp); - return; - } + if (!upstream_is_gone && !num_ours && !num_theirs) + goto conclude; #define LABEL(string) (s->no_gettext ? (string) : _(string)) @@ -1549,16 +1675,17 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) color_fprintf(s->fp, header_color, LABEL(N_("behind "))); color_fprintf(s->fp, branch_color_remote, "%d", num_theirs); } else if (!num_theirs) { - color_fprintf(s->fp, header_color, LABEL(N_(("ahead ")))); + color_fprintf(s->fp, header_color, LABEL(N_("ahead "))); color_fprintf(s->fp, branch_color_local, "%d", num_ours); } else { - color_fprintf(s->fp, header_color, LABEL(N_(("ahead ")))); + color_fprintf(s->fp, header_color, LABEL(N_("ahead "))); color_fprintf(s->fp, branch_color_local, "%d", num_ours); color_fprintf(s->fp, header_color, ", %s", LABEL(N_("behind "))); color_fprintf(s->fp, branch_color_remote, "%d", num_theirs); } color_fprintf(s->fp, header_color, "]"); + conclude: fputc(s->null_termination ? '\0' : '\n', s->fp); } |