git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Brandon Williams <bmwill@google.com>
To: git@vger.kernel.org
Cc: Brandon Williams <bmwill@google.com>
Subject: [PATCH 4/4 v4] ls-files: add pathspec matching for submodules
Date: Mon, 26 Sep 2016 15:46:43 -0700	[thread overview]
Message-ID: <1474930003-83750-5-git-send-email-bmwill@google.com> (raw)
In-Reply-To: <1474930003-83750-1-git-send-email-bmwill@google.com>

Pathspecs can be a bit tricky when trying to apply them to submodules.
The main challenge is that the pathspecs will be with respect to the
superproject and not with respect to paths in the submodule.  The
approach this patch takes is to pass in the identical pathspec from the
superproject to the submodule in addition to the submodule-prefix, which
is the path from the root of the superproject to the submodule, and then
we can compare an entry in the submodule prepended with the
submodule-prefix to the pathspec in order to determine if there is a
match.

This patch also permits the pathspec logic to perform a prefix match against
submodules since a pathspec could refer to a file inside of a submodule.
Due to limitations in the wildmatch logic, a prefix match is only done
literally.  If any wildcard character is encountered we'll simply punt
and produce a false positive match.  More accurate matching will be done
once inside the submodule.  This is due to the superproject not knowing
what files could exist in the submodule.

Signed-off-by: Brandon Williams <bmwill@google.com>
---
 builtin/ls-files.c                     |  28 ++++++---
 dir.c                                  |  46 +++++++++++++-
 dir.h                                  |   4 ++
 t/t3007-ls-files-recurse-submodules.sh | 108 ++++++++++++++++++++++++++++++++-
 4 files changed, 174 insertions(+), 12 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a39367f..2488a02 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -199,6 +199,7 @@ static void show_gitlink(const struct cache_entry *ce)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
 	int status;
+	int i;
 
 	argv_array_pushf(&cp.args, "--submodule-prefix=%s%s/",
 			 submodule_prefix ? submodule_prefix : "",
@@ -209,6 +210,15 @@ static void show_gitlink(const struct cache_entry *ce)
 	/* add supported options */
 	argv_array_pushv(&cp.args, recurse_submodules_opts.argv);
 
+	/*
+	 * Pass in the original pathspec args.  The submodule will be
+	 * responsible for prepending the 'submodule_prefix' prior to comparing
+	 * against the pathspec for matches.
+	 */
+	argv_array_push(&cp.args, "--");
+	for (i = 0; i < pathspec.nr; i++)
+		argv_array_push(&cp.args, pathspec.items[i].original);
+
 	cp.git_cmd = 1;
 	cp.dir = ce->name;
 	status = run_command(&cp);
@@ -227,7 +237,8 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 	if (len >= ce_namelen(ce))
 		die("git ls-files: internal error - cache entry not superset of prefix");
 
-	if (recurse_submodules && S_ISGITLINK(ce->ce_mode)) {
+	if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
+	    submodule_path_match(&pathspec, name.buf, ps_matched)) {
 		show_gitlink(ce);
 	} else if (match_pathspec(&pathspec, name.buf, name.len,
 				  len, ps_matched,
@@ -608,17 +619,20 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 		die("ls-files --recurse-submodules does not support "
 		    "--error-unmatch");
 
-	if (recurse_submodules && argc)
-		die("ls-files --recurse-submodules does not support path "
-		    "arguments");
-
 	parse_pathspec(&pathspec, 0,
 		       PATHSPEC_PREFER_CWD |
 		       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
 		       prefix, argv);
 
-	/* Find common prefix for all pathspec's */
-	max_prefix = common_prefix(&pathspec);
+	/*
+	 * Find common prefix for all pathspec's
+	 * This is used as a performance optimization which unfortunately cannot
+	 * be done when recursing into submodules
+	 */
+	if (recurse_submodules)
+		max_prefix = NULL;
+	else
+		max_prefix = common_prefix(&pathspec);
 	max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
 
 	/* Treat unmatching pathspec elements as errors */
diff --git a/dir.c b/dir.c
index 0ea235f..28e9736 100644
--- a/dir.c
+++ b/dir.c
@@ -207,8 +207,9 @@ int within_depth(const char *name, int namelen,
 	return 1;
 }
 
-#define DO_MATCH_EXCLUDE   1
-#define DO_MATCH_DIRECTORY 2
+#define DO_MATCH_EXCLUDE   (1<<0)
+#define DO_MATCH_DIRECTORY (1<<1)
+#define DO_MATCH_SUBMODULE (1<<2)
 
 /*
  * Does 'match' match the given name?
@@ -283,6 +284,32 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
 			 item->nowildcard_len - prefix))
 		return MATCHED_FNMATCH;
 
+	/* Perform checks to see if "name" is a super set of the pathspec */
+	if (flags & DO_MATCH_SUBMODULE) {
+		/* name is a literal prefix of the pathspec */
+		if ((namelen < matchlen) &&
+		    (match[namelen] == '/') &&
+		    !ps_strncmp(item, match, name, namelen))
+			return MATCHED_RECURSIVELY;
+
+		/* name" doesn't match up to the first wild character */
+		if (item->nowildcard_len < item->len &&
+		    ps_strncmp(item, match, name,
+			       item->nowildcard_len - prefix))
+			return 0;
+
+		/*
+		 * Here is where we would perform a wildmatch to check if
+		 * "name" can be matched as a directory (or a prefix) against
+		 * the pathspec.  Since wildmatch doesn't have this capability
+		 * at the present we have to punt and say that it is a match,
+		 * potentially returning a false positive
+		 * The submodules themselves will be able to perform more
+		 * accurate matching to determine if the pathspec matches.
+		 */
+		return MATCHED_RECURSIVELY;
+	}
+
 	return 0;
 }
 
@@ -386,6 +413,21 @@ int match_pathspec(const struct pathspec *ps,
 	return negative ? 0 : positive;
 }
 
+/**
+ * Check if a submodule is a superset of the pathspec
+ */
+int submodule_path_match(const struct pathspec *ps,
+			 const char *submodule_name,
+			 char *seen)
+{
+	int matched = do_match_pathspec(ps, submodule_name,
+					strlen(submodule_name),
+					0, seen,
+					DO_MATCH_DIRECTORY |
+					DO_MATCH_SUBMODULE);
+	return matched;
+}
+
 int report_path_error(const char *ps_matched,
 		      const struct pathspec *pathspec,
 		      const char *prefix)
diff --git a/dir.h b/dir.h
index da1a858..97c83bb 100644
--- a/dir.h
+++ b/dir.h
@@ -304,6 +304,10 @@ extern int git_fnmatch(const struct pathspec_item *item,
 		       const char *pattern, const char *string,
 		       int prefix);
 
+extern int submodule_path_match(const struct pathspec *ps,
+				const char *submodule_name,
+				char *seen);
+
 static inline int ce_path_match(const struct cache_entry *ce,
 				const struct pathspec *pathspec,
 				char *seen)
diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh
index 40767da..4a51d38 100755
--- a/t/t3007-ls-files-recurse-submodules.sh
+++ b/t/t3007-ls-files-recurse-submodules.sh
@@ -81,9 +81,111 @@ test_expect_success 'ls-files recurses more than 1 level' '
 	test_cmp expect actual
 '
 
-test_expect_success '--recurse-submodules does not support using path arguments' '
-	test_must_fail git ls-files --recurse-submodules b 2>actual &&
-	test_i18ngrep "does not support path arguments" actual
+test_expect_success '--recurse-submodules and pathspecs setup' '
+	echo e >submodule/subsub/e.txt &&
+	git -C submodule/subsub add e.txt &&
+	git -C submodule/subsub commit -m "adding e.txt" &&
+	echo f >submodule/f.TXT &&
+	echo g >submodule/g.txt &&
+	git -C submodule add f.TXT g.txt &&
+	git -C submodule commit -m "add f and g" &&
+	echo h >h.txt &&
+	mkdir sib &&
+	echo sib >sib/file &&
+	git add h.txt sib/file &&
+	git commit -m "add h and sib/file" &&
+	git init sub &&
+	echo sub >sub/file &&
+	git -C sub add file &&
+	git -C sub commit -m "add file" &&
+	git submodule add ./sub &&
+	git commit -m "added sub" &&
+
+	cat >expect <<-\EOF &&
+	.gitmodules
+	a
+	b/b
+	h.txt
+	sib/file
+	sub/file
+	submodule/.gitmodules
+	submodule/c
+	submodule/f.TXT
+	submodule/g.txt
+	submodule/subsub/d
+	submodule/subsub/e.txt
+	EOF
+
+	git ls-files --recurse-submodules >actual &&
+	test_cmp expect actual &&
+	cat actual &&
+	git ls-files --recurse-submodules "*" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+	cat >expect <<-\EOF &&
+	h.txt
+	submodule/g.txt
+	submodule/subsub/e.txt
+	EOF
+
+	git ls-files --recurse-submodules "*.txt" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+	cat >expect <<-\EOF &&
+	h.txt
+	submodule/f.TXT
+	submodule/g.txt
+	submodule/subsub/e.txt
+	EOF
+
+	git ls-files --recurse-submodules ":(icase)*.txt" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+	cat >expect <<-\EOF &&
+	h.txt
+	submodule/f.TXT
+	submodule/g.txt
+	EOF
+
+	git ls-files --recurse-submodules ":(icase)*.txt" ":(exclude)submodule/subsub/*" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+	cat >expect <<-\EOF &&
+	sub/file
+	EOF
+
+	git ls-files --recurse-submodules "sub" >actual &&
+	test_cmp expect actual &&
+	git ls-files --recurse-submodules "sub/" >actual &&
+	test_cmp expect actual &&
+	git ls-files --recurse-submodules "sub/file" >actual &&
+	test_cmp expect actual &&
+	git ls-files --recurse-submodules "su*/file" >actual &&
+	test_cmp expect actual &&
+	git ls-files --recurse-submodules "su?/file" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+	cat >expect <<-\EOF &&
+	sib/file
+	sub/file
+	EOF
+
+	git ls-files --recurse-submodules "s??/file" >actual &&
+	test_cmp expect actual &&
+	git ls-files --recurse-submodules "s???file" >actual &&
+	test_cmp expect actual &&
+	git ls-files --recurse-submodules "s*file" >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success '--recurse-submodules does not support --error-unmatch' '
-- 
2.8.0.rc3.226.g39d4020


  parent reply	other threads:[~2016-09-26 22:47 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-24  0:13 [PATCH 0/3] recursive support for ls-files Brandon Williams
2016-09-24  0:13 ` [PATCH 1/3 v3] submodules: make submodule-prefix option an envvar Brandon Williams
2016-09-25 23:34   ` Junio C Hamano
2016-09-24  0:13 ` [PATCH 2/3 v3] ls-files: optionally recurse into submodules Brandon Williams
2016-09-24  0:13 ` [PATCH 3/3 v3] ls-files: add pathspec matching for submodules Brandon Williams
2016-09-25  7:17 ` [PATCH 0/3] recursive support for ls-files Jeff King
2016-09-25 16:32   ` Brandon Williams
2016-09-25 18:38     ` Junio C Hamano
2016-09-26 17:04       ` Brandon Williams
2016-09-26 18:17         ` Junio C Hamano
2016-09-26 18:38           ` Brandon Williams
2016-09-26 18:48             ` Junio C Hamano
2016-09-26 22:46 ` [PATCH 0/4 v4] " Brandon Williams
2016-09-26 22:46   ` [PATCH 1/4 v4] submodules: make submodule-prefix option Brandon Williams
2016-09-27 18:17     ` Junio C Hamano
2016-09-27 20:29       ` Brandon Williams
2016-09-27 20:35         ` Junio C Hamano
2016-09-27 20:43           ` Brandon Williams
2016-09-26 22:46   ` [PATCH 2/4 v4] ls-files: optionally recurse into submodules Brandon Williams
2016-09-27 18:29     ` Junio C Hamano
2016-09-27 20:33       ` Brandon Williams
2016-09-26 22:46   ` [PATCH 3/4 v4] ls-files: pass through safe options for --recurse-submodules Brandon Williams
2016-09-27 18:40     ` Junio C Hamano
2016-09-27 20:11       ` Junio C Hamano
2016-09-27 20:52         ` Brandon Williams
2016-09-27 20:58           ` Junio C Hamano
2016-09-27 20:59           ` Stefan Beller
2016-09-28 17:24         ` Brandon Williams
2016-09-28 18:59           ` Junio C Hamano
2016-09-27 20:49       ` Brandon Williams
2016-09-27 18:43     ` Junio C Hamano
2016-09-27 20:44       ` Brandon Williams
2016-09-27 20:59         ` Junio C Hamano
2016-09-26 22:46   ` Brandon Williams [this message]
2016-09-27 20:01     ` [PATCH 4/4 v4] ls-files: add pathspec matching for submodules Junio C Hamano
2016-09-27 20:40       ` Brandon Williams
2016-09-28 21:50   ` [PATCH v5 0/4] recursive support for ls-files Brandon Williams
2016-09-28 21:50     ` [PATCH v5 1/4] git: make super-prefix option Brandon Williams
2016-09-28 22:01       ` Stefan Beller
2016-09-28 22:19       ` Junio C Hamano
2016-09-29 18:39       ` Jeff King
2016-09-29 18:44         ` Brandon Williams
2016-09-28 21:50     ` [PATCH v5 2/4] ls-files: optionally recurse into submodules Brandon Williams
2016-09-28 22:11       ` Stefan Beller
2016-09-28 22:22       ` Junio C Hamano
2016-09-28 21:50     ` [PATCH v5 3/4] ls-files: pass through safe options for --recurse-submodules Brandon Williams
2016-09-28 21:50     ` [PATCH v5 4/4] ls-files: add pathspec matching for submodules Brandon Williams
2016-09-29 21:48     ` [PATCH v6 0/4] recursive support for ls-files Brandon Williams
2016-09-29 21:48       ` [PATCH v6 1/4] git: make super-prefix option Brandon Williams
2016-10-04 17:31         ` Stefan Beller
2016-10-04 17:35           ` Junio C Hamano
2016-10-04 17:39           ` Jeff King
2016-09-29 21:48       ` [PATCH v6 2/4] ls-files: optionally recurse into submodules Brandon Williams
2016-09-29 21:48       ` [PATCH v6 3/4] ls-files: pass through safe options for --recurse-submodules Brandon Williams
2016-09-30  0:14         ` Junio C Hamano
2016-09-30 16:33           ` Brandon Williams
2016-09-30 17:01             ` Brandon Williams
2016-09-29 21:48       ` [PATCH v6 4/4] ls-files: add pathspec matching for submodules Brandon Williams
2016-10-04 17:56         ` Stefan Beller
2016-10-07 18:18       ` [PATCH v7 0/4] recursive support for ls-files Brandon Williams
2016-10-07 18:18         ` [PATCH v7 1/4] git: make super-prefix option Brandon Williams
2016-10-07 18:18         ` [PATCH v7 2/4] ls-files: optionally recurse into submodules Brandon Williams
2016-10-07 18:18         ` [PATCH v7 3/4] ls-files: pass through safe options for --recurse-submodules Brandon Williams
2016-10-07 18:18         ` [PATCH v7 4/4] ls-files: add pathspec matching for submodules Brandon Williams
2016-10-07 18:34         ` [PATCH v7 0/4] recursive support for ls-files Stefan Beller
2016-10-07 18:35           ` Stefan Beller
2016-10-07 18:45             ` Brandon Williams

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1474930003-83750-5-git-send-email-bmwill@google.com \
    --to=bmwill@google.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).