119 lines
4.7 KiB
Diff
119 lines
4.7 KiB
Diff
From fdcad5a53e14bd397e4fa323e7fd0c3bf16dd373 Mon Sep 17 00:00:00 2001
|
|
From: Johannes Schindelin <johannes.schindelin@gmx.de>
|
|
Date: Wed, 23 Mar 2022 23:00:41 +0100
|
|
Subject: [PATCH] Fix `GIT_CEILING_DIRECTORIES` with `C:\` and the likes
|
|
|
|
When determining the length of the longest ancestor of a given path with
|
|
respect to to e.g. `GIT_CEILING_DIRECTORIES`, we special-case the root
|
|
directory by returning 0 (i.e. we pretend that the path `/` does not end
|
|
in a slash by virtually stripping it).
|
|
|
|
That is the correct behavior because when normalizing paths, the root
|
|
directory is special: all other directory paths have their trailing
|
|
slash stripped, but not the root directory's path (because it would
|
|
become the empty string, which is not a legal path).
|
|
|
|
However, this special-casing of the root directory in
|
|
`longest_ancestor_length()` completely forgets about Windows-style root
|
|
directories, e.g. `C:\`. These _also_ get normalized with a trailing
|
|
slash (because `C:` would actually refer to the current directory on
|
|
that drive, not necessarily to its root directory).
|
|
|
|
In fc56c7b34b (mingw: accomodate t0060-path-utils for MSYS2,
|
|
2016-01-27), we almost got it right. We noticed that
|
|
`longest_ancestor_length()` expects a slash _after_ the matched prefix,
|
|
and if the prefix already ends in a slash, the normalized path won't
|
|
ever match and -1 is returned.
|
|
|
|
But then that commit went astray: The correct fix is not to adjust the
|
|
_tests_ to expect an incorrect -1 when that function is fed a prefix
|
|
that ends in a slash, but instead to treat such a prefix as if the
|
|
trailing slash had been removed.
|
|
|
|
Likewise, that function needs to handle the case where it is fed a path
|
|
that ends in a slash (not only a prefix that ends in a slash): if it
|
|
matches the prefix (plus trailing slash), we still need to verify that
|
|
the path does not end there, otherwise the prefix is not actually an
|
|
ancestor of the path but identical to it (and we need to return -1 in
|
|
that case).
|
|
|
|
With these two adjustments, we no longer need to play games in t0060
|
|
where we only add `$rootoff` if the passed prefix is different from the
|
|
MSYS2 pseudo root, instead we also add it for the MSYS2 pseudo root
|
|
itself. We do have to be careful to skip that logic entirely for Windows
|
|
paths, though, because they do are not subject to that MSYS2 pseudo root
|
|
treatment.
|
|
|
|
This patch fixes the scenario where a user has set
|
|
`GIT_CEILING_DIRECTORIES=C:\`, which would be ignored otherwise.
|
|
|
|
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
|
|
---
|
|
path.c | 14 +++++++++-----
|
|
t/t0060-path-utils.sh | 20 ++++++++++++++------
|
|
2 files changed, 23 insertions(+), 11 deletions(-)
|
|
|
|
diff --git a/path.c b/path.c
|
|
index 7b385e5eb28227..853e7165c89fd0 100644
|
|
--- a/path.c
|
|
+++ b/path.c
|
|
@@ -1218,11 +1218,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes)
|
|
const char *ceil = prefixes->items[i].string;
|
|
int len = strlen(ceil);
|
|
|
|
- if (len == 1 && ceil[0] == '/')
|
|
- len = 0; /* root matches anything, with length 0 */
|
|
- else if (!strncmp(path, ceil, len) && path[len] == '/')
|
|
- ; /* match of length len */
|
|
- else
|
|
+ /*
|
|
+ * For root directories (`/`, `C:/`, `//server/share/`)
|
|
+ * adjust the length to exclude the trailing slash.
|
|
+ */
|
|
+ if (len > 0 && ceil[len - 1] == '/')
|
|
+ len--;
|
|
+
|
|
+ if (strncmp(path, ceil, len) ||
|
|
+ path[len] != '/' || !path[len + 1])
|
|
continue; /* no match */
|
|
|
|
if (len > max_len)
|
|
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
|
|
index 56db5c8abab62e..f538264cdd3382 100755
|
|
--- a/t/t0060-path-utils.sh
|
|
+++ b/t/t0060-path-utils.sh
|
|
@@ -55,12 +55,15 @@ fi
|
|
ancestor() {
|
|
# We do some math with the expected ancestor length.
|
|
expected=$3
|
|
- if test -n "$rootoff" && test "x$expected" != x-1; then
|
|
- expected=$(($expected-$rootslash))
|
|
- test $expected -lt 0 ||
|
|
- expected=$(($expected+$rootoff))
|
|
- fi
|
|
- test_expect_success "longest ancestor: $1 $2 => $expected" \
|
|
+ case "$rootoff,$expected,$2" in
|
|
+ *,*,//*) ;; # leave UNC paths alone
|
|
+ [0-9]*,[0-9]*,/*)
|
|
+ # On Windows, expect MSYS2 pseudo root translation for
|
|
+ # Unix-style absolute paths
|
|
+ expected=$(($expected-$rootslash+$rootoff))
|
|
+ ;;
|
|
+ esac
|
|
+ test_expect_success $4 "longest ancestor: $1 $2 => $expected" \
|
|
"actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
|
|
test \"\$actual\" = '$expected'"
|
|
}
|
|
@@ -156,6 +159,11 @@ ancestor /foo/bar /foo 4
|
|
ancestor /foo/bar /foo:/bar 4
|
|
ancestor /foo/bar /bar -1
|
|
|
|
+# Windows-specific: DOS drives, network shares
|
|
+ancestor C:/Users/me C:/ 2 MINGW
|
|
+ancestor D:/Users/me C:/ -1 MINGW
|
|
+ancestor //server/share/my-directory //server/share/ 14 MINGW
|
|
+
|
|
test_expect_success 'strip_path_suffix' '
|
|
test c:/msysgit = $(test-tool path-utils strip_path_suffix \
|
|
c:/msysgit/libexec//git-core libexec/git-core)
|