From 645a29c40a12a4ade1b041dce246ae241c502a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 14 Jul 2013 15:36:03 +0700 Subject: parse_pathspec: make sure the prefix part is wildcard-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepending prefix to pathspec is a trick to workaround the fact that commands can be executed in a subdirectory, but all git commands run at worktree's root. The prefix part should always be treated as literal string. Make it so. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- path.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'path.c') diff --git a/path.c b/path.c index 04ff1487ed..f4b49d6924 100644 --- a/path.c +++ b/path.c @@ -492,8 +492,14 @@ const char *relative_path(const char *abs, const char *base) * * Note that this function is purely textual. It does not follow symlinks, * verify the existence of the path, or make any system calls. + * + * prefix_len != NULL is for a specific case of prefix_pathspec(): + * assume that src == dst and src[0..prefix_len-1] is already + * normalized, any time "../" eats up to the prefix_len part, + * prefix_len is reduced. In the end prefix_len is the remaining + * prefix that has not been overridden by user pathspec. */ -int normalize_path_copy(char *dst, const char *src) +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) { char *dst0; @@ -568,11 +574,18 @@ int normalize_path_copy(char *dst, const char *src) /* Windows: dst[-1] cannot be backslash anymore */ while (dst0 < dst && dst[-1] != '/') dst--; + if (prefix_len && *prefix_len > dst - dst0) + *prefix_len = dst - dst0; } *dst = '\0'; return 0; } +int normalize_path_copy(char *dst, const char *src) +{ + return normalize_path_copy_len(dst, src, NULL); +} + /* * path = Canonical absolute path * prefixes = string_list containing normalized, absolute paths without -- cgit v1.2.1 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.1 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.1 From 53ec551c87c731c5c4171e943842998bdfb5548e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 27 Jan 2014 20:36:12 -0500 Subject: expand_user_path: do not look at NULL path We explicitly check for and handle the case that the incoming "path" variable is NULL, but before doing so we call strchrnul on it, leading to a potential segfault. We can fix this simply by moving the strchrnul call down; as a bonus, we can tighten the scope on the associated variable. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'path.c') diff --git a/path.c b/path.c index 6f2aa699ad..3a5796156b 100644 --- a/path.c +++ b/path.c @@ -231,12 +231,12 @@ static struct passwd *getpw_str(const char *username, size_t len) char *expand_user_path(const char *path) { struct strbuf user_path = STRBUF_INIT; - const char *first_slash = strchrnul(path, '/'); const char *to_copy = path; if (path == NULL) goto return_null; if (path[0] == '~') { + const char *first_slash = strchrnul(path, '/'); const char *username = path + 1; size_t username_len = first_slash - username; if (username_len == 0) { -- cgit v1.2.1