Merge branch 'jc/check-attr-honor-working-tree'
[gitweb.git] / path.c
diff --git a/path.c b/path.c
index 9fd28bcd080ef0a64f0b8d8a5d9d680a741d75c3..f9c5062427e7d8170a1e2e597fcf22ae517a865e 100644 (file)
--- a/path.c
+++ b/path.c
@@ -265,12 +265,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) {
@@ -434,6 +434,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.
  *
@@ -454,6 +464,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]))
@@ -530,6 +550,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.
  *