Code clean-up with help from coccinelle tool continues.
* rs/cocci:
coccicheck: make transformation for strbuf_addf(sb, "...") more precise
use strbuf_add_unique_abbrev() for adding short hashes, part 2
use strbuf_addstr() instead of strbuf_addf() with "%s", part 2
gitignore: ignore output files of coccicheck make target
if (ce_stage(ce))
printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
else
- printf("%06o %s %d\t", ce->ce_mode, sha1_to_hex(ce->sha1), ce_stage(ce));
+ printf("%06o %s %d\t", ce->ce_mode,
+ oid_to_hex(&ce->oid), ce_stage(ce));
utf8_fprintf(stdout, "%s\n", ce->name);
}
}
static int clone_submodule(const char *path, const char *gitdir, const char *url,
- const char *depth, const char *reference, int quiet)
+ const char *depth, struct string_list *reference,
+ int quiet, int progress)
{
struct child_process cp = CHILD_PROCESS_INIT;
argv_array_push(&cp.args, "--no-checkout");
if (quiet)
argv_array_push(&cp.args, "--quiet");
+ if (progress)
+ argv_array_push(&cp.args, "--progress");
if (depth && *depth)
argv_array_pushl(&cp.args, "--depth", depth, NULL);
- if (reference && *reference)
- argv_array_pushl(&cp.args, "--reference", reference, NULL);
+ if (reference->nr) {
+ struct string_list_item *item;
+ for_each_string_list_item(item, reference)
+ argv_array_pushl(&cp.args, "--reference",
+ item->string, NULL);
+ }
if (gitdir && *gitdir)
argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
return run_command(&cp);
}
+struct submodule_alternate_setup {
+ const char *submodule_name;
+ enum SUBMODULE_ALTERNATE_ERROR_MODE {
+ SUBMODULE_ALTERNATE_ERROR_DIE,
+ SUBMODULE_ALTERNATE_ERROR_INFO,
+ SUBMODULE_ALTERNATE_ERROR_IGNORE
+ } error_mode;
+ struct string_list *reference;
+};
+#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
+ SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
+
+static int add_possible_reference_from_superproject(
+ struct alternate_object_database *alt, void *sas_cb)
+{
+ struct submodule_alternate_setup *sas = sas_cb;
+
+ /* directory name, minus trailing slash */
+ size_t namelen = alt->name - alt->base - 1;
+ struct strbuf name = STRBUF_INIT;
+ strbuf_add(&name, alt->base, namelen);
+
+ /*
+ * If the alternate object store is another repository, try the
+ * standard layout with .git/modules/<name>/objects
+ */
+ if (ends_with(name.buf, ".git/objects")) {
+ char *sm_alternate;
+ struct strbuf sb = STRBUF_INIT;
+ struct strbuf err = STRBUF_INIT;
+ strbuf_add(&sb, name.buf, name.len - strlen("objects"));
+ /*
+ * We need to end the new path with '/' to mark it as a dir,
+ * otherwise a submodule name containing '/' will be broken
+ * as the last part of a missing submodule reference would
+ * be taken as a file name.
+ */
+ strbuf_addf(&sb, "modules/%s/", sas->submodule_name);
+
+ sm_alternate = compute_alternate_path(sb.buf, &err);
+ if (sm_alternate) {
+ string_list_append(sas->reference, xstrdup(sb.buf));
+ free(sm_alternate);
+ } else {
+ switch (sas->error_mode) {
+ case SUBMODULE_ALTERNATE_ERROR_DIE:
+ die(_("submodule '%s' cannot add alternate: %s"),
+ sas->submodule_name, err.buf);
+ case SUBMODULE_ALTERNATE_ERROR_INFO:
+ fprintf(stderr, _("submodule '%s' cannot add alternate: %s"),
+ sas->submodule_name, err.buf);
+ case SUBMODULE_ALTERNATE_ERROR_IGNORE:
+ ; /* nothing */
+ }
+ }
+ strbuf_release(&sb);
+ }
+
+ strbuf_release(&name);
+ return 0;
+}
+
+static void prepare_possible_alternates(const char *sm_name,
+ struct string_list *reference)
+{
+ char *sm_alternate = NULL, *error_strategy = NULL;
+ struct submodule_alternate_setup sas = SUBMODULE_ALTERNATE_SETUP_INIT;
+
+ git_config_get_string("submodule.alternateLocation", &sm_alternate);
+ if (!sm_alternate)
+ return;
+
+ git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
+
+ if (!error_strategy)
+ error_strategy = xstrdup("die");
+
+ sas.submodule_name = sm_name;
+ sas.reference = reference;
+ if (!strcmp(error_strategy, "die"))
+ sas.error_mode = SUBMODULE_ALTERNATE_ERROR_DIE;
+ else if (!strcmp(error_strategy, "info"))
+ sas.error_mode = SUBMODULE_ALTERNATE_ERROR_INFO;
+ else if (!strcmp(error_strategy, "ignore"))
+ sas.error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE;
+ else
+ die(_("Value '%s' for submodule.alternateErrorStrategy is not recognized"), error_strategy);
+
+ if (!strcmp(sm_alternate, "superproject"))
+ foreach_alt_odb(add_possible_reference_from_superproject, &sas);
+ else if (!strcmp(sm_alternate, "no"))
+ ; /* do nothing */
+ else
+ die(_("Value '%s' for submodule.alternateLocation is not recognized"), sm_alternate);
+
+ free(sm_alternate);
+ free(error_strategy);
+}
+
static int module_clone(int argc, const char **argv, const char *prefix)
{
- const char *name = NULL, *url = NULL;
- const char *reference = NULL, *depth = NULL;
+ const char *name = NULL, *url = NULL, *depth = NULL;
int quiet = 0;
+ int progress = 0;
FILE *submodule_dot_git;
char *p, *path = NULL, *sm_gitdir;
struct strbuf rel_path = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
+ struct string_list reference = STRING_LIST_INIT_NODUP;
struct option module_clone_options[] = {
OPT_STRING(0, "prefix", &prefix,
OPT_STRING(0, "url", &url,
N_("string"),
N_("url where to clone the submodule from")),
- OPT_STRING(0, "reference", &reference,
- N_("string"),
+ OPT_STRING_LIST(0, "reference", &reference,
+ N_("repo"),
N_("reference repository")),
OPT_STRING(0, "depth", &depth,
N_("string"),
N_("depth for shallow clones")),
OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
+ OPT_BOOL(0, "progress", &progress,
+ N_("force cloning progress")),
OPT_END()
};
if (!file_exists(sm_gitdir)) {
if (safe_create_leading_directories_const(sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
- if (clone_submodule(path, sm_gitdir, url, depth, reference, quiet))
+
+ prepare_possible_alternates(name, &reference);
+
+ if (clone_submodule(path, sm_gitdir, url, depth, &reference,
+ quiet, progress))
die(_("clone of '%s' into submodule path '%s' failed"),
url, path);
} else {
struct submodule_update_strategy update;
/* configuration parameters which are passed on to the children */
+ int progress;
int quiet;
int recommend_shallow;
- const char *reference;
+ struct string_list references;
const char *depth;
const char *recursive_prefix;
const char *prefix;
int failed_clones_nr, failed_clones_alloc;
};
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
- SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \
+ SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \
+ NULL, NULL, NULL, \
STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
if (suc->recursive_prefix)
strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
else
- strbuf_addf(&sb, "%s", ce->name);
+ strbuf_addstr(&sb, ce->name);
strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
strbuf_addch(out, '\n');
goto cleanup;
strbuf_reset(&sb);
strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
- sha1_to_hex(ce->sha1), ce_stage(ce),
+ oid_to_hex(&ce->oid), ce_stage(ce),
needs_cloning, ce->name);
string_list_append(&suc->projectlines, sb.buf);
child->err = -1;
argv_array_push(&child->args, "submodule--helper");
argv_array_push(&child->args, "clone");
+ if (suc->progress)
+ argv_array_push(&child->args, "--progress");
if (suc->quiet)
argv_array_push(&child->args, "--quiet");
if (suc->prefix)
argv_array_pushl(&child->args, "--path", sub->path, NULL);
argv_array_pushl(&child->args, "--name", sub->name, NULL);
argv_array_pushl(&child->args, "--url", url, NULL);
- if (suc->reference)
- argv_array_push(&child->args, suc->reference);
+ if (suc->references.nr) {
+ struct string_list_item *item;
+ for_each_string_list_item(item, &suc->references)
+ argv_array_pushl(&child->args, "--reference", item->string, NULL);
+ }
if (suc->depth)
argv_array_push(&child->args, suc->depth);
OPT_STRING(0, "update", &update,
N_("string"),
N_("rebase, merge, checkout or none")),
- OPT_STRING(0, "reference", &suc.reference, N_("repo"),
+ OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"),
N_("reference repository")),
OPT_STRING(0, "depth", &suc.depth, "<depth>",
N_("Create a shallow clone truncated to the "
OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
N_("whether the initial clone should follow the shallow recommendation")),
OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+ OPT_BOOL(0, "progress", &suc.progress,
+ N_("force cloning progress")),
OPT_END()
};
#include "ll-merge.h"
#include "string-list.h"
#include "argv-array.h"
+#include "graph.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
#endif
static int diff_detect_rename_default;
+static int diff_indent_heuristic; /* experimental */
static int diff_compaction_heuristic; /* experimental */
static int diff_rename_limit_default = 400;
static int diff_suppress_blank_empty;
GIT_COLOR_NORMAL, /* FUNCINFO */
};
+static NORETURN void die_want_option(const char *option_name)
+{
+ die(_("option '%s' requires a value"), option_name);
+}
+
static int parse_diff_color_slot(const char *var)
{
if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
static int parse_submodule_params(struct diff_options *options, const char *value)
{
if (!strcmp(value, "log"))
- DIFF_OPT_SET(options, SUBMODULE_LOG);
+ options->submodule_format = DIFF_SUBMODULE_LOG;
else if (!strcmp(value, "short"))
- DIFF_OPT_CLR(options, SUBMODULE_LOG);
+ options->submodule_format = DIFF_SUBMODULE_SHORT;
+ else if (!strcmp(value, "diff"))
+ options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
else
return -1;
return 0;
diff_detect_rename_default = 1;
}
+int git_diff_heuristic_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "diff.indentheuristic")) {
+ diff_indent_heuristic = git_config_bool(var, value);
+ if (diff_indent_heuristic)
+ diff_compaction_heuristic = 0;
+ }
+ if (!strcmp(var, "diff.compactionheuristic")) {
+ diff_compaction_heuristic = git_config_bool(var, value);
+ if (diff_compaction_heuristic)
+ diff_indent_heuristic = 0;
+ }
+ return 0;
+}
+
int git_diff_ui_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
diff_detect_rename_default = git_config_rename(var, value);
return 0;
}
- if (!strcmp(var, "diff.compactionheuristic")) {
- diff_compaction_heuristic = git_config_bool(var, value);
- return 0;
- }
if (!strcmp(var, "diff.autorefreshindex")) {
diff_auto_refresh_index = git_config_bool(var, value);
return 0;
return 0;
}
+ if (git_diff_heuristic_config(var, value, cb) < 0)
+ return -1;
if (git_color_config(var, value, cb) < 0)
return -1;
const char **label_path;
struct diff_words_data *diff_words;
struct diff_options *opt;
- int *found_changesp;
struct strbuf *header;
};
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.color_diff = want_color(o->use_color);
- ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b);
ecbdata.opt = o;
if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
{
if (word_regex && *begin < buffer->size) {
regmatch_t match[1];
- if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+ if (!regexec_buf(word_regex, buffer->ptr + *begin,
+ buffer->size - *begin, 1, match, 0)) {
char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
'\n', match[0].rm_eo - match[0].rm_so);
*end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
struct diff_options *o = ecbdata->opt;
const char *line_prefix = diff_line_prefix(o);
+ o->found_changes = 1;
+
if (ecbdata->header) {
- fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
+ fprintf(o->file, "%s", ecbdata->header->buf);
strbuf_reset(ecbdata->header);
ecbdata->header = NULL;
}
- *(ecbdata->found_changesp) = 1;
if (ecbdata->label_path[0]) {
const char *name_a_tab, *name_b_tab;
name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
- fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
+ fprintf(o->file, "%s%s--- %s%s%s\n",
line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
- fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
+ fprintf(o->file, "%s%s+++ %s%s%s\n",
line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
find_lno(line, ecbdata);
emit_hunk_header(ecbdata, line, len);
if (line[len-1] != '\n')
- putc('\n', ecbdata->opt->file);
- return;
- }
-
- if (len < 1) {
- emit_line(ecbdata->opt, reset, reset, line, len);
- if (ecbdata->diff_words
- && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
- fputs("~\n", ecbdata->opt->file);
+ putc('\n', o->file);
return;
}
}
diff_words_flush(ecbdata);
if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
- emit_line(ecbdata->opt, context, reset, line, len);
- fputs("~\n", ecbdata->opt->file);
+ emit_line(o, context, reset, line, len);
+ fputs("~\n", o->file);
} else {
/*
* Skip the prefix character, if any. With
line++;
len--;
}
- emit_line(ecbdata->opt, context, reset, line, len);
+ emit_line(o, context, reset, line, len);
}
return;
}
default:
/* incomplete line at the end */
ecbdata->lno_in_preimage++;
- emit_line(ecbdata->opt,
- diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
+ emit_line(o, diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
reset, line, len);
break;
}
*/
if (options->stat_width == -1)
- width = term_columns() - options->output_prefix_length;
+ width = term_columns() - strlen(line_prefix);
else
width = options->stat_width ? options->stat_width : 80;
number_width = decimal_width(max_change) > number_width ?
struct strbuf header = STRBUF_INIT;
const char *line_prefix = diff_line_prefix(o);
- if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
- (!one->mode || S_ISGITLINK(one->mode)) &&
- (!two->mode || S_ISGITLINK(two->mode))) {
+ diff_set_mnemonic_prefix(o, "a/", "b/");
+ if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ a_prefix = o->b_prefix;
+ b_prefix = o->a_prefix;
+ } else {
+ a_prefix = o->a_prefix;
+ b_prefix = o->b_prefix;
+ }
+
+ if (o->submodule_format == DIFF_SUBMODULE_LOG &&
+ (!one->mode || S_ISGITLINK(one->mode)) &&
+ (!two->mode || S_ISGITLINK(two->mode))) {
const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
show_submodule_summary(o->file, one->path ? one->path : two->path,
line_prefix,
- one->oid.hash, two->oid.hash,
+ &one->oid, &two->oid,
two->dirty_submodule,
meta, del, add, reset);
return;
+ } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
+ (!one->mode || S_ISGITLINK(one->mode)) &&
+ (!two->mode || S_ISGITLINK(two->mode))) {
+ const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
+ const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
+ show_submodule_inline_diff(o->file, one->path ? one->path : two->path,
+ line_prefix,
+ &one->oid, &two->oid,
+ two->dirty_submodule,
+ meta, del, add, reset, o);
+ return;
}
if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
textconv_two = get_textconv(two);
}
- diff_set_mnemonic_prefix(o, "a/", "b/");
- if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
- a_prefix = o->b_prefix;
- b_prefix = o->a_prefix;
- } else {
- a_prefix = o->a_prefix;
- b_prefix = o->b_prefix;
- }
-
/* Never use a non-valid filename anywhere if at all possible */
name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
ecbdata.color_diff = want_color(o->use_color);
- ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b);
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
check_blank_at_eof(&mf1, &mf2, &ecbdata);
* This is not the sha1 we are looking for, or
* unreusable because it is not a regular file.
*/
- if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
+ if (hashcmp(sha1, ce->oid.hash) || !S_ISREG(ce->ce_mode))
return 0;
/*
}
strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
find_unique_abbrev(one->oid.hash, abbrev));
- strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
+ strbuf_add_unique_abbrev(msg, two->oid.hash, abbrev);
if (one->mode == two->mode)
strbuf_addf(msg, " %06o", one->mode);
strbuf_addf(msg, "%s\n", reset);
options->use_color = diff_use_color_default;
options->detect_rename = diff_detect_rename_default;
options->xdl_opts |= diff_algorithm;
- if (diff_compaction_heuristic)
+ if (diff_indent_heuristic)
+ DIFF_XDL_SET(options, INDENT_HEURISTIC);
+ else if (diff_compaction_heuristic)
DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
options->orderfile = diff_order_file_cfg;
if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
count++;
if (count > 1)
- die("--name-only, --name-status, --check and -s are mutually exclusive");
+ die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
/*
* Most of the time we can say "there are changes"
if (*arg == '=')
width = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
- die("Option '--stat-width' requires a value");
+ die_want_option("--stat-width");
else if (!*arg) {
width = strtoul(av[1], &end, 10);
argcount = 2;
if (*arg == '=')
name_width = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
- die("Option '--stat-name-width' requires a value");
+ die_want_option("--stat-name-width");
else if (!*arg) {
name_width = strtoul(av[1], &end, 10);
argcount = 2;
if (*arg == '=')
graph_width = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
- die("Option '--stat-graph-width' requires a value");
+ die_want_option("--stat-graph-width");
else if (!*arg) {
graph_width = strtoul(av[1], &end, 10);
argcount = 2;
if (*arg == '=')
count = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
- die("Option '--stat-count' requires a value");
+ die_want_option("--stat-count");
else if (!*arg) {
count = strtoul(av[1], &end, 10);
argcount = 2;
DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
else if (!strcmp(arg, "--ignore-blank-lines"))
DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
- else if (!strcmp(arg, "--compaction-heuristic"))
+ else if (!strcmp(arg, "--indent-heuristic")) {
+ DIFF_XDL_SET(options, INDENT_HEURISTIC);
+ DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
+ } else if (!strcmp(arg, "--no-indent-heuristic"))
+ DIFF_XDL_CLR(options, INDENT_HEURISTIC);
+ else if (!strcmp(arg, "--compaction-heuristic")) {
DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
- else if (!strcmp(arg, "--no-compaction-heuristic"))
+ DIFF_XDL_CLR(options, INDENT_HEURISTIC);
+ } else if (!strcmp(arg, "--no-compaction-heuristic"))
DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
else if (!strcmp(arg, "--patience"))
options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, arg);
} else if (!strcmp(arg, "--submodule"))
- DIFF_OPT_SET(options, SUBMODULE_LOG);
+ options->submodule_format = DIFF_SUBMODULE_LOG;
else if (skip_prefix(arg, "--submodule=", &arg))
return parse_submodule_opt(options, arg);
else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
options->a_prefix = optarg;
return argcount;
}
+ else if ((argcount = parse_long_opt("line-prefix", av, &optarg))) {
+ options->line_prefix = optarg;
+ options->line_prefix_length = strlen(options->line_prefix);
+ graph_setup_line_prefix(options);
+ return argcount;
+ }
else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
options->b_prefix = optarg;
return argcount;
int ret = 0;
size_t alloc;
- strbuf_git_path_submodule(&objects_directory, path, "objects/");
+ ret = strbuf_git_path_submodule(&objects_directory, path, "objects/");
+ if (ret)
+ goto done;
if (!is_directory(objects_directory.buf)) {
ret = -1;
goto done;
static int prepare_submodule_summary(struct rev_info *rev, const char *path,
struct commit *left, struct commit *right,
- int *fast_forward, int *fast_backward)
+ struct commit_list *merge_bases)
{
- struct commit_list *merge_bases, *list;
+ struct commit_list *list;
init_revisions(rev, NULL);
setup_revisions(0, NULL, rev, NULL);
left->object.flags |= SYMMETRIC_LEFT;
add_pending_object(rev, &left->object, path);
add_pending_object(rev, &right->object, path);
- merge_bases = get_merge_bases(left, right);
- if (merge_bases) {
- if (merge_bases->item == left)
- *fast_forward = 1;
- else if (merge_bases->item == right)
- *fast_backward = 1;
- }
for (list = merge_bases; list; list = list->next) {
list->item->object.flags |= UNINTERESTING;
add_pending_object(rev, &list->item->object,
strbuf_release(&sb);
}
-void show_submodule_summary(FILE *f, const char *path,
+/* Helper function to display the submodule header line prior to the full
+ * summary output. If it can locate the submodule objects directory it will
+ * attempt to lookup both the left and right commits and put them into the
+ * left and right pointers.
+ */
+static void show_submodule_header(FILE *f, const char *path,
const char *line_prefix,
- unsigned char one[20], unsigned char two[20],
+ struct object_id *one, struct object_id *two,
unsigned dirty_submodule, const char *meta,
- const char *del, const char *add, const char *reset)
+ const char *reset,
+ struct commit **left, struct commit **right,
+ struct commit_list **merge_bases)
{
- struct rev_info rev;
- struct commit *left = NULL, *right = NULL;
const char *message = NULL;
struct strbuf sb = STRBUF_INIT;
int fast_forward = 0, fast_backward = 0;
- if (is_null_sha1(two))
- message = "(submodule deleted)";
- else if (add_submodule_odb(path))
- message = "(not checked out)";
- else if (is_null_sha1(one))
- message = "(new submodule)";
- else if (!(left = lookup_commit_reference(one)) ||
- !(right = lookup_commit_reference(two)))
- message = "(commits not present)";
- else if (prepare_submodule_summary(&rev, path, left, right,
- &fast_forward, &fast_backward))
- message = "(revision walker failed)";
-
if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
fprintf(f, "%sSubmodule %s contains untracked content\n",
line_prefix, path);
fprintf(f, "%sSubmodule %s contains modified content\n",
line_prefix, path);
- if (!hashcmp(one, two)) {
+ if (is_null_oid(one))
+ message = "(new submodule)";
+ else if (is_null_oid(two))
+ message = "(submodule deleted)";
+
+ if (add_submodule_odb(path)) {
+ if (!message)
+ message = "(not initialized)";
+ goto output_header;
+ }
+
+ /*
+ * Attempt to lookup the commit references, and determine if this is
+ * a fast forward or fast backwards update.
+ */
+ *left = lookup_commit_reference(one->hash);
+ *right = lookup_commit_reference(two->hash);
+
+ /*
+ * Warn about missing commits in the submodule project, but only if
+ * they aren't null.
+ */
+ if ((!is_null_oid(one) && !*left) ||
+ (!is_null_oid(two) && !*right))
+ message = "(commits not present)";
+
+ *merge_bases = get_merge_bases(*left, *right);
+ if (*merge_bases) {
+ if ((*merge_bases)->item == *left)
+ fast_forward = 1;
+ else if ((*merge_bases)->item == *right)
+ fast_backward = 1;
+ }
+
+ if (!oidcmp(one, two)) {
strbuf_release(&sb);
return;
}
+output_header:
strbuf_addf(&sb, "%s%sSubmodule %s %s..", line_prefix, meta, path,
- find_unique_abbrev(one, DEFAULT_ABBREV));
+ find_unique_abbrev(one->hash, DEFAULT_ABBREV));
if (!fast_backward && !fast_forward)
strbuf_addch(&sb, '.');
- strbuf_addf(&sb, "%s", find_unique_abbrev(two->hash, DEFAULT_ABBREV));
- strbuf_add_unique_abbrev(&sb, two, DEFAULT_ABBREV);
++ strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
if (message)
strbuf_addf(&sb, " %s%s\n", message, reset);
else
strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
fwrite(sb.buf, sb.len, 1, f);
- if (!message) /* only NULL if we succeeded in setting up the walk */
- print_submodule_summary(&rev, f, line_prefix, del, add, reset);
+ strbuf_release(&sb);
+}
+
+void show_submodule_summary(FILE *f, const char *path,
+ const char *line_prefix,
+ struct object_id *one, struct object_id *two,
+ unsigned dirty_submodule, const char *meta,
+ const char *del, const char *add, const char *reset)
+{
+ struct rev_info rev;
+ struct commit *left = NULL, *right = NULL;
+ struct commit_list *merge_bases = NULL;
+
+ show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
+ meta, reset, &left, &right, &merge_bases);
+
+ /*
+ * If we don't have both a left and a right pointer, there is no
+ * reason to try and display a summary. The header line should contain
+ * all the information the user needs.
+ */
+ if (!left || !right)
+ goto out;
+
+ /* Treat revision walker failure the same as missing commits */
+ if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
+ fprintf(f, "%s(revision walker failed)\n", line_prefix);
+ goto out;
+ }
+
+ print_submodule_summary(&rev, f, line_prefix, del, add, reset);
+
+out:
+ if (merge_bases)
+ free_commit_list(merge_bases);
+ clear_commit_marks(left, ~0);
+ clear_commit_marks(right, ~0);
+}
+
+void show_submodule_inline_diff(FILE *f, const char *path,
+ const char *line_prefix,
+ struct object_id *one, struct object_id *two,
+ unsigned dirty_submodule, const char *meta,
+ const char *del, const char *add, const char *reset,
+ const struct diff_options *o)
+{
+ const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
+ struct commit *left = NULL, *right = NULL;
+ struct commit_list *merge_bases = NULL;
+ struct strbuf submodule_dir = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
+ meta, reset, &left, &right, &merge_bases);
+
+ /* We need a valid left and right commit to display a difference */
+ if (!(left || is_null_oid(one)) ||
+ !(right || is_null_oid(two)))
+ goto done;
+
+ if (left)
+ old = one;
+ if (right)
+ new = two;
+
+ fflush(f);
+ cp.git_cmd = 1;
+ cp.dir = path;
+ cp.out = dup(fileno(f));
+ cp.no_stdin = 1;
+
+ /* TODO: other options may need to be passed here. */
+ argv_array_push(&cp.args, "diff");
+ argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
+ if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
+ o->b_prefix, path);
+ argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
+ o->a_prefix, path);
+ } else {
+ argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
+ o->a_prefix, path);
+ argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
+ o->b_prefix, path);
+ }
+ argv_array_push(&cp.args, oid_to_hex(old));
+ /*
+ * If the submodule has modified content, we will diff against the
+ * work tree, under the assumption that the user has asked for the
+ * diff format and wishes to actually see all differences even if they
+ * haven't yet been committed to the submodule yet.
+ */
+ if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
+ argv_array_push(&cp.args, oid_to_hex(new));
+
+ if (run_command(&cp))
+ fprintf(f, "(diff failed)\n");
+
+done:
+ strbuf_release(&submodule_dir);
+ if (merge_bases)
+ free_commit_list(merge_bases);
if (left)
clear_commit_marks(left, ~0);
if (right)
clear_commit_marks(right, ~0);
-
- strbuf_release(&sb);
}
void set_config_fetch_recurse_submodules(int value)
sha1_array_append(&ref_tips_after_fetch, new_sha1);
}
-static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
+static int add_sha1_to_argv(const unsigned char sha1[20], void *data)
{
argv_array_push(data, sha1_to_hex(sha1));
+ return 0;
}
static void calculate_changed_submodule_paths(void)
if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
argv_array_push(out, *var);
}
+ argv_array_push(out, "GIT_DIR=.git");
}
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;
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);
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);
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);
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", "");
}
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;
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);
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);
}
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;
+ }
+
}
}
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;
}
}
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->oid.hash);
+ }
}
}
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;
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;
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);
}
/*
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);
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;
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;
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;
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;
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;
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/";
}
}
-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;
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,
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)
_(" (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,
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,
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,
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);
}
/*
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));
+ strbuf_add_unique_abbrev(&cb->buf, nsha1, DEFAULT_ABBREV);
}
return 1;
}
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)
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);
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);
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,
? _(" (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"));
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;
}
}
-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;
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->oid.hash);
+ 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;
+ }
+}