From 7fbd422162f2b49bc06a29a063f519450165dc86 Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Mon, 14 Oct 2013 10:29:39 +0800 Subject: relative_path should honor dos-drive-prefix Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have DOS drive prefix (such as "C:/windows"). ($gmane/234434) E.g., When execute: test-path-utils relative_path "C:/a/b" "D:/x/y", should return "C:/a/b", but returns "../../C:/a/b", which is wrong. So make relative_path honor DOS drive prefix, and add test cases for it in t0060. Reported-by: Tvangeste Helped-by: Johannes Sixt Signed-off-by: Jiang Xin Signed-off-by: Jonathan Nieder --- path.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'path.c') diff --git a/path.c b/path.c index 7f3324aeea..0c16dc5fdb 100644 --- a/path.c +++ b/path.c @@ -441,6 +441,16 @@ int adjust_shared_perm(const char *path) return 0; } +static int have_same_root(const char *path1, const char *path2) +{ + int is_abs1, is_abs2; + + is_abs1 = is_absolute_path(path1); + is_abs2 = is_absolute_path(path2); + return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) || + (!is_abs1 && !is_abs2); +} + /* * Give path as relative to prefix. * @@ -461,6 +471,16 @@ const char *relative_path(const char *in, const char *prefix, else if (!prefix_len) return in; + if (have_same_root(in, prefix)) { + /* bypass dos_drive, for "c:" is identical to "C:" */ + if (has_dos_drive_prefix(in)) { + i = 2; + j = 2; + } + } else { + return in; + } + while (i < prefix_len && j < in_len && prefix[i] == in[j]) { if (is_dir_sep(prefix[i])) { while (is_dir_sep(prefix[i])) -- cgit v1.2.3 From 41894ae3a315f75ebc924881c6ce9a69d70ce9c0 Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Mon, 14 Oct 2013 10:29:40 +0800 Subject: Use simpler relative_path when set_git_dir Using a relative_path as git_dir first appears in v1.5.6-1-g044bbbc. It will make git_dir shorter only if git_dir is inside work_tree, and this will increase performance. But my last refactor effort on relative_path function (commit v1.8.3-rc2-12-ge02ca72) changed that. Always use relative_path as git_dir may bring troubles like $gmane/234434. Because new relative_path is a combination of original relative_path from path.c and original path_relative from quote.c, so in order to restore the origin implementation, save the original relative_path as remove_leading_path, and call it in setup.c. Suggested-by: Karsten Blees Signed-off-by: Jiang Xin Signed-off-by: Jonathan Nieder --- path.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'path.c') diff --git a/path.c b/path.c index 0c16dc5fdb..fa62da58ed 100644 --- a/path.c +++ b/path.c @@ -557,6 +557,51 @@ const char *relative_path(const char *in, const char *prefix, return sb->buf; } +/* + * A simpler implementation of relative_path + * + * Get relative path by removing "prefix" from "in". This function + * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter + * to increase performance when traversing the path to work_tree. + */ +const char *remove_leading_path(const char *in, const char *prefix) +{ + static char buf[PATH_MAX + 1]; + int i = 0, j = 0; + + if (!prefix || !prefix[0]) + return in; + while (prefix[i]) { + if (is_dir_sep(prefix[i])) { + if (!is_dir_sep(in[j])) + return in; + while (is_dir_sep(prefix[i])) + i++; + while (is_dir_sep(in[j])) + j++; + continue; + } else if (in[j] != prefix[i]) { + return in; + } + i++; + j++; + } + if ( + /* "/foo" is a prefix of "/foo" */ + in[j] && + /* "/foo" is not a prefix of "/foobar" */ + !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j]) + ) + return in; + while (is_dir_sep(in[j])) + j++; + if (!in[j]) + strcpy(buf, "."); + else + strcpy(buf, in + j); + return buf; +} + /* * It is okay if dst == src, but they should not overlap otherwise. * -- cgit v1.2.3