diff options
Diffstat (limited to 'wt-status.c')
-rw-r--r-- | wt-status.c | 544 |
1 files changed, 365 insertions, 179 deletions
diff --git a/wt-status.c b/wt-status.c index a93e8201bf..ef7486474a 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" @@ -15,8 +14,9 @@ #include "submodule.h" #include "column.h" #include "strbuf.h" +#include "utf8.h" -static char cut_line[] = +static const char cut_line[] = "------------------------ >8 ------------------------\n"; static char default_wt_status_colors[][COLOR_MAXLEN] = { @@ -127,7 +127,7 @@ void wt_status_prepare(struct wt_status *s) s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; s->use_color = -1; s->relative_paths = 1; - s->branch = resolve_refdup("HEAD", sha1, 0, NULL); + s->branch = resolve_refdup("HEAD", 0, sha1, NULL); s->reference = "HEAD"; s->fp = stdout; s->index_file = get_index_file(); @@ -187,7 +187,7 @@ static void wt_status_print_unmerged_header(struct wt_status *s) } else { status_printf_ln(s, c, _(" (use \"git add/rm <file>...\" as appropriate to mark resolution)")); } - status_printf_ln(s, c, ""); + status_printf_ln(s, c, "%s", ""); } static void wt_status_print_cached_header(struct wt_status *s) @@ -203,7 +203,7 @@ static void wt_status_print_cached_header(struct wt_status *s) status_printf_ln(s, c, _(" (use \"git reset %s <file>...\" to unstage)"), s->reference); else status_printf_ln(s, c, _(" (use \"git rm --cached <file>...\" to unstage)")); - status_printf_ln(s, c, ""); + status_printf_ln(s, c, "%s", ""); } static void wt_status_print_dirty_header(struct wt_status *s, @@ -222,7 +222,7 @@ static void wt_status_print_dirty_header(struct wt_status *s, status_printf_ln(s, c, _(" (use \"git checkout -- <file>...\" to discard changes in working directory)")); if (has_dirty_submodules) status_printf_ln(s, c, _(" (commit or discard the untracked or modified content in submodules)")); - status_printf_ln(s, c, ""); + status_printf_ln(s, c, "%s", ""); } static void wt_status_print_other_header(struct wt_status *s, @@ -234,36 +234,99 @@ static void wt_status_print_other_header(struct wt_status *s, if (!s->hints) return; status_printf_ln(s, c, _(" (use \"git %s <file>...\" to include in what will be committed)"), how); - status_printf_ln(s, c, ""); + status_printf_ln(s, c, "%s", ""); } static void wt_status_print_trailer(struct wt_status *s) { - status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); + status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); } #define quote_path quote_path_relative +static const char *wt_status_unmerged_status_string(int stagemask) +{ + switch (stagemask) { + case 1: + return _("both deleted:"); + case 2: + return _("added by us:"); + case 3: + return _("deleted by them:"); + case 4: + return _("added by them:"); + case 5: + return _("deleted by us:"); + case 6: + return _("both added:"); + case 7: + return _("both modified:"); + default: + die(_("bug: unhandled unmerged status %x"), stagemask); + } +} + +static const char *wt_status_diff_status_string(int status) +{ + switch (status) { + case DIFF_STATUS_ADDED: + return _("new file:"); + case DIFF_STATUS_COPIED: + return _("copied:"); + case DIFF_STATUS_DELETED: + return _("deleted:"); + case DIFF_STATUS_MODIFIED: + return _("modified:"); + case DIFF_STATUS_RENAMED: + return _("renamed:"); + case DIFF_STATUS_TYPE_CHANGED: + return _("typechange:"); + case DIFF_STATUS_UNKNOWN: + return _("unknown:"); + case DIFF_STATUS_UNMERGED: + return _("unmerged:"); + default: + return NULL; + } +} + +static int maxwidth(const char *(*label)(int), int minval, int maxval) +{ + int result = 0, i; + + for (i = minval; i <= maxval; i++) { + const char *s = label(i); + int len = s ? utf8_strwidth(s) : 0; + if (len > result) + result = len; + } + return result; +} + static void wt_status_print_unmerged_data(struct wt_status *s, struct string_list_item *it) { const char *c = color(WT_STATUS_UNMERGED, s); struct wt_status_change_data *d = it->util; struct strbuf onebuf = STRBUF_INIT; - const char *one, *how = _("bug"); + static char *padding; + static int label_width; + const char *one, *how; + int len; + + if (!padding) { + label_width = maxwidth(wt_status_unmerged_status_string, 1, 7); + label_width += strlen(" "); + padding = xmallocz(label_width); + memset(padding, ' ', label_width); + } one = quote_path(it->string, s->prefix, &onebuf); status_printf(s, color(WT_STATUS_HEADER, s), "\t"); - switch (d->stagemask) { - case 1: how = _("both deleted:"); break; - case 2: how = _("added by us:"); break; - case 3: how = _("deleted by them:"); break; - case 4: how = _("added by them:"); break; - case 5: how = _("deleted by us:"); break; - case 6: how = _("both added:"); break; - case 7: how = _("both modified:"); break; - } - status_printf_more(s, c, "%-20s%s\n", how, one); + + how = wt_status_unmerged_status_string(d->stagemask); + len = label_width - utf8_strwidth(how); + status_printf_more(s, c, "%s%.*s%s\n", how, len, padding, one); strbuf_release(&onebuf); } @@ -279,6 +342,18 @@ static void wt_status_print_change_data(struct wt_status *s, const char *one, *two; struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT; struct strbuf extra = STRBUF_INIT; + static char *padding; + static int label_width; + const char *what; + int len; + + if (!padding) { + /* If DIFF_STATUS_* uses outside the range [A..Z], we're in trouble */ + label_width = maxwidth(wt_status_diff_status_string, 'A', 'Z'); + label_width += strlen(" "); + padding = xmallocz(label_width); + memset(padding, ' ', label_width); + } one_name = two_name = it->string; switch (change_type) { @@ -310,34 +385,17 @@ static void wt_status_print_change_data(struct wt_status *s, two = quote_path(two_name, s->prefix, &twobuf); status_printf(s, color(WT_STATUS_HEADER, s), "\t"); - switch (status) { - case DIFF_STATUS_ADDED: - status_printf_more(s, c, _("new file: %s"), one); - break; - case DIFF_STATUS_COPIED: - status_printf_more(s, c, _("copied: %s -> %s"), one, two); - break; - case DIFF_STATUS_DELETED: - status_printf_more(s, c, _("deleted: %s"), one); - break; - case DIFF_STATUS_MODIFIED: - status_printf_more(s, c, _("modified: %s"), one); - break; - case DIFF_STATUS_RENAMED: - status_printf_more(s, c, _("renamed: %s -> %s"), one, two); - break; - case DIFF_STATUS_TYPE_CHANGED: - status_printf_more(s, c, _("typechange: %s"), one); - break; - case DIFF_STATUS_UNKNOWN: - status_printf_more(s, c, _("unknown: %s"), one); - break; - case DIFF_STATUS_UNMERGED: - status_printf_more(s, c, _("unmerged: %s"), one); - break; - default: + what = wt_status_diff_status_string(status); + if (!what) die(_("bug: unhandled diff status %c"), status); - } + len = label_width - utf8_strwidth(what); + assert(len >= 0); + if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED) + status_printf_more(s, c, "%s%.*s%s -> %s", + what, len, padding, one, two); + else + status_printf_more(s, c, "%s%.*s%s", + what, len, padding, one); if (extra.len) { status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf); strbuf_release(&extra); @@ -460,9 +518,19 @@ static void wt_status_collect_changes_index(struct wt_status *s) opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference; setup_revisions(0, NULL, &rev, &opt); + DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG); if (s->ignore_submodule_arg) { - DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg); + } else { + /* + * Unless the user did explicitly request a submodule ignore + * mode by passing a command line option we do not ignore any + * changed submodule SHA-1s when comparing index and HEAD, no + * matter what is configured. Otherwise the user won't be + * shown any submodules she manually added (and which are + * staged to be committed), which would be really confusing. + */ + handle_ignore_submodules_arg(&rev.diffopt, "dirty"); } rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; @@ -484,7 +552,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s) struct wt_status_change_data *d; const struct cache_entry *ce = active_cache[i]; - if (!ce_path_match(ce, &s->pathspec)) + if (!ce_path_match(ce, &s->pathspec, NULL)) continue; it = string_list_insert(&s->change, ce->name); d = it->util; @@ -505,20 +573,19 @@ static void wt_status_collect_untracked(struct wt_status *s) { int i; struct dir_struct dir; - struct timeval t_begin; + uint64_t t_begin = getnanotime(); if (!s->show_untracked_files) return; - if (advice_status_u_option) - gettimeofday(&t_begin, NULL); - memset(&dir, 0, sizeof(dir)); if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) dir.flags |= 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); @@ -526,7 +593,7 @@ static void wt_status_collect_untracked(struct wt_status *s) for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (cache_name_is_other(ent->name, ent->len) && - match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL)) + dir_path_match(ent, &s->pathspec, 0, NULL)) string_list_insert(&s->untracked, ent->name); free(ent); } @@ -534,7 +601,7 @@ static void wt_status_collect_untracked(struct wt_status *s) for (i = 0; i < dir.ignored_nr; i++) { struct dir_entry *ent = dir.ignored[i]; if (cache_name_is_other(ent->name, ent->len) && - match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL)) + dir_path_match(ent, &s->pathspec, 0, NULL)) string_list_insert(&s->ignored, ent->name); free(ent); } @@ -543,13 +610,8 @@ static void wt_status_collect_untracked(struct wt_status *s) free(dir.ignored); clear_directory(&dir); - if (advice_status_u_option) { - struct timeval t_end; - gettimeofday(&t_end, NULL); - s->untracked_in_ms = - (uint64_t)t_end.tv_sec * 1000 + t_end.tv_usec / 1000 - - ((uint64_t)t_begin.tv_sec * 1000 + t_begin.tv_usec / 1000); - } + if (advice_status_u_option) + s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; } void wt_status_collect(struct wt_status *s) @@ -664,44 +726,30 @@ static void wt_status_print_changed(struct wt_status *s) static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted) { - struct child_process sm_summary; - char summary_limit[64]; - char index[PATH_MAX]; - const char *env[] = { NULL, NULL }; - struct argv_array argv = ARGV_ARRAY_INIT; + struct child_process sm_summary = CHILD_PROCESS_INIT; struct strbuf cmd_stdout = STRBUF_INIT; struct strbuf summary = STRBUF_INIT; char *summary_content; - size_t len; - - sprintf(summary_limit, "%d", s->submodule_summary); - snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file); - - env[0] = index; - argv_array_push(&argv, "submodule"); - argv_array_push(&argv, "summary"); - argv_array_push(&argv, uncommitted ? "--files" : "--cached"); - argv_array_push(&argv, "--for-status"); - argv_array_push(&argv, "--summary-limit"); - argv_array_push(&argv, summary_limit); + + argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s", + s->index_file); + + argv_array_push(&sm_summary.args, "submodule"); + argv_array_push(&sm_summary.args, "summary"); + argv_array_push(&sm_summary.args, uncommitted ? "--files" : "--cached"); + argv_array_push(&sm_summary.args, "--for-status"); + argv_array_push(&sm_summary.args, "--summary-limit"); + argv_array_pushf(&sm_summary.args, "%d", s->submodule_summary); if (!uncommitted) - argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD"); + argv_array_push(&sm_summary.args, s->amend ? "HEAD^" : "HEAD"); - memset(&sm_summary, 0, sizeof(sm_summary)); - sm_summary.argv = argv.argv; - sm_summary.env = env; sm_summary.git_cmd = 1; sm_summary.no_stdin = 1; - fflush(s->fp); - sm_summary.out = -1; - - run_command(&sm_summary); - argv_array_clear(&argv); - 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 @@ -712,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); @@ -767,7 +816,7 @@ static void wt_status_print_other(struct wt_status *s, string_list_clear(&output, 0); strbuf_release(&buf); conclude: - status_printf_ln(s, GIT_COLOR_NORMAL, ""); + status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); } void wt_status_truncate_message_at_cut_line(struct strbuf *buf) @@ -775,17 +824,31 @@ 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); } +void wt_status_add_cut_line(FILE *fp) +{ + const char *explanation = _("Do not touch the line above.\nEverything below will be removed."); + struct strbuf buf = STRBUF_INIT; + + fprintf(fp, "%c %s", comment_line_char, cut_line); + strbuf_add_commented_lines(&buf, explanation, strlen(explanation)); + fputs(buf.buf, fp); + strbuf_release(&buf); +} + 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); @@ -807,30 +870,42 @@ static void wt_status_print_verbose(struct wt_status *s) * diff before committing. */ if (s->fp != stdout) { - const char *explanation = _("Do not touch the line above.\nEverything below will be removed."); - struct strbuf buf = STRBUF_INIT; - rev.diffopt.use_color = 0; - fprintf(s->fp, "%c %s", comment_line_char, cut_line); - strbuf_add_commented_lines(&buf, explanation, strlen(explanation)); - fputs(buf.buf, s->fp); - strbuf_release(&buf); + 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 (prefixcmp(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; @@ -849,7 +924,7 @@ static void wt_status_print_tracking(struct wt_status *s) color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c", comment_line_char); else - fprintf_ln(s->fp, ""); + fputs("", s->fp); } static int has_unmerged(struct wt_status *s) @@ -913,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 { @@ -951,21 +1026,142 @@ 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); + } + } + for (i = 0; split[i]; i++) + strbuf_release(split[i]); + +} + +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\")")); @@ -974,15 +1170,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\")")); @@ -1079,17 +1268,18 @@ 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; - while (&sb.len && sb.buf[sb.len - 1] == '\n') + while (sb.len && sb.buf[sb.len - 1] == '\n') strbuf_setlen(&sb, sb.len - 1); if (!sb.len) goto got_nothing; - if (!prefixcmp(sb.buf, "refs/heads/")) - strbuf_remove(&sb,0, strlen("refs/heads/")); - else if (!prefixcmp(sb.buf, "refs/")) + 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)) { const char *abbrev; @@ -1119,18 +1309,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 (prefixcmp(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; } @@ -1152,19 +1346,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 (!prefixcmp(ref, "refs/tags/")) - ofs = strlen("refs/tags/"); - else if (!prefixcmp(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); @@ -1176,7 +1368,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)) { @@ -1195,7 +1387,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); @@ -1204,7 +1396,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); @@ -1245,18 +1437,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 (!prefixcmp(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 "); @@ -1264,8 +1455,9 @@ void wt_status_print(struct wt_status *s) branch_name = ""; on_what = _("Not currently on any branch."); } - } - status_printf(s, color(WT_STATUS_HEADER, s), ""); + } 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); if (!s->is_initial) @@ -1278,9 +1470,9 @@ void wt_status_print(struct wt_status *s) free(state.detached_from); if (s->is_initial) { - status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); + status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit")); - status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); + status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); } wt_status_print_updated(s); @@ -1297,7 +1489,7 @@ void wt_status_print(struct wt_status *s) if (s->show_ignored_files) wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f"); if (advice_status_u_option && 2000 < s->untracked_in_ms) { - status_printf_ln(s, GIT_COLOR_NORMAL, ""); + status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); status_printf_ln(s, GIT_COLOR_NORMAL, _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n" "may speed it up, but you have to be careful not to forget to add\n" @@ -1446,42 +1638,35 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) return; branch_name = s->branch; - if (!prefixcmp(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)) @@ -1502,6 +1687,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) } color_fprintf(s->fp, header_color, "]"); + conclude: fputc(s->null_termination ? '\0' : '\n', s->fp); } |