Skip to content

Commit fdcad5a

Browse files
committed
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 fc56c7b (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 <[email protected]>
1 parent 8959555 commit fdcad5a

File tree

2 files changed

+23
-11
lines changed

2 files changed

+23
-11
lines changed

path.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,11 +1218,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes)
12181218
const char *ceil = prefixes->items[i].string;
12191219
int len = strlen(ceil);
12201220

1221-
if (len == 1 && ceil[0] == '/')
1222-
len = 0; /* root matches anything, with length 0 */
1223-
else if (!strncmp(path, ceil, len) && path[len] == '/')
1224-
; /* match of length len */
1225-
else
1221+
/*
1222+
* For root directories (`/`, `C:/`, `//server/share/`)
1223+
* adjust the length to exclude the trailing slash.
1224+
*/
1225+
if (len > 0 && ceil[len - 1] == '/')
1226+
len--;
1227+
1228+
if (strncmp(path, ceil, len) ||
1229+
path[len] != '/' || !path[len + 1])
12261230
continue; /* no match */
12271231

12281232
if (len > max_len)

t/t0060-path-utils.sh

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ fi
5555
ancestor() {
5656
# We do some math with the expected ancestor length.
5757
expected=$3
58-
if test -n "$rootoff" && test "x$expected" != x-1; then
59-
expected=$(($expected-$rootslash))
60-
test $expected -lt 0 ||
61-
expected=$(($expected+$rootoff))
62-
fi
63-
test_expect_success "longest ancestor: $1 $2 => $expected" \
58+
case "$rootoff,$expected,$2" in
59+
*,*,//*) ;; # leave UNC paths alone
60+
[0-9]*,[0-9]*,/*)
61+
# On Windows, expect MSYS2 pseudo root translation for
62+
# Unix-style absolute paths
63+
expected=$(($expected-$rootslash+$rootoff))
64+
;;
65+
esac
66+
test_expect_success $4 "longest ancestor: $1 $2 => $expected" \
6467
"actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
6568
test \"\$actual\" = '$expected'"
6669
}
@@ -156,6 +159,11 @@ ancestor /foo/bar /foo 4
156159
ancestor /foo/bar /foo:/bar 4
157160
ancestor /foo/bar /bar -1
158161

162+
# Windows-specific: DOS drives, network shares
163+
ancestor C:/Users/me C:/ 2 MINGW
164+
ancestor D:/Users/me C:/ -1 MINGW
165+
ancestor //server/share/my-directory //server/share/ 14 MINGW
166+
159167
test_expect_success 'strip_path_suffix' '
160168
test c:/msysgit = $(test-tool path-utils strip_path_suffix \
161169
c:/msysgit/libexec//git-core libexec/git-core)

0 commit comments

Comments
 (0)