Merge branch 'jh/status-v2-porcelain'
Enhance "git status --porcelain" output by collecting more data on the state of the index and the working tree files, which may further be used to teach git-prompt (in contrib/) to make fewer calls to git. * jh/status-v2-porcelain: status: unit tests for --porcelain=v2 test-lib-functions.sh: add lf_to_nul helper git-status.txt: describe --porcelain=v2 format status: print branch info with --porcelain=v2 --branch status: print per-file porcelain v2 status data status: collect per-file data for --porcelain=v2 status: support --porcelain[=<version>] status: cleanup API to wt_status_print status: rename long-format print routines
This commit is contained in:
570
wt-status.c
570
wt-status.c
@ -139,7 +139,7 @@ void wt_status_prepare(struct wt_status *s)
|
||||
s->display_comment_prefix = 0;
|
||||
}
|
||||
|
||||
static void wt_status_print_unmerged_header(struct wt_status *s)
|
||||
static void wt_longstatus_print_unmerged_header(struct wt_status *s)
|
||||
{
|
||||
int i;
|
||||
int del_mod_conflict = 0;
|
||||
@ -191,7 +191,7 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
|
||||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_cached_header(struct wt_status *s)
|
||||
static void wt_longstatus_print_cached_header(struct wt_status *s)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER, s);
|
||||
|
||||
@ -207,9 +207,9 @@ static void wt_status_print_cached_header(struct wt_status *s)
|
||||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_dirty_header(struct wt_status *s,
|
||||
int has_deleted,
|
||||
int has_dirty_submodules)
|
||||
static void wt_longstatus_print_dirty_header(struct wt_status *s,
|
||||
int has_deleted,
|
||||
int has_dirty_submodules)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER, s);
|
||||
|
||||
@ -226,9 +226,9 @@ static void wt_status_print_dirty_header(struct wt_status *s,
|
||||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_other_header(struct wt_status *s,
|
||||
const char *what,
|
||||
const char *how)
|
||||
static void wt_longstatus_print_other_header(struct wt_status *s,
|
||||
const char *what,
|
||||
const char *how)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER, s);
|
||||
status_printf_ln(s, c, "%s:", what);
|
||||
@ -238,7 +238,7 @@ static void wt_status_print_other_header(struct wt_status *s,
|
||||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_trailer(struct wt_status *s)
|
||||
static void wt_longstatus_print_trailer(struct wt_status *s)
|
||||
{
|
||||
status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
|
||||
}
|
||||
@ -304,8 +304,8 @@ static int maxwidth(const char *(*label)(int), int minval, int maxval)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void wt_status_print_unmerged_data(struct wt_status *s,
|
||||
struct string_list_item *it)
|
||||
static void wt_longstatus_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;
|
||||
@ -331,9 +331,9 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
|
||||
strbuf_release(&onebuf);
|
||||
}
|
||||
|
||||
static void wt_status_print_change_data(struct wt_status *s,
|
||||
int change_type,
|
||||
struct string_list_item *it)
|
||||
static void wt_longstatus_print_change_data(struct wt_status *s,
|
||||
int change_type,
|
||||
struct string_list_item *it)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
const char *c = color(change_type, s);
|
||||
@ -378,7 +378,7 @@ static void wt_status_print_change_data(struct wt_status *s,
|
||||
status = d->worktree_status;
|
||||
break;
|
||||
default:
|
||||
die("BUG: unhandled change_type %d in wt_status_print_change_data",
|
||||
die("BUG: unhandled change_type %d in wt_longstatus_print_change_data",
|
||||
change_type);
|
||||
}
|
||||
|
||||
@ -434,6 +434,31 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
|
||||
if (S_ISGITLINK(p->two->mode))
|
||||
d->new_submodule_commits = !!oidcmp(&p->one->oid,
|
||||
&p->two->oid);
|
||||
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_ADDED:
|
||||
die("BUG: worktree status add???");
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_DELETED:
|
||||
d->mode_index = p->one->mode;
|
||||
oidcpy(&d->oid_index, &p->one->oid);
|
||||
/* mode_worktree is zero for a delete. */
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
d->mode_index = p->one->mode;
|
||||
d->mode_worktree = p->two->mode;
|
||||
oidcpy(&d->oid_index, &p->one->oid);
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_UNKNOWN:
|
||||
die("BUG: worktree status unknown???");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,12 +504,36 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
|
||||
if (!d->index_status)
|
||||
d->index_status = p->status;
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_ADDED:
|
||||
/* Leave {mode,oid}_head zero for an add. */
|
||||
d->mode_index = p->two->mode;
|
||||
oidcpy(&d->oid_index, &p->two->oid);
|
||||
break;
|
||||
case DIFF_STATUS_DELETED:
|
||||
d->mode_head = p->one->mode;
|
||||
oidcpy(&d->oid_head, &p->one->oid);
|
||||
/* Leave {mode,oid}_index zero for a delete. */
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_COPIED:
|
||||
case DIFF_STATUS_RENAMED:
|
||||
d->head_path = xstrdup(p->one->path);
|
||||
d->score = p->score * 100 / MAX_SCORE;
|
||||
/* fallthru */
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
d->mode_head = p->one->mode;
|
||||
d->mode_index = p->two->mode;
|
||||
oidcpy(&d->oid_head, &p->one->oid);
|
||||
oidcpy(&d->oid_index, &p->two->oid);
|
||||
break;
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
d->stagemask = unmerged_mask(p->two->path);
|
||||
/*
|
||||
* Don't bother setting {mode,oid}_{head,index} since the print
|
||||
* code will output the stage values directly and not use the
|
||||
* values in these fields.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -565,9 +614,17 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
|
||||
if (ce_stage(ce)) {
|
||||
d->index_status = DIFF_STATUS_UNMERGED;
|
||||
d->stagemask |= (1 << (ce_stage(ce) - 1));
|
||||
}
|
||||
else
|
||||
/*
|
||||
* Don't bother setting {mode,oid}_{head,index} since the print
|
||||
* code will output the stage values directly and not use the
|
||||
* values in these fields.
|
||||
*/
|
||||
} else {
|
||||
d->index_status = DIFF_STATUS_ADDED;
|
||||
/* Leave {mode,oid}_head zero for adds. */
|
||||
d->mode_index = ce->ce_mode;
|
||||
hashcpy(d->oid_index.hash, ce->sha1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,7 +684,7 @@ void wt_status_collect(struct wt_status *s)
|
||||
wt_status_collect_untracked(s);
|
||||
}
|
||||
|
||||
static void wt_status_print_unmerged(struct wt_status *s)
|
||||
static void wt_longstatus_print_unmerged(struct wt_status *s)
|
||||
{
|
||||
int shown_header = 0;
|
||||
int i;
|
||||
@ -640,17 +697,17 @@ static void wt_status_print_unmerged(struct wt_status *s)
|
||||
if (!d->stagemask)
|
||||
continue;
|
||||
if (!shown_header) {
|
||||
wt_status_print_unmerged_header(s);
|
||||
wt_longstatus_print_unmerged_header(s);
|
||||
shown_header = 1;
|
||||
}
|
||||
wt_status_print_unmerged_data(s, it);
|
||||
wt_longstatus_print_unmerged_data(s, it);
|
||||
}
|
||||
if (shown_header)
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
|
||||
}
|
||||
|
||||
static void wt_status_print_updated(struct wt_status *s)
|
||||
static void wt_longstatus_print_updated(struct wt_status *s)
|
||||
{
|
||||
int shown_header = 0;
|
||||
int i;
|
||||
@ -664,14 +721,14 @@ static void wt_status_print_updated(struct wt_status *s)
|
||||
d->index_status == DIFF_STATUS_UNMERGED)
|
||||
continue;
|
||||
if (!shown_header) {
|
||||
wt_status_print_cached_header(s);
|
||||
wt_longstatus_print_cached_header(s);
|
||||
s->commitable = 1;
|
||||
shown_header = 1;
|
||||
}
|
||||
wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
|
||||
wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);
|
||||
}
|
||||
if (shown_header)
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -703,7 +760,7 @@ static int wt_status_check_worktree_changes(struct wt_status *s,
|
||||
return changes;
|
||||
}
|
||||
|
||||
static void wt_status_print_changed(struct wt_status *s)
|
||||
static void wt_longstatus_print_changed(struct wt_status *s)
|
||||
{
|
||||
int i, dirty_submodules;
|
||||
int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
|
||||
@ -711,7 +768,7 @@ static void wt_status_print_changed(struct wt_status *s)
|
||||
if (!worktree_changes)
|
||||
return;
|
||||
|
||||
wt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
|
||||
wt_longstatus_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
|
||||
|
||||
for (i = 0; i < s->change.nr; i++) {
|
||||
struct wt_status_change_data *d;
|
||||
@ -721,12 +778,12 @@ static void wt_status_print_changed(struct wt_status *s)
|
||||
if (!d->worktree_status ||
|
||||
d->worktree_status == DIFF_STATUS_UNMERGED)
|
||||
continue;
|
||||
wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
|
||||
wt_longstatus_print_change_data(s, WT_STATUS_CHANGED, it);
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
|
||||
static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted)
|
||||
{
|
||||
struct child_process sm_summary = CHILD_PROCESS_INIT;
|
||||
struct strbuf cmd_stdout = STRBUF_INIT;
|
||||
@ -772,10 +829,10 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
|
||||
strbuf_release(&summary);
|
||||
}
|
||||
|
||||
static void wt_status_print_other(struct wt_status *s,
|
||||
struct string_list *l,
|
||||
const char *what,
|
||||
const char *how)
|
||||
static void wt_longstatus_print_other(struct wt_status *s,
|
||||
struct string_list *l,
|
||||
const char *what,
|
||||
const char *how)
|
||||
{
|
||||
int i;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
@ -785,7 +842,7 @@ static void wt_status_print_other(struct wt_status *s,
|
||||
if (!l->nr)
|
||||
return;
|
||||
|
||||
wt_status_print_other_header(s, what, how);
|
||||
wt_longstatus_print_other_header(s, what, how);
|
||||
|
||||
for (i = 0; i < l->nr; i++) {
|
||||
struct string_list_item *it;
|
||||
@ -845,7 +902,7 @@ void wt_status_add_cut_line(FILE *fp)
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void wt_status_print_verbose(struct wt_status *s)
|
||||
static void wt_longstatus_print_verbose(struct wt_status *s)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct setup_revision_opt opt;
|
||||
@ -878,7 +935,7 @@ static void wt_status_print_verbose(struct wt_status *s)
|
||||
if (s->verbose > 1 && s->commitable) {
|
||||
/* print_updated() printed a header, so do we */
|
||||
if (s->fp != stdout)
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
status_printf_ln(s, c, _("Changes to be committed:"));
|
||||
rev.diffopt.a_prefix = "c/";
|
||||
rev.diffopt.b_prefix = "i/";
|
||||
@ -896,7 +953,7 @@ static void wt_status_print_verbose(struct wt_status *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void wt_status_print_tracking(struct wt_status *s)
|
||||
static void wt_longstatus_print_tracking(struct wt_status *s)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *cp, *ep, *branch_name;
|
||||
@ -962,7 +1019,7 @@ static void show_merge_in_progress(struct wt_status *s,
|
||||
status_printf_ln(s, color,
|
||||
_(" (use \"git commit\" to conclude merge)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_am_in_progress(struct wt_status *s,
|
||||
@ -983,7 +1040,7 @@ static void show_am_in_progress(struct wt_status *s,
|
||||
status_printf_ln(s, color,
|
||||
_(" (use \"git am --abort\" to restore the original branch)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static char *read_line_from_git_path(const char *filename)
|
||||
@ -1207,7 +1264,7 @@ static void show_rebase_in_progress(struct wt_status *s,
|
||||
_(" (use \"git rebase --continue\" once you are satisfied with your changes)"));
|
||||
}
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_cherry_pick_in_progress(struct wt_status *s,
|
||||
@ -1226,7 +1283,7 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
|
||||
status_printf_ln(s, color,
|
||||
_(" (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_revert_in_progress(struct wt_status *s,
|
||||
@ -1245,7 +1302,7 @@ static void show_revert_in_progress(struct wt_status *s,
|
||||
status_printf_ln(s, color,
|
||||
_(" (use \"git revert --abort\" to cancel the revert operation)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_bisect_in_progress(struct wt_status *s,
|
||||
@ -1262,7 +1319,7 @@ static void show_bisect_in_progress(struct wt_status *s,
|
||||
if (s->hints)
|
||||
status_printf_ln(s, color,
|
||||
_(" (use \"git bisect reset\" to get back to the original branch)"));
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1432,8 +1489,8 @@ void wt_status_get_state(struct wt_status_state *state,
|
||||
wt_status_get_detached_from(state);
|
||||
}
|
||||
|
||||
static void wt_status_print_state(struct wt_status *s,
|
||||
struct wt_status_state *state)
|
||||
static void wt_longstatus_print_state(struct wt_status *s,
|
||||
struct wt_status_state *state)
|
||||
{
|
||||
const char *state_color = color(WT_STATUS_HEADER, s);
|
||||
if (state->merge_in_progress)
|
||||
@ -1450,7 +1507,7 @@ static void wt_status_print_state(struct wt_status *s,
|
||||
show_bisect_in_progress(s, state, state_color);
|
||||
}
|
||||
|
||||
void wt_status_print(struct wt_status *s)
|
||||
static void wt_longstatus_print(struct wt_status *s)
|
||||
{
|
||||
const char *branch_color = color(WT_STATUS_ONBRANCH, s);
|
||||
const char *branch_status_color = color(WT_STATUS_HEADER, s);
|
||||
@ -1487,10 +1544,10 @@ void wt_status_print(struct wt_status *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)
|
||||
wt_status_print_tracking(s);
|
||||
wt_longstatus_print_tracking(s);
|
||||
}
|
||||
|
||||
wt_status_print_state(s, &state);
|
||||
wt_longstatus_print_state(s, &state);
|
||||
free(state.branch);
|
||||
free(state.onto);
|
||||
free(state.detached_from);
|
||||
@ -1501,19 +1558,19 @@ void wt_status_print(struct wt_status *s)
|
||||
status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
|
||||
}
|
||||
|
||||
wt_status_print_updated(s);
|
||||
wt_status_print_unmerged(s);
|
||||
wt_status_print_changed(s);
|
||||
wt_longstatus_print_updated(s);
|
||||
wt_longstatus_print_unmerged(s);
|
||||
wt_longstatus_print_changed(s);
|
||||
if (s->submodule_summary &&
|
||||
(!s->ignore_submodule_arg ||
|
||||
strcmp(s->ignore_submodule_arg, "all"))) {
|
||||
wt_status_print_submodule_summary(s, 0); /* staged */
|
||||
wt_status_print_submodule_summary(s, 1); /* unstaged */
|
||||
wt_longstatus_print_submodule_summary(s, 0); /* staged */
|
||||
wt_longstatus_print_submodule_summary(s, 1); /* unstaged */
|
||||
}
|
||||
if (s->show_untracked_files) {
|
||||
wt_status_print_other(s, &s->untracked, _("Untracked files"), "add");
|
||||
wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
|
||||
if (s->show_ignored_files)
|
||||
wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f");
|
||||
wt_longstatus_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, "%s", "");
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL,
|
||||
@ -1528,7 +1585,7 @@ void wt_status_print(struct wt_status *s)
|
||||
? _(" (use -u option to show untracked files)") : "");
|
||||
|
||||
if (s->verbose)
|
||||
wt_status_print_verbose(s);
|
||||
wt_longstatus_print_verbose(s);
|
||||
if (!s->commitable) {
|
||||
if (s->amend)
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
|
||||
@ -1717,7 +1774,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
|
||||
fputc(s->null_termination ? '\0' : '\n', s->fp);
|
||||
}
|
||||
|
||||
void wt_shortstatus_print(struct wt_status *s)
|
||||
static void wt_shortstatus_print(struct wt_status *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -1749,7 +1806,7 @@ void wt_shortstatus_print(struct wt_status *s)
|
||||
}
|
||||
}
|
||||
|
||||
void wt_porcelain_print(struct wt_status *s)
|
||||
static void wt_porcelain_print(struct wt_status *s)
|
||||
{
|
||||
s->use_color = 0;
|
||||
s->relative_paths = 0;
|
||||
@ -1757,3 +1814,398 @@ void wt_porcelain_print(struct wt_status *s)
|
||||
s->no_gettext = 1;
|
||||
wt_shortstatus_print(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print branch information for porcelain v2 output. These lines
|
||||
* are printed when the '--branch' parameter is given.
|
||||
*
|
||||
* # branch.oid <commit><eol>
|
||||
* # branch.head <head><eol>
|
||||
* [# branch.upstream <upstream><eol>
|
||||
* [# branch.ab +<ahead> -<behind><eol>]]
|
||||
*
|
||||
* <commit> ::= the current commit hash or the the literal
|
||||
* "(initial)" to indicate an initialized repo
|
||||
* with no commits.
|
||||
*
|
||||
* <head> ::= <branch_name> the current branch name or
|
||||
* "(detached)" literal when detached head or
|
||||
* "(unknown)" when something is wrong.
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
*
|
||||
* The end-of-line is defined by the -z flag.
|
||||
*
|
||||
* <eol> ::= NUL when -z,
|
||||
* LF when NOT -z.
|
||||
*
|
||||
*/
|
||||
static void wt_porcelain_v2_print_tracking(struct wt_status *s)
|
||||
{
|
||||
struct branch *branch;
|
||||
const char *base;
|
||||
const char *branch_name;
|
||||
struct wt_status_state state;
|
||||
int ab_info, nr_ahead, nr_behind;
|
||||
char eol = s->null_termination ? '\0' : '\n';
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD"));
|
||||
|
||||
fprintf(s->fp, "# branch.oid %s%c",
|
||||
(s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)),
|
||||
eol);
|
||||
|
||||
if (!s->branch)
|
||||
fprintf(s->fp, "# branch.head %s%c", "(unknown)", eol);
|
||||
else {
|
||||
if (!strcmp(s->branch, "HEAD")) {
|
||||
fprintf(s->fp, "# branch.head %s%c", "(detached)", eol);
|
||||
|
||||
if (state.rebase_in_progress || state.rebase_interactive_in_progress)
|
||||
branch_name = state.onto;
|
||||
else if (state.detached_from)
|
||||
branch_name = state.detached_from;
|
||||
else
|
||||
branch_name = "";
|
||||
} else {
|
||||
branch_name = NULL;
|
||||
skip_prefix(s->branch, "refs/heads/", &branch_name);
|
||||
|
||||
fprintf(s->fp, "# branch.head %s%c", branch_name, eol);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
free(state.branch);
|
||||
free(state.onto);
|
||||
free(state.detached_from);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert various submodule status values into a
|
||||
* fixed-length string of characters in the buffer provided.
|
||||
*/
|
||||
static void wt_porcelain_v2_submodule_state(
|
||||
struct wt_status_change_data *d,
|
||||
char sub[5])
|
||||
{
|
||||
if (S_ISGITLINK(d->mode_head) ||
|
||||
S_ISGITLINK(d->mode_index) ||
|
||||
S_ISGITLINK(d->mode_worktree)) {
|
||||
sub[0] = 'S';
|
||||
sub[1] = d->new_submodule_commits ? 'C' : '.';
|
||||
sub[2] = (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) ? 'M' : '.';
|
||||
sub[3] = (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ? 'U' : '.';
|
||||
} else {
|
||||
sub[0] = 'N';
|
||||
sub[1] = '.';
|
||||
sub[2] = '.';
|
||||
sub[3] = '.';
|
||||
}
|
||||
sub[4] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix-up changed entries before we print them.
|
||||
*/
|
||||
static void wt_porcelain_v2_fix_up_changed(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
|
||||
if (!d->index_status) {
|
||||
/*
|
||||
* This entry is unchanged in the index (relative to the head).
|
||||
* Therefore, the collect_updated_cb was never called for this
|
||||
* entry (during the head-vs-index scan) and so the head column
|
||||
* fields were never set.
|
||||
*
|
||||
* We must have data for the index column (from the
|
||||
* index-vs-worktree scan (otherwise, this entry should not be
|
||||
* in the list of changes)).
|
||||
*
|
||||
* Copy index column fields to the head column, so that our
|
||||
* output looks complete.
|
||||
*/
|
||||
assert(d->mode_head == 0);
|
||||
d->mode_head = d->mode_index;
|
||||
oidcpy(&d->oid_head, &d->oid_index);
|
||||
}
|
||||
|
||||
if (!d->worktree_status) {
|
||||
/*
|
||||
* This entry is unchanged in the worktree (relative to the index).
|
||||
* Therefore, the collect_changed_cb was never called for this entry
|
||||
* (during the index-vs-worktree scan) and so the worktree column
|
||||
* fields were never set.
|
||||
*
|
||||
* We must have data for the index column (from the head-vs-index
|
||||
* scan).
|
||||
*
|
||||
* Copy the index column fields to the worktree column so that
|
||||
* our output looks complete.
|
||||
*
|
||||
* Note that we only have a mode field in the worktree column
|
||||
* because the scan code tries really hard to not have to compute it.
|
||||
*/
|
||||
assert(d->mode_worktree == 0);
|
||||
d->mode_worktree = d->mode_index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain v2 info for tracked entries with changes.
|
||||
*/
|
||||
static void wt_porcelain_v2_print_changed_entry(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
struct strbuf buf_index = STRBUF_INIT;
|
||||
struct strbuf buf_head = STRBUF_INIT;
|
||||
const char *path_index = NULL;
|
||||
const char *path_head = NULL;
|
||||
char key[3];
|
||||
char submodule_token[5];
|
||||
char sep_char, eol_char;
|
||||
|
||||
wt_porcelain_v2_fix_up_changed(it, s);
|
||||
wt_porcelain_v2_submodule_state(d, submodule_token);
|
||||
|
||||
key[0] = d->index_status ? d->index_status : '.';
|
||||
key[1] = d->worktree_status ? d->worktree_status : '.';
|
||||
key[2] = 0;
|
||||
|
||||
if (s->null_termination) {
|
||||
/*
|
||||
* In -z mode, we DO NOT C-quote pathnames. Current path is ALWAYS first.
|
||||
* A single NUL character separates them.
|
||||
*/
|
||||
sep_char = '\0';
|
||||
eol_char = '\0';
|
||||
path_index = it->string;
|
||||
path_head = d->head_path;
|
||||
} else {
|
||||
/*
|
||||
* Path(s) are C-quoted if necessary. Current path is ALWAYS first.
|
||||
* The source path is only present when necessary.
|
||||
* A single TAB separates them (because paths can contain spaces
|
||||
* which are not escaped and C-quoting does escape TAB characters).
|
||||
*/
|
||||
sep_char = '\t';
|
||||
eol_char = '\n';
|
||||
path_index = quote_path(it->string, s->prefix, &buf_index);
|
||||
if (d->head_path)
|
||||
path_head = quote_path(d->head_path, s->prefix, &buf_head);
|
||||
}
|
||||
|
||||
if (path_head)
|
||||
fprintf(s->fp, "2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c",
|
||||
key, submodule_token,
|
||||
d->mode_head, d->mode_index, d->mode_worktree,
|
||||
oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
|
||||
key[0], d->score,
|
||||
path_index, sep_char, path_head, eol_char);
|
||||
else
|
||||
fprintf(s->fp, "1 %s %s %06o %06o %06o %s %s %s%c",
|
||||
key, submodule_token,
|
||||
d->mode_head, d->mode_index, d->mode_worktree,
|
||||
oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
|
||||
path_index, eol_char);
|
||||
|
||||
strbuf_release(&buf_index);
|
||||
strbuf_release(&buf_head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain v2 status info for unmerged entries.
|
||||
*/
|
||||
static void wt_porcelain_v2_print_unmerged_entry(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
const struct cache_entry *ce;
|
||||
struct strbuf buf_index = STRBUF_INIT;
|
||||
const char *path_index = NULL;
|
||||
int pos, stage, sum;
|
||||
struct {
|
||||
int mode;
|
||||
struct object_id oid;
|
||||
} stages[3];
|
||||
char *key;
|
||||
char submodule_token[5];
|
||||
char unmerged_prefix = 'u';
|
||||
char eol_char = s->null_termination ? '\0' : '\n';
|
||||
|
||||
wt_porcelain_v2_submodule_state(d, submodule_token);
|
||||
|
||||
switch (d->stagemask) {
|
||||
case 1: key = "DD"; break; /* both deleted */
|
||||
case 2: key = "AU"; break; /* added by us */
|
||||
case 3: key = "UD"; break; /* deleted by them */
|
||||
case 4: key = "UA"; break; /* added by them */
|
||||
case 5: key = "DU"; break; /* deleted by us */
|
||||
case 6: key = "AA"; break; /* both added */
|
||||
case 7: key = "UU"; break; /* both modified */
|
||||
default:
|
||||
die("BUG: unhandled unmerged status %x", d->stagemask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disregard d.aux.porcelain_v2 data that we accumulated
|
||||
* for the head and index columns during the scans and
|
||||
* replace with the actual stage data.
|
||||
*
|
||||
* Note that this is a last-one-wins for each the individual
|
||||
* stage [123] columns in the event of multiple cache entries
|
||||
* for same stage.
|
||||
*/
|
||||
memset(stages, 0, sizeof(stages));
|
||||
sum = 0;
|
||||
pos = cache_name_pos(it->string, strlen(it->string));
|
||||
assert(pos < 0);
|
||||
pos = -pos-1;
|
||||
while (pos < active_nr) {
|
||||
ce = active_cache[pos++];
|
||||
stage = ce_stage(ce);
|
||||
if (strcmp(ce->name, it->string) || !stage)
|
||||
break;
|
||||
stages[stage - 1].mode = ce->ce_mode;
|
||||
hashcpy(stages[stage - 1].oid.hash, ce->sha1);
|
||||
sum |= (1 << (stage - 1));
|
||||
}
|
||||
if (sum != d->stagemask)
|
||||
die("BUG: observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask);
|
||||
|
||||
if (s->null_termination)
|
||||
path_index = it->string;
|
||||
else
|
||||
path_index = quote_path(it->string, s->prefix, &buf_index);
|
||||
|
||||
fprintf(s->fp, "%c %s %s %06o %06o %06o %06o %s %s %s %s%c",
|
||||
unmerged_prefix, key, submodule_token,
|
||||
stages[0].mode, /* stage 1 */
|
||||
stages[1].mode, /* stage 2 */
|
||||
stages[2].mode, /* stage 3 */
|
||||
d->mode_worktree,
|
||||
oid_to_hex(&stages[0].oid), /* stage 1 */
|
||||
oid_to_hex(&stages[1].oid), /* stage 2 */
|
||||
oid_to_hex(&stages[2].oid), /* stage 3 */
|
||||
path_index,
|
||||
eol_char);
|
||||
|
||||
strbuf_release(&buf_index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain V2 status info for untracked and ignored entries.
|
||||
*/
|
||||
static void wt_porcelain_v2_print_other(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s,
|
||||
char prefix)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
const char *path;
|
||||
char eol_char;
|
||||
|
||||
if (s->null_termination) {
|
||||
path = it->string;
|
||||
eol_char = '\0';
|
||||
} else {
|
||||
path = quote_path(it->string, s->prefix, &buf);
|
||||
eol_char = '\n';
|
||||
}
|
||||
|
||||
fprintf(s->fp, "%c %s%c", prefix, path, eol_char);
|
||||
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain V2 status.
|
||||
*
|
||||
* [<v2_branch>]
|
||||
* [<v2_changed_items>]*
|
||||
* [<v2_unmerged_items>]*
|
||||
* [<v2_untracked_items>]*
|
||||
* [<v2_ignored_items>]*
|
||||
*
|
||||
*/
|
||||
static void wt_porcelain_v2_print(struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d;
|
||||
struct string_list_item *it;
|
||||
int i;
|
||||
|
||||
if (s->show_branch)
|
||||
wt_porcelain_v2_print_tracking(s);
|
||||
|
||||
for (i = 0; i < s->change.nr; i++) {
|
||||
it = &(s->change.items[i]);
|
||||
d = it->util;
|
||||
if (!d->stagemask)
|
||||
wt_porcelain_v2_print_changed_entry(it, s);
|
||||
}
|
||||
|
||||
for (i = 0; i < s->change.nr; i++) {
|
||||
it = &(s->change.items[i]);
|
||||
d = it->util;
|
||||
if (d->stagemask)
|
||||
wt_porcelain_v2_print_unmerged_entry(it, s);
|
||||
}
|
||||
|
||||
for (i = 0; i < s->untracked.nr; i++) {
|
||||
it = &(s->untracked.items[i]);
|
||||
wt_porcelain_v2_print_other(it, s, '?');
|
||||
}
|
||||
|
||||
for (i = 0; i < s->ignored.nr; i++) {
|
||||
it = &(s->ignored.items[i]);
|
||||
wt_porcelain_v2_print_other(it, s, '!');
|
||||
}
|
||||
}
|
||||
|
||||
void wt_status_print(struct wt_status *s)
|
||||
{
|
||||
switch (s->status_format) {
|
||||
case STATUS_FORMAT_SHORT:
|
||||
wt_shortstatus_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_PORCELAIN:
|
||||
wt_porcelain_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_PORCELAIN_V2:
|
||||
wt_porcelain_v2_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_UNSPECIFIED:
|
||||
die("BUG: finalize_deferred_config() should have been called");
|
||||
break;
|
||||
case STATUS_FORMAT_NONE:
|
||||
case STATUS_FORMAT_LONG:
|
||||
wt_longstatus_print(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user