summaryrefslogtreecommitdiff
path: root/wt-status.c
diff options
context:
space:
mode:
Diffstat (limited to 'wt-status.c')
-rw-r--r--wt-status.c127
1 files changed, 81 insertions, 46 deletions
diff --git a/wt-status.c b/wt-status.c
index f5debcd2b4..5ffab61015 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -136,7 +136,11 @@ void wt_status_prepare(struct wt_status *s)
s->ignored.strdup_strings = 1;
s->show_branch = -1; /* unspecified */
s->show_stash = 0;
+ s->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED;
s->display_comment_prefix = 0;
+ s->detect_rename = -1;
+ s->rename_score = -1;
+ s->rename_limit = -1;
}
static void wt_longstatus_print_unmerged_header(struct wt_status *s)
@@ -263,7 +267,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)
case 7:
return _("both modified:");
default:
- die("BUG: unhandled unmerged status %x", stagemask);
+ BUG("unhandled unmerged status %x", stagemask);
}
}
@@ -376,7 +380,7 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
status = d->worktree_status;
break;
default:
- die("BUG: unhandled change_type %d in wt_longstatus_print_change_data",
+ BUG("unhandled change_type %d in wt_longstatus_print_change_data",
change_type);
}
@@ -394,7 +398,7 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
status_printf(s, color(WT_STATUS_HEADER, s), "\t");
what = wt_status_diff_status_string(status);
if (!what)
- die("BUG: unhandled diff status %c", status);
+ BUG("unhandled diff status %c", status);
len = label_width - utf8_strwidth(what);
assert(len >= 0);
if (one_name != two_name)
@@ -469,7 +473,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
case DIFF_STATUS_COPIED:
case DIFF_STATUS_RENAMED:
if (d->rename_status)
- die("BUG: multiple renames on the same target? how?");
+ BUG("multiple renames on the same target? how?");
d->rename_source = xstrdup(p->one->path);
d->rename_score = p->score * 100 / MAX_SCORE;
d->rename_status = p->status;
@@ -483,7 +487,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
break;
default:
- die("BUG: unhandled diff-files status '%c'", p->status);
+ BUG("unhandled diff-files status '%c'", p->status);
break;
}
@@ -546,7 +550,7 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
case DIFF_STATUS_COPIED:
case DIFF_STATUS_RENAMED:
if (d->rename_status)
- die("BUG: multiple renames on the same target? how?");
+ BUG("multiple renames on the same target? how?");
d->rename_source = xstrdup(p->one->path);
d->rename_score = p->score * 100 / MAX_SCORE;
d->rename_status = p->status;
@@ -568,7 +572,7 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
break;
default:
- die("BUG: unhandled diff-index status '%c'", p->status);
+ BUG("unhandled diff-index status '%c'", p->status);
break;
}
}
@@ -591,6 +595,9 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
}
rev.diffopt.format_callback = wt_status_collect_changed_cb;
rev.diffopt.format_callback_data = s;
+ rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
+ rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
+ rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
copy_pathspec(&rev.prune_data, &s->pathspec);
run_diff_files(&rev, 0);
}
@@ -602,7 +609,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
init_revisions(&rev, NULL);
memset(&opt, 0, sizeof(opt));
- opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
+ opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
setup_revisions(0, NULL, &rev, &opt);
rev.diffopt.flags.override_submodule_config = 1;
@@ -624,9 +631,9 @@ static void wt_status_collect_changes_index(struct wt_status *s)
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = wt_status_collect_updated_cb;
rev.diffopt.format_callback_data = s;
- rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
- rev.diffopt.rename_limit = 200;
- rev.diffopt.break_opt = 0;
+ rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
+ rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
+ rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
copy_pathspec(&rev.prune_data, &s->pathspec);
run_diff_index(&rev, 1);
}
@@ -640,7 +647,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, NULL))
+ if (!ce_path_match(&the_index, ce, &s->pathspec, NULL))
continue;
if (ce_intent_to_add(ce))
continue;
@@ -696,7 +703,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) &&
- dir_path_match(ent, &s->pathspec, 0, NULL))
+ dir_path_match(&the_index, ent, &s->pathspec, 0, NULL))
string_list_insert(&s->untracked, ent->name);
free(ent);
}
@@ -704,7 +711,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) &&
- dir_path_match(ent, &s->pathspec, 0, NULL))
+ dir_path_match(&the_index, ent, &s->pathspec, 0, NULL))
string_list_insert(&s->ignored, ent->name);
free(ent);
}
@@ -980,11 +987,13 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
rev.diffopt.ita_invisible_in_index = 1;
memset(&opt, 0, sizeof(opt));
- opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
+ opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
setup_revisions(0, NULL, &rev, &opt);
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
- rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
+ rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
+ rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
+ rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
rev.diffopt.file = s->fp;
rev.diffopt.close_file = 0;
/*
@@ -1032,7 +1041,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
if (!skip_prefix(s->branch, "refs/heads/", &branch_name))
return;
branch = branch_get(branch_name);
- if (!format_tracking_info(branch, &sb))
+ if (!format_tracking_info(branch, &sb, s->ahead_behind_flags))
return;
i = 0;
@@ -1187,7 +1196,7 @@ static void abbrev_sha1_in_line(struct strbuf *line)
strbuf_trim(split[1]);
if (!get_oid(split[1]->buf, &oid)) {
strbuf_reset(split[1]);
- strbuf_add_unique_abbrev(split[1], oid.hash,
+ strbuf_add_unique_abbrev(split[1], &oid,
DEFAULT_ABBREV);
strbuf_addch(split[1], ' ');
strbuf_reset(line);
@@ -1308,7 +1317,7 @@ 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)) {
+ } else if (state->rebase_in_progress || !stat(git_path_merge_msg(the_repository), &st)) {
print_rebase_state(s, state, color);
if (s->hints)
status_printf_ln(s, color,
@@ -1349,7 +1358,7 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
const char *color)
{
status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
- find_unique_abbrev(state->cherry_pick_head_sha1, DEFAULT_ABBREV));
+ find_unique_abbrev(&state->cherry_pick_head_oid, DEFAULT_ABBREV));
if (s->hints) {
if (has_unmerged(s))
status_printf_ln(s, color,
@@ -1368,7 +1377,7 @@ static void show_revert_in_progress(struct wt_status *s,
const char *color)
{
status_printf_ln(s, color, _("You are currently reverting commit %s."),
- find_unique_abbrev(state->revert_head_sha1, DEFAULT_ABBREV));
+ find_unique_abbrev(&state->revert_head_oid, DEFAULT_ABBREV));
if (s->hints) {
if (has_unmerged(s))
status_printf_ln(s, color,
@@ -1421,7 +1430,7 @@ static char *get_branch(const struct worktree *wt, const char *path)
;
else if (!get_oid_hex(sb.buf, &oid)) {
strbuf_reset(&sb);
- strbuf_add_unique_abbrev(&sb, oid.hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(&sb, &oid, DEFAULT_ABBREV);
} else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
goto got_nothing;
else /* bisect */
@@ -1458,7 +1467,7 @@ static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
if (!strcmp(cb->buf.buf, "HEAD")) {
/* HEAD is relative. Resolve it to the right reflog entry. */
strbuf_reset(&cb->buf);
- strbuf_add_unique_abbrev(&cb->buf, noid->hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(&cb->buf, noid, DEFAULT_ABBREV);
}
return 1;
}
@@ -1480,7 +1489,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
/* sha1 is a commit? match without further lookup */
(!oidcmp(&cb.noid, &oid) ||
/* perhaps sha1 is a tag, try to dereference to a commit */
- ((commit = lookup_commit_reference_gently(&oid, 1)) != NULL &&
+ ((commit = lookup_commit_reference_gently(the_repository, &oid, 1)) != NULL &&
!oidcmp(&cb.noid, &commit->object.oid)))) {
const char *from = ref;
if (!skip_prefix(from, "refs/tags/", &from))
@@ -1488,10 +1497,10 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
state->detached_from = xstrdup(from);
} else
state->detached_from =
- xstrdup(find_unique_abbrev(cb.noid.hash, DEFAULT_ABBREV));
- hashcpy(state->detached_sha1, cb.noid.hash);
+ xstrdup(find_unique_abbrev(&cb.noid, DEFAULT_ABBREV));
+ oidcpy(&state->detached_oid, &cb.noid);
state->detached_at = !get_oid("HEAD", &oid) &&
- !hashcmp(oid.hash, state->detached_sha1);
+ !oidcmp(&oid, &state->detached_oid);
free(ref);
strbuf_release(&cb.buf);
@@ -1543,20 +1552,20 @@ void wt_status_get_state(struct wt_status_state *state,
struct stat st;
struct object_id oid;
- if (!stat(git_path_merge_head(), &st)) {
+ if (!stat(git_path_merge_head(the_repository), &st)) {
state->merge_in_progress = 1;
} else if (wt_status_check_rebase(NULL, state)) {
; /* all set */
- } else if (!stat(git_path_cherry_pick_head(), &st) &&
+ } else if (!stat(git_path_cherry_pick_head(the_repository), &st) &&
!get_oid("CHERRY_PICK_HEAD", &oid)) {
state->cherry_pick_in_progress = 1;
- hashcpy(state->cherry_pick_head_sha1, oid.hash);
+ oidcpy(&state->cherry_pick_head_oid, &oid);
}
wt_status_check_bisect(NULL, state);
- if (!stat(git_path_revert_head(), &st) &&
+ if (!stat(git_path_revert_head(the_repository), &st) &&
!get_oid("REVERT_HEAD", &oid)) {
state->revert_in_progress = 1;
- hashcpy(state->revert_head_sha1, oid.hash);
+ oidcpy(&state->revert_head_oid, &oid);
}
if (get_detached_from)
@@ -1793,7 +1802,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
const char *base;
char *short_base;
const char *branch_name;
- int num_ours, num_theirs;
+ int num_ours, num_theirs, sti;
int upstream_is_gone = 0;
color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
@@ -1819,7 +1828,9 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
color_fprintf(s->fp, branch_color_local, "%s", branch_name);
- if (stat_tracking_info(branch, &num_ours, &num_theirs, &base) < 0) {
+ sti = stat_tracking_info(branch, &num_ours, &num_theirs, &base,
+ s->ahead_behind_flags);
+ if (sti < 0) {
if (!base)
goto conclude;
@@ -1831,12 +1842,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
color_fprintf(s->fp, branch_color_remote, "%s", short_base);
free(short_base);
- if (!upstream_is_gone && !num_ours && !num_theirs)
+ if (!upstream_is_gone && !sti)
goto conclude;
color_fprintf(s->fp, header_color, " [");
if (upstream_is_gone) {
color_fprintf(s->fp, header_color, LABEL(N_("gone")));
+ } else if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK) {
+ color_fprintf(s->fp, header_color, LABEL(N_("different")));
} else if (!num_ours) {
color_fprintf(s->fp, header_color, LABEL(N_("behind ")));
color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
@@ -1905,18 +1918,19 @@ static void wt_porcelain_print(struct wt_status *s)
*
* <upstream> ::= the upstream branch name, when set.
*
- * <ahead> ::= integer ahead value, when upstream set
- * and the commit is present (not gone).
- *
- * <behind> ::= integer behind value, when upstream set
- * and commit is present.
+ * <ahead> ::= integer ahead value or '?'.
*
+ * <behind> ::= integer behind value or '?'.
*
* The end-of-line is defined by the -z flag.
*
* <eol> ::= NUL when -z,
* LF when NOT -z.
*
+ * When an upstream is set and present, the 'branch.ab' line will
+ * be printed with the ahead/behind counts for the branch and the
+ * upstream. When AHEAD_BEHIND_QUICK is requested and the branches
+ * are different, '?' will be substituted for the actual count.
*/
static void wt_porcelain_v2_print_tracking(struct wt_status *s)
{
@@ -1956,14 +1970,25 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
/* Lookup stats on the upstream tracking branch, if set. */
branch = branch_get(branch_name);
base = NULL;
- ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, &base) == 0);
+ ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
+ &base, s->ahead_behind_flags);
if (base) {
base = shorten_unambiguous_ref(base, 0);
fprintf(s->fp, "# branch.upstream %s%c", base, eol);
free((char *)base);
- if (ab_info)
- fprintf(s->fp, "# branch.ab +%d -%d%c", nr_ahead, nr_behind, eol);
+ if (ab_info > 0) {
+ /* different */
+ if (nr_ahead || nr_behind)
+ fprintf(s->fp, "# branch.ab +%d -%d%c",
+ nr_ahead, nr_behind, eol);
+ else
+ fprintf(s->fp, "# branch.ab +? -?%c",
+ eol);
+ } else if (!ab_info) {
+ /* same */
+ fprintf(s->fp, "# branch.ab +0 -0%c", eol);
+ }
}
}
@@ -2141,7 +2166,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
case 6: key = "AA"; break; /* both added */
case 7: key = "UU"; break; /* both modified */
default:
- die("BUG: unhandled unmerged status %x", d->stagemask);
+ BUG("unhandled unmerged status %x", d->stagemask);
}
/*
@@ -2168,7 +2193,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
sum |= (1 << (stage - 1));
}
if (sum != d->stagemask)
- die("BUG: observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask);
+ BUG("observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask);
if (s->null_termination)
path_index = it->string;
@@ -2272,7 +2297,7 @@ void wt_status_print(struct wt_status *s)
wt_porcelain_v2_print(s);
break;
case STATUS_FORMAT_UNSPECIFIED:
- die("BUG: finalize_deferred_config() should have been called");
+ BUG("finalize_deferred_config() should have been called");
break;
case STATUS_FORMAT_NONE:
case STATUS_FORMAT_LONG:
@@ -2315,7 +2340,17 @@ int has_uncommitted_changes(int ignore_submodules)
if (ignore_submodules)
rev_info.diffopt.flags.ignore_submodules = 1;
rev_info.diffopt.flags.quick = 1;
+
add_head_to_pending(&rev_info);
+ if (!rev_info.pending.nr) {
+ /*
+ * We have no head (or it's corrupt); use the empty tree,
+ * which will complain if the index is non-empty.
+ */
+ struct tree *tree = lookup_tree(the_repository, the_hash_algo->empty_tree);
+ add_pending_object(&rev_info, &tree->object, "");
+ }
+
diff_setup_done(&rev_info.diffopt);
result = run_diff_index(&rev_info, 1);
return diff_result_code(&rev_info.diffopt, result);