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>

Stefan Beller committed Jan 17, 2017 at 17:05 UTC 3d415425c7b6ca0d2243a4589672369e7a02fc2c
3 files changed +49 -4
git.c
+1 -1
@@ -471,7 +471,7 @@ static struct cmd_struct commands[] = {
471 { "prune-packed", cmd_prune_packed, RUN_SETUP },
472 { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
473 { "push", cmd_push, RUN_SETUP },
474 - { "read-tree", cmd_read_tree, RUN_SETUP },
474 + { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
475 { "receive-pack", cmd_receive_pack },
476 { "reflog", cmd_reflog, RUN_SETUP },
477 { "remote", cmd_remote, RUN_SETUP },
t/t1001-read-tree-m-2way.sh
+8
@@ -363,6 +363,14 @@ test_expect_success 'a/b (untracked) vs a, plus c/d case test.' '
363 test -f a/b
364 '
365
366 +test_expect_success 'read-tree supports the super-prefix' '
367 + cat <<-EOF >expect &&
368 + error: Updating '\''fictional/a'\'' would lose untracked files in it
369 + EOF
370 + test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual &&
371 + test_cmp expect actual
372 +'
373 +
374 test_expect_success 'a/b vs a, plus c/d case setup.' '
375 rm -f .git/index &&
376 rm -fr a &&
unpack-trees.c
+40 -3
@@ -52,6 +52,41 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
52 ? ((o)->msgs[(type)]) \
53 : (unpack_plumbing_errors[(type)]) )
54
55 +static const char *super_prefixed(const char *path)
56 +{
57 + /*
58 + * It is necessary and sufficient to have two static buffers
59 + * here, as the return value of this function is fed to
60 + * error() using the unpack_*_errors[] templates we see above.
61 + */
62 + static struct strbuf buf[2] = {STRBUF_INIT, STRBUF_INIT};
63 + static int super_prefix_len = -1;
64 + static unsigned idx = ARRAY_SIZE(buf) - 1;
65 +
66 + if (super_prefix_len < 0) {
67 + const char *super_prefix = get_super_prefix();
68 + if (!super_prefix) {
69 + super_prefix_len = 0;
70 + } else {
71 + int i;
72 + for (i = 0; i < ARRAY_SIZE(buf); i++)
73 + strbuf_addstr(&buf[i], super_prefix);
74 + super_prefix_len = buf[0].len;
75 + }
76 + }
77 +
78 + if (!super_prefix_len)
79 + return path;
80 +
81 + if (++idx >= ARRAY_SIZE(buf))
82 + idx = 0;
83 +
84 + strbuf_setlen(&buf[idx], super_prefix_len);
85 + strbuf_addstr(&buf[idx], path);
86 +
87 + return buf[idx].buf;
88 +}
89 +
90 void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
91 const char *cmd)
92 {
@@ -172,7 +207,7 @@ static int add_rejected_path(struct unpack_trees_options *o,
207 const char *path)
208 {
209 if (!o->show_all_errors)
175 - return error(ERRORMSG(o, e), path);
210 + return error(ERRORMSG(o, e), super_prefixed(path));
211
212 /*
213 * Otherwise, insert in a list for future display by
@@ -196,7 +231,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
231 something_displayed = 1;
232 for (i = 0; i < rejects->nr; i++)
233 strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
199 - error(ERRORMSG(o, e), path.buf);
234 + error(ERRORMSG(o, e), super_prefixed(path.buf));
235 strbuf_release(&path);
236 }
237 string_list_clear(rejects, 0);
@@ -1918,7 +1953,9 @@ int bind_merge(const struct cache_entry * const *src,
1953 o->merge_size);
1954 if (a && old)
1955 return o->gently ? -1 :
1921 - error(ERRORMSG(o, ERROR_BIND_OVERLAP), a->name, old->name);
1956 + error(ERRORMSG(o, ERROR_BIND_OVERLAP),
1957 + super_prefixed(a->name),
1958 + super_prefixed(old->name));
1959 if (!a)
1960 return keep_entry(old, o);
1961 else