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

Improve handling of "." and ".." in git-diff-*

From
Linus Torvalds <torvalds@osdl.org>
Date
Aug 17, 2005, 03:44 UTC
Message-ID
<Pine.LNX.4.58.0508162037080.3553@g5.osdl.org>

This fixes up usage of ".." (without an ending slash) and "." (with or without the ending slash) in the git diff family.

It also fixes pathspec matching for the case of an empty pathspec, since a "." in the top-level directory (or enough ".." under subdirectories) will result in an empty pathspec. We used to not match it against anything, but it should in fact match everything.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
----

This is in addition to the previous fix for the t0000 testcase and applies independently of that.

NOTE! This does _not_ handle ".." or "." in the _middle_ of a pathspec. If you have people who do

	git diff net/../char/
this will not help them - it will _not_ simplify this to
	git diff char/

and only ".." and "." components at the head of the pathspec will be honoured.

Finally, if you try to give a pathspec that goes "outside" of the .git directory (eg, a ".." pattern at the top of the git archive), the code will politely tell you to f*ck off and die (this is not a new feature, the previous code drop did that too, but because we now handle it even for the top-level case without any extra prefix, it's worth noting).

---
diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c
--- a/diffcore-pathspec.c
+++ b/diffcore-pathspec.c
@@ -29,6 +29,8 @@ static int matches_pathspec(const char *
 		    name[len] == 0 ||
 		    name[len] == '/')
 			return 1;
+		if (!len)
+			return 1;
 	}
 	return 0;
 }
diff --git a/read-cache.c b/read-cache.c
--- a/read-cache.c
+++ b/read-cache.c
@@ -191,6 +191,8 @@ int ce_path_match(const struct cache_ent
 			return 1;
 		if (name[matchlen] == '/' || !name[matchlen])
 			return 1;
+		if (!matchlen)
+			return 1;
 	}
 	return 0;
 }
diff --git a/setup.c b/setup.c
--- a/setup.c
+++ b/setup.c
@@ -1,23 +1,60 @@
 #include "cache.h"
 
+static char *prefix_path(const char *prefix, int len, char *path)
+{
+	char *orig = path;
+	for (;;) {
+		char c;
+		if (*path != '.')
+			break;
+		c = path[1];
+		/* "." */
+		if (!c) {
+			path++;
+			break;
+		}
+		/* "./" */
+		if (c == '/') {
+			path += 2;
+			continue;
+		}
+		if (c != '.')
+			break;
+		c = path[2];
+		if (!c)
+			path += 2;
+		else if (c == '/')
+			path += 3;
+		else
+			break;
+		/* ".." and "../" */
+		/* Remove last component of the prefix */
+		do {
+			if (!len)
+				die("'%s' is outside repository", orig);
+			len--;
+		} while (len && prefix[len-1] != '/');
+		continue;
+	}
+	if (len) {
+		int speclen = strlen(path);
+		char *n = xmalloc(speclen + len + 1);
+	
+		memcpy(n, prefix, len);
+		memcpy(n + len, path, speclen+1);
+		path = n;
+	}
+	return path;
+}
+
 const char **get_pathspec(const char *prefix, char **pathspec)
 {
 	char *entry = *pathspec;
 	char **p;
 	int prefixlen;
 
-	if (!prefix) {
-		char **p;
-		if (!entry)
-			return NULL;
-		p = pathspec;
-		do {
-			if (*entry != '.')
-				continue;
-			/* fixup ? */
-		} while ((entry = *++p) != NULL);
-		return (const char **) pathspec;
-	}
+	if (!prefix && !entry)
+		return NULL;
 
 	if (!entry) {
 		static const char *spec[2];
@@ -27,38 +64,10 @@ const char **get_pathspec(const char *pr
 	}
 
 	/* Otherwise we have to re-write the entries.. */
-	prefixlen = strlen(prefix);
 	p = pathspec;
+	prefixlen = prefix ? strlen(prefix) : 0;
 	do {
-		int speclen, len = prefixlen;
-		char *n;
-
-		for (;;) {
-			if (!strcmp(entry, ".")) {
-				entry++;
-				break;
-			}
-			if (!strncmp(entry, "./", 2)) {
-				entry += 2;
-				continue;
-			}
-			if (!strncmp(entry, "../", 3)) {
-				do {
-					if (!len)
-						die("'%s' is outside repository", *p);
-					len--;
-				} while (len && prefix[len-1] != '/');
-				entry += 3;
-				continue;
-			}
-			break;
-		}
-		speclen = strlen(entry);
-		n = xmalloc(speclen + len + 1);
-		
-		memcpy(n, prefix, len);
-		memcpy(n + len, entry, speclen+1);
-		*p = n;
+		*p = prefix_path(prefix, prefixlen, entry);
 	} while ((entry = *++p) != NULL);
 	return (const char **) pathspec;
 }
Next: Linus Torvalds
Message 1 of 4 in “Improve handling of "." and ".." in git-diff-*”
  1. Linus TorvaldsAug 17, 2005
  2. Linus TorvaldsAug 17, 2005
  3. Alex RiesenAug 23, 2005
  4. Alex RiesenAug 23, 2005

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.