unpack-trees: support super-prefix option

In the future we want to support working tree operations within submodules,
e.g. "git checkout --recurse-submodules", which will update the submodule
to the commit as recorded in its superproject. In the submodule the
unpack-tree operation is carried out as usual, but the reporting to the
user needs to prefix any path with the superproject. The mechanism for
this is the super-prefix. (see 74866d757, git: make super-prefix option)

Add support for the super-prefix option for commands that unpack trees
by wrapping any path output in unpacking trees in the newly introduced
super_prefixed function. This new function prefixes any path with the
super-prefix if there is one.  Assuming the submodule case doesn't happen
in the majority of the cases, we'd want to have a fast behavior for no
super prefix, i.e. no reallocation/copying, but just returning path.

Another aspect of introducing the `super_prefixed` function is to consider
who owns the memory and if this is the right place where the path gets
modified. As the super prefix ought to change the output behavior only and
not the actual unpack tree part, it is fine to be that late in the line.
As we get passed in 'const char *path', we cannot change the path itself,
which means in case of a super prefix we have to copy over the path.
We need two static buffers in that function as the error messages
contain at most two paths.

For testing purposes enable it in read-tree, which has no output
of paths other than an unpack-trees.c. These are all converted in
this patch.

Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Stefan Beller 2017-01-17 17:05:20 -08:00 committed by Junio C Hamano
parent 83587d5236
commit 3d415425c7
3 changed files with 49 additions and 4 deletions

View File

@ -471,7 +471,7 @@ static struct cmd_struct commands[] = {
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
{ "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
{ "push", cmd_push, RUN_SETUP },
{ "read-tree", cmd_read_tree, RUN_SETUP },
{ "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },

View File

@ -363,6 +363,14 @@ test_expect_success 'a/b (untracked) vs a, plus c/d case test.' '
test -f a/b
test_expect_success 'read-tree supports the super-prefix' '
cat <<-EOF >expect &&
error: Updating '\''fictional/a'\'' would lose untracked files in it
test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual &&
test_cmp expect actual
test_expect_success 'a/b vs a, plus c/d case setup.' '
rm -f .git/index &&
rm -fr a &&

View File

@ -52,6 +52,41 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
? ((o)->msgs[(type)]) \
: (unpack_plumbing_errors[(type)]) )
static const char *super_prefixed(const char *path)
* It is necessary and sufficient to have two static buffers
* here, as the return value of this function is fed to
* error() using the unpack_*_errors[] templates we see above.
static struct strbuf buf[2] = {STRBUF_INIT, STRBUF_INIT};
static int super_prefix_len = -1;
static unsigned idx = ARRAY_SIZE(buf) - 1;
if (super_prefix_len < 0) {
const char *super_prefix = get_super_prefix();
if (!super_prefix) {
super_prefix_len = 0;
} else {
int i;
for (i = 0; i < ARRAY_SIZE(buf); i++)
strbuf_addstr(&buf[i], super_prefix);
super_prefix_len = buf[0].len;
if (!super_prefix_len)
return path;
if (++idx >= ARRAY_SIZE(buf))
idx = 0;
strbuf_setlen(&buf[idx], super_prefix_len);
strbuf_addstr(&buf[idx], path);
return buf[idx].buf;
void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
const char *cmd)
@ -172,7 +207,7 @@ static int add_rejected_path(struct unpack_trees_options *o,
const char *path)
if (!o->show_all_errors)
return error(ERRORMSG(o, e), path);
return error(ERRORMSG(o, e), super_prefixed(path));
* Otherwise, insert in a list for future display by
@ -196,7 +231,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
something_displayed = 1;
for (i = 0; i < rejects->nr; i++)
strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
error(ERRORMSG(o, e), path.buf);
error(ERRORMSG(o, e), super_prefixed(path.buf));
string_list_clear(rejects, 0);
@ -1918,7 +1953,9 @@ int bind_merge(const struct cache_entry * const *src,
if (a && old)
return o->gently ? -1 :
error(ERRORMSG(o, ERROR_BIND_OVERLAP), a->name, old->name);
if (!a)
return keep_entry(old, o);