ls-files: fix bug when recursing with relative pathspec

When using the --recurse-submodules flag with a relative pathspec which includes "..", an error is produced inside the child process spawned for a submodule. When creating the pathspec struct in the child, the ".." is interpreted to mean "go up a directory" which causes an error stating that the path ".." is outside of the repository. While it is true that ".." is outside the scope of the submodule, it is confusing to a user who originally invoked the command where ".." was indeed still inside the scope of the superproject. Since the child process launched for the submodule has some context that it is operating underneath a superproject, this error could be avoided. This patch fixes the bug by passing the 'prefix' to the child process. Now each child process that works on a submodule has two points of reference to the superproject: (1) the 'super_prefix' which is the path from the root of the superproject down to root of the submodule and (2) the 'prefix' which is the path from the root of the superproject down to the directory where the user invoked the git command. With these two pieces of information a child process can correctly interpret the pathspecs provided by the user as well as being able to properly format its output relative to the directory the user invoked the original command from. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Brandon Williams committed Mar 17, 2017 at 10:22 UTC b2dfeb7c005b83145e9f61305658f5dac745482a
2 files changed +52 -12
builtin/ls-files.c
+13 -12
@@ -172,7 +172,9 @@ static void show_killed_files(struct dir_struct *dir)
172 /*
173 * Compile an argv_array with all of the options supported by --recurse_submodules
174 */
175 -static void compile_submodule_options(const struct dir_struct *dir, int show_tag)
175 +static void compile_submodule_options(const char **argv,
176 + const struct dir_struct *dir,
177 + int show_tag)
178 {
179 if (line_terminator == '\0')
180 argv_array_push(&submodule_options, "-z");
@@ -186,6 +188,11 @@ static void compile_submodule_options(const struct dir_struct *dir, int show_tag
188 argv_array_push(&submodule_options, "--eol");
189 if (debug_mode)
190 argv_array_push(&submodule_options, "--debug");
191 +
192 + /* Add Pathspecs */
193 + argv_array_push(&submodule_options, "--");
194 + for (; *argv; argv++)
195 + argv_array_push(&submodule_options, *argv);
196 }
197
198 /**
@@ -195,8 +202,11 @@ static void show_gitlink(const struct cache_entry *ce)
202 {
203 struct child_process cp = CHILD_PROCESS_INIT;
204 int status;
198 - int i;
205
206 + if (prefix_len)
207 + argv_array_pushf(&cp.env_array, "%s=%s",
208 + GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
209 + prefix);
210 argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
211 super_prefix ? super_prefix : "",
212 ce->name);
@@ -206,15 +216,6 @@ static void show_gitlink(const struct cache_entry *ce)
216 /* add supported options */
217 argv_array_pushv(&cp.args, submodule_options.argv);
218
209 - /*
210 - * Pass in the original pathspec args. The submodule will be
211 - * responsible for prepending the 'submodule_prefix' prior to comparing
212 - * against the pathspec for matches.
213 - */
214 - argv_array_push(&cp.args, "--");
215 - for (i = 0; i < pathspec.nr; i++)
216 - argv_array_push(&cp.args, pathspec.items[i].original);
217 -
219 cp.git_cmd = 1;
220 cp.dir = ce->name;
221 status = run_command(&cp);
@@ -604,7 +605,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
605 setup_work_tree();
606
607 if (recurse_submodules)
607 - compile_submodule_options(&dir, show_tag);
608 + compile_submodule_options(argv, &dir, show_tag);
609
610 if (recurse_submodules &&
611 (show_stage || show_deleted || show_others || show_unmerged ||
t/t3007-ls-files-recurse-submodules.sh
+39
@@ -188,6 +188,45 @@ test_expect_success '--recurse-submodules and pathspecs' '
188 test_cmp expect actual
189 '
190
191 +test_expect_success '--recurse-submodules and relative paths' '
192 + # From subdir
193 + cat >expect <<-\EOF &&
194 + b
195 + EOF
196 + git -C b ls-files --recurse-submodules >actual &&
197 + test_cmp expect actual &&
198 +
199 + # Relative path to top
200 + cat >expect <<-\EOF &&
201 + ../.gitmodules
202 + ../a
203 + b
204 + ../h.txt
205 + ../sib/file
206 + ../sub/file
207 + ../submodule/.gitmodules
208 + ../submodule/c
209 + ../submodule/f.TXT
210 + ../submodule/g.txt
211 + ../submodule/subsub/d
212 + ../submodule/subsub/e.txt
213 + EOF
214 + git -C b ls-files --recurse-submodules -- .. >actual &&
215 + test_cmp expect actual &&
216 +
217 + # Relative path to submodule
218 + cat >expect <<-\EOF &&
219 + ../submodule/.gitmodules
220 + ../submodule/c
221 + ../submodule/f.TXT
222 + ../submodule/g.txt
223 + ../submodule/subsub/d
224 + ../submodule/subsub/e.txt
225 + EOF
226 + git -C b ls-files --recurse-submodules -- ../submodule >actual &&
227 + test_cmp expect actual
228 +'
229 +
230 test_expect_success '--recurse-submodules does not support --error-unmatch' '
231 test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual &&
232 test_i18ngrep "does not support --error-unmatch" actual