git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 1/4] magic pathspec: add tentative ":/path/from/top/level" pathspec support

From
Junio C Hamano <gitster@pobox.com>
Date
Apr 7, 2011, 01:16 UTC
Message-ID
<1302138996-10006-2-git-send-email-gitster@pobox.com>
In-Reply-To
<1302138996-10006-1-git-send-email-gitster@pobox.com>

Support ":/" magic string that can be prefixed to a pathspec element to say "this names the path from the top-level of the working tree", when you are in the subdirectory.

For example, you should be able to say:
    $ edit Makefile ;# top-level
    $ cd Documentation
    $ edit git.txt ;# in the subdirectory
and then do one of three things, still inside the subdirectory:
    $ git add -u .  ;# add only Documentation/git.txt
    $ git add -u :/ ;# add everything, including paths outside Documentation
    $ git add -u    ;# whatever the default setting is.

To truly support magic pathspec, the API needs to be restructured so that get_pathspec() and init_pathspec() are unified into one call. Currently, the former just prefixes the user supplied pathspec with the current subdirectory path, and the latter takes the output from the former and pre-parses them into a bit richer structure for easier handling. They should become a single API function that takes the current subdirectory path and the remainder of argv[] (after parsing --options and revision arguments from the command line) and returns an array of parsed pathspec elements, and "magic" should become attributes of struct pathspec_item.

This patch implements only "top" magic because it is the only kind of magic that can be hacked into the system without such a refactoring. Other types of magic that are envisioned (e.g. "icase") needs to be able to express more than what a simple string can encode and needs to wait.

The syntax for magic pathspec prefix is designed to be extensible yet simple to type to invoke a simple magic like "from the top". The parser for the magic prefix is hooked into get_pathspec() function in this patch, and it needs to be moved when we refactor the API.

But we have to start from somewhere.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/glossary-content.txt |   31 +++++++++++-
 setup.c                            |   98 +++++++++++++++++++++++++++++++++++-
 2 files changed, 126 insertions(+), 3 deletions(-)
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 33716a3..e51d7e6 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -277,7 +277,8 @@ This commit is referred to as a "merge commit", or sometimes just a
        Pattern used to specify paths.
 +
 Pathspecs are used on the command line of "git ls-files", "git
-ls-tree", "git grep", "git checkout", and many other commands to
+ls-tree", "git add", "git grep", "git diff", "git checkout",
+and many other commands to
 limit the scope of operations to some subset of the tree or
 worktree.  See the documentation of each command for whether
 paths are relative to the current directory or toplevel.  The
@@ -296,6 +297,34 @@ For example, Documentation/*.jpg will match all .jpg files
 in the Documentation subtree,
 including Documentation/chapter_1/figure_1.jpg.
 
++
+A pathspec that begins with a colon `:` has special meaning.  In the
+short form, the leading colon `:` is followed by zero or more "magic
+signature" letters (which optionally is terminated by another colon `:`),
+and the remainder is the pattern to match against the path. The optional
+colon that terminates the "magic signature" can be omitted if the pattern
+begins with a character that cannot be a "magic signature" and is not a
+colon.
++
+In the long form, the leading colon `:` is followed by a open
+parenthesis `(`, a comma-separated list of zero or more "magic words",
+and a close parentheses `)`, and the remainder is the pattern to match
+against the path.
++
+The "magic signature" consists of an ASCII symbol that is not
+alphanumeric.
++
+--
+top `/`;;
+	The magic word `top` (mnemonic: `/`) makes the pattern match
+	from the root of the working tree, even when you are running
+	the command from inside a subdirectory.
+--
++
+Currently only the slash `/` is recognized as the "magic signature",
+but it is envisioned that we will support more types of magic in later
+versions of git.
+
 [[def_parent]]parent::
 	A <<def_commit_object,commit object>> contains a (possibly empty) list
 	of the logical predecessor(s) in the line of development, i.e. its
diff --git a/setup.c b/setup.c
index 03cd84f..820ed05 100644
--- a/setup.c
+++ b/setup.c
@@ -126,6 +126,101 @@ void verify_non_filename(const char *prefix, const char *arg)
 	    "Use '--' to separate filenames from revisions", arg);
 }
 
+/*
+ * Magic pathspec
+ *
+ * NEEDSWORK: These need to be moved to dir.h or even to a new
+ * pathspec.h when we restructure get_pathspec() users to use the
+ * "struct pathspec" interface.
+ *
+ * Possible future magic semantics include stuff like:
+ *
+ *	{ PATHSPEC_NOGLOB, '!', "noglob" },
+ *	{ PATHSPEC_ICASE, '\0', "icase" },
+ *	{ PATHSPEC_RECURSIVE, '*', "recursive" },
+ *	{ PATHSPEC_REGEXP, '\0', "regexp" },
+ *
+ */
+#define PATHSPEC_FROMTOP    (1<<0)
+
+struct pathspec_magic {
+	unsigned bit;
+	char mnemonic; /* this cannot be ':'! */
+	const char *name;
+} pathspec_magic[] = {
+	{ PATHSPEC_FROMTOP, '/', "top" },
+};
+
+/*
+ * Take an element of a pathspec and check for magic signatures.
+ * Append the result to the prefix.
+ *
+ * For now, we only parse the syntax and throw out anything other than
+ * "top" magic.
+ *
+ * NEEDSWORK: This needs to be rewritten when we start migrating
+ * get_pathspec() users to use the "struct pathspec" interface.  For
+ * example, a pathspec element may be marked as case-insensitive, but
+ * the prefix part must always match literally, and a single stupid
+ * string cannot express such a case.
+ */
+const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
+{
+	unsigned magic = 0;
+	const char *copyfrom = elt;
+	int i;
+
+	if (elt[0] != ':') {
+		; /* nothing to do */
+	} else if (elt[1] == '(') {
+		/* longhand */
+		const char *nextat;
+		for (copyfrom = elt + 2;
+		     *copyfrom && *copyfrom != ')';
+		     copyfrom = nextat) {
+			size_t len = strcspn(copyfrom, ",)");
+			if (copyfrom[len] == ')')
+				nextat = copyfrom + len;
+			else
+				nextat = copyfrom + len + 1;
+			if (!len)
+				continue;
+			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+				if (strlen(pathspec_magic[i].name) == len &&
+				    !strncmp(pathspec_magic[i].name, copyfrom, len)) {
+					magic |= pathspec_magic[i].bit;
+					break;
+				}
+			if (ARRAY_SIZE(pathspec_magic) <= i)
+				die("Invalid pathspec magic '%.*s' in '%s'",
+				    (int) len, copyfrom, elt);
+		}
+		if (*copyfrom == ')')
+			copyfrom++;
+	} else {
+		/* shorthand */
+		for (copyfrom = elt + 1;
+		     *copyfrom && *copyfrom != ':';
+		     copyfrom++) {
+			char ch = *copyfrom;
+			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+				if (pathspec_magic[i].mnemonic == ch) {
+					magic |= pathspec_magic[i].bit;
+					break;
+				}
+			if (ARRAY_SIZE(pathspec_magic) <= i)
+				break;
+		}
+		if (*copyfrom == ':')
+			copyfrom++;
+	}
+
+	if (magic & PATHSPEC_FROMTOP)
+		return xstrdup(copyfrom);
+	else
+		return prefix_path(prefix, prefixlen, copyfrom);
+}
+
 const char **get_pathspec(const char *prefix, const char **pathspec)
 {
 	const char *entry = *pathspec;
@@ -147,8 +242,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
 	dst = pathspec;
 	prefixlen = prefix ? strlen(prefix) : 0;
 	while (*src) {
-		const char *p = prefix_path(prefix, prefixlen, *src);
-		*(dst++) = p;
+		*(dst++) = prefix_pathspec(prefix, prefixlen, *src);
 		src++;
 	}
 	*dst = NULL;
-- 
1.7.5.rc1
Previous: Junio C HamanoNext: Junio C Hamano
Message 2 of 35 in “Redoing the "add -u" migration plan”
  1. 0/4 Redoing the "add -u" migration planJunio C Hamano, Apr 7, 2011
  2. 1/4 magic pathspec: add tentative ":/path/from/top/level" pathspec supportJunio C Hamano, Apr 7, 2011
  3. Junio C HamanoApr 7, 2011
  4. Nguyen Thai Ngoc DuyApr 7, 2011
  5. Junio C HamanoApr 7, 2011
  6. Nguyen Thai Ngoc DuyApr 8, 2011
  7. Nguyen Thai Ngoc DuyApr 7, 2011
  8. Junio C HamanoApr 7, 2011
  9. Nguyen Thai Ngoc DuyApr 8, 2011
  10. Junio C HamanoApr 8, 2011
  11. Nguyen Thai Ngoc DuyApr 8, 2011
  12. Junio C HamanoApr 8, 2011
  13. Nguyen Thai Ngoc DuyApr 8, 2011
  14. 2/4 add -u: get rid of "treewideupdate" configurationJunio C Hamano, Apr 7, 2011
  15. Jeff KingApr 8, 2011
  16. Junio C HamanoApr 8, 2011
  17. Jeff KingApr 8, 2011
  18. Junio C HamanoApr 8, 2011
  19. Jeff KingApr 8, 2011
  20. Junio C HamanoApr 8, 2011
  21. Junio C HamanoApr 8, 2011
  22. Nguyen Thai Ngoc DuyApr 9, 2011
  23. Junio C HamanoApr 9, 2011
  24. Nguyen Thai Ngoc DuyApr 9, 2011
  25. Junio C HamanoApr 9, 2011
  26. Nguyen Thai Ngoc DuyApr 9, 2011
  27. Junio C HamanoApr 9, 2011
  28. Nguyen Thai Ngoc DuyApr 9, 2011
  29. Nguyen Thai Ngoc DuyApr 9, 2011
  30. Junio C HamanoApr 9, 2011
  31. Nguyen Thai Ngoc DuyMay 3, 2011
  32. Junio C HamanoMay 3, 2011
  33. Nguyen Thai Ngoc DuyMay 3, 2011
  34. 3/4 add: make "add -u/-A" update full tree without pathspec (step 2)Junio C Hamano, Apr 7, 2011
  35. 4/4 add: make "add -u/-A" update full tree without pathspec (step 3)Junio C Hamano, Apr 7, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.