Skip to contentSkip to conversation

Regression in e02ca72: git svn rebase is broken on Windows

PATCH In review Tvangeste 9 participants 56 messages Jun 15, 2016 1 message not in the archive

lore
Share & tools
lore

Tvangeste

author
#0 lore raw

Hi,

After bisecting this problem I ended up with the mentioned commit that completely breaks git-svn for me on Windows (mingw/msys version).

==========
#> git svn rebase
warning: unable to access '': Invalid argument
warning: unable to access '': Invalid argument
fatal: unable to access '../../../../w:/work/my/repo.git/.git/config': Invalid argument
fatal: index file open failed: Invalid argument
Cannot rebase: You have unstaged changes.
Please commit or stash them.
rebase refs/remotes/trunk: command returned error: 1
==========

Please note that I use the official git repository as-is, this one (no additional patches):
git://git.kernel.org/pub/scm/git/git.git

e02ca72f70ed8f0268a81f72cb3230c72e538e77 is the first bad commit commit e02ca72f70ed8f0268a81f72cb3230c72e538e77

Author: Jiang Xin
Date:   Tue Jun 25 23:53:43 2013 +0800

path.c: refactor relative_path(), not only strip prefix

Thanks,
--Tvangeste

#1 lore raw

Am 10.09.2013 15:14, schrieb Tvangeste:

Quoted 13 lines

After bisecting this problem I ended up with the mentioned commit that completely breaks git-svn for me on Windows (mingw/msys version).

==========
#> git svn rebase
warning: unable to access '': Invalid argument
warning: unable to access '': Invalid argument
fatal: unable to access '../../../../w:/work/my/repo.git/.git/config': Invalid argument
fatal: index file open failed: Invalid argument
Cannot rebase: You have unstaged changes.
Please commit or stash them.
rebase refs/remotes/trunk: command returned error: 1
==========

Can you please run the command with GIT_TRACE=2?

-- Hannes

author
#2 lore raw

10.09.2013, 18:53, "Johannes Sixt" <[email protected]>:

Quoted 1 line

Can you please run the command with GIT_TRACE=2?

Sure:
#> git --version
trace: built-in: git 'version'
git version 1.8.4.242.gbb80ee0

#> git svn rebase -l
trace: exec: 'git-svn' 'rebase' '-l'
trace: run_command: 'git-svn' 'rebase' '-l'
trace: built-in: git 'rev-parse' '--git-dir'
trace: built-in: git 'rev-parse' '--show-cdup'
trace: built-in: git 'config' '--bool' '--get' 'svn.fetchall'
trace: built-in: git 'config' '--bool' '--get' 'svn.noauthcache'
trace: built-in: git 'config' '--bool' '--get' 'svn.nocheckout'
trace: built-in: git 'config' '--get' 'svn.authorsprog'
trace: built-in: git 'config' '--bool' '--get' 'svn.dryrun'
trace: built-in: git 'config' '--bool' '--get' 'svn.preservemerges'
trace: built-in: git 'config' '--bool' '--get' 'svn.followparent'
trace: built-in: git 'config' '--bool' '--get' 'svn.useSvmProps'
trace: built-in: git 'config' '--get' 'svn.authorsfile'
trace: built-in: git 'config' '--get' 'svn.username'
trace: built-in: git 'config' '--get' 'svn.repackflags'
trace: built-in: git 'config' '--bool' '--get' 'svn.localtime'
trace: built-in: git 'config' '--int' '--get' 'svn.repack'
trace: built-in: git 'config' '--get' 'svn.ignorepaths'
trace: built-in: git 'config' '--bool' '--get' 'svn.verbose'
trace: built-in: git 'config' '--bool' '--get' 'svn.quiet'
trace: built-in: git 'config' '--int' '--get' 'svn.logwindowsize'
trace: built-in: git 'config' '--get' 'svn.ignorerefs'
trace: built-in: git 'config' '--get' 'svn.configdir'
trace: built-in: git 'config' '--bool' '--get' 'svn.merge'
trace: built-in: git 'config' '--bool' '--get' 'svn.addauthorfrom'
trace: built-in: git 'config' '--bool' '--get' 'svn.useSvnsyncProps'
trace: built-in: git 'config' '--bool' '--get' 'svn.noMetadata'
trace: built-in: git 'config' '--bool' '--get' 'svn.local'
trace: built-in: git 'config' '--get' 'svn.strategy'
trace: built-in: git 'config' '--get' 'svn.includepaths'
trace: built-in: git 'config' '--bool' '--get' 'svn.uselogauthor'
trace: built-in: git 'rev-parse' '--symbolic' '--all'
trace: built-in: git 'config' '-l'
trace: built-in: git 'config' '-l'
trace: built-in: git 'update-index' '--refresh'
trace: built-in: git 'rev-list' '--first-parent' '--pretty=medium' 'HEAD' '--'
trace: built-in: git 'config' '--bool' 'svn.useSvmProps'
trace: built-in: git 'config' '-l'
trace: built-in: git 'for-each-ref' '--format=%(refname)' 'refs/'
trace: built-in: git 'for-each-ref' '--format=%(refname)' 'refs/'
trace: built-in: git 'for-each-ref' '--format=%(refname)' 'refs/'
trace: built-in: git 'for-each-ref' '--format=%(refname)' 'refs/'
trace: built-in: git 'for-each-ref' '--format=%(refname)' 'refs/'
trace: built-in: git 'config' '--get' 'svn-remote.svn.rewriteRoot'
trace: built-in: git 'config' '--get' 'svn-remote.svn.url'
trace: built-in: git 'config' '--get' 'svn-remote.svn.pushurl'
trace: built-in: git 'config' '--get' 'svn-remote.svn.uuid'
trace: built-in: git 'rev-list' '--pretty=raw' '--reverse' 'cdc459d7cedcec6bb26812e24661c7318f031be4..refs/remotes/trunk' '--'
trace: built-in: git 'config' '--get' 'svn-remote.svn.rewriteRoot'
trace: built-in: git 'config' '--get' 'svn-remote.svn.rewriteUUID'
trace: built-in: git 'diff-index' 'HEAD' '--'
trace: exec: 'git-rebase' 'refs/remotes/trunk'
trace: run_command: 'git-rebase' 'refs/remotes/trunk'
trace: built-in: git 'rev-parse' '--parseopt' '--' 'refs/remotes/trunk'
trace: built-in: git 'rev-parse' '--git-dir'
trace: built-in: git 'rev-parse' '--is-bare-repository'
trace: built-in: git 'rev-parse' '--show-toplevel'
trace: built-in: git 'config' '--bool' 'rebase.stat'
trace: built-in: git 'config' '--bool' 'rebase.autostash'
trace: built-in: git 'config' '--bool' 'rebase.autosquash'
trace: built-in: git 'rev-parse' '--verify' 'refs/remotes/trunk^0'
trace: built-in: git 'rev-parse' '--verify' 'refs/remotes/trunk^0'
trace: built-in: git 'symbolic-ref' '-q' 'HEAD'
trace: built-in: git 'rev-parse' '--verify' 'HEAD'
trace: built-in: git 'rev-parse' '--verify' 'HEAD'
trace: built-in: git 'update-index' '-q' '--ignore-submodules' '--refresh'
fatal: unable to access '../../../../w:/work/xxx/xxx-xxx-OSS.git.new/.git/config': Invalid argument
trace: built-in: git 'diff-files' '--quiet' '--ignore-submodules'
fatal: index file open failed: Invalid argument
Cannot rebase: You have unstaged changes.
trace: built-in: git 'diff-index' '--cached' '--quiet' '--ignore-submodules' 'HEAD' '--'
Please commit or stash them.
rebase refs/remotes/trunk: command returned error: 1

Thanks,
--Tvangeste

#3 lore raw
In reply to Tvangeste

Hi Tvangeste,

Quoted 2 lines · Tvangeste

After bisecting this problem I ended up with the mentioned commit that completely breaks git-svn for me on Windows (mingw/msys version).

Have you tried with Git for Windows yet?

Ciao,
Johannes

author
#4 lore raw

10.09.2013, 18:13, "Johannes Schindelin" <[email protected]>:

Quoted 1 line

Have you tried with Git for Windows yet?

What's Git for Windows? If you mean msysgit, then I say no, because the latest msysgit version is from June 02, and the change under discussion was made later on, on June 25th. So, this regression is not in msysgit release (yet).

Thanks,
--Tvangeste

#5 lore raw

Hi Tvangeste,

Quoted 4 lines · Tvangeste

10.09.2013, 18:13, "Johannes Schindelin" <[email protected]>:

Quoted 1 line

Have you tried with Git for Windows yet?

What's Git for Windows? If you mean msysgit,

Actually, they are two different things: Git for Windows is what the name says, and it comes with an installer. msysGit is the development environment to *build* Git for Windows.

Quoted 3 lines

then I say no, because the latest msysgit version is from June 02, and the change under discussion was made later on, on June 25th. So, this regression is not in msysgit release (yet).

Given the explanation what msysGit is, you might suspect that I'd like you to try to fix this in the msysGit context: After installing

https://code.google.com/p/msysgit/downloads/list?q=net+installer

you will have a full build environment, including the build of our latest master. You can then "cd /git/" and "git checkout pt/tentative-1.8.4 && make install" to make sure that the version we are very close to releasing as the new Git for Windows version does not break your workflow.

Ciao,
Johannes

Signature · 18 lines
-- 
*** Please reply-to-all at all times ***
*** (do not pretend to know who is subscribed and who is not) ***
*** Please avoid top-posting. ***
The msysGit Wiki is here: https://github.com/msysgit/msysgit/wiki - Github accounts are free.

You received this message because you are subscribed to the Google
Groups "msysGit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, and view previous threads, visit this group at
http://groups.google.com/group/msysgit?hl=en_US?hl=en

--- 
You received this message because you are subscribed to the Google Groups "msysGit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.
author
#6 lore raw

10.09.2013, 20:02, "Johannes Schindelin" <[email protected]>:

Quoted 4 lines

 Given the explanation what msysGit is, you might suspect that I'd like you to try to fix this in the msysGit context: After installing

         https://code.google.com/p/msysgit/downloads/list?q=net+installer

No problem. Here's what I have so far:

1. Installed the latest msysgit from the URL you've provided. Tried the git that comes out of the box that comes with the installer (1.8.3.msysgit):

1a. On CMD: everything is fine.
1b. On msys shell: everything is fine.

2. Checked out the branch you've suggested to try (pt/tentative-1.8.4) and installed it. Tried the new version:

2a. On CMD: got the problem that is being discussed in this thread. 2b. On msys shell: everything is fine.

So, in summary. That commit e02ca72, somewhere between 1.8.3 and 1.8.4 introduced the regression in git/git-svn on Windows, when executed in CMD.

Thanks,
--Tvangeste

#7 lore raw
In reply to Tvangeste
Quoted 20 lines · Tvangeste

Hi,

After bisecting this problem I ended up with the mentioned commit that completely breaks git-svn for me on Windows (mingw/msys version).

==========
#> git svn rebase
warning: unable to access '': Invalid argument
warning: unable to access '': Invalid argument
fatal: unable to access '../../../../w:/work/my/repo.git/.git/config': Invalid argument
fatal: index file open failed: Invalid argument
Cannot rebase: You have unstaged changes.
Please commit or stash them.
rebase refs/remotes/trunk: command returned error: 1
==========

Please note that I use the official git repository as-is, this one (no additional patches):
git://git.kernel.org/pub/scm/git/git.git

e02ca72f70ed8f0268a81f72cb3230c72e538e77 is the first bad commit commit e02ca72f70ed8f0268a81f72cb3230c72e538e77

Quoted 7 lines
Author: Jiang Xin
Date:   Tue Jun 25 23:53:43 2013 +0800

path.c: refactor relative_path(), not only strip prefix

Thanks,
--Tvangeste

The suspect commit and symptom look consistent. You started from a directory whose absolute path is "w:/work/..." and the updated code mistakenly thoguht that something that begins with "w" (not '/') is not an absolute, so added a series of ../ to make it relative, or something silly like that.

Jiang?

#8 lore raw

2013/9/11 Junio C Hamano <[email protected]>:

Quoted 30 lines
Quoted 20 lines · Tvangeste

Hi,

After bisecting this problem I ended up with the mentioned commit that completely breaks git-svn for me on Windows (mingw/msys version).

==========
#> git svn rebase
warning: unable to access '': Invalid argument
warning: unable to access '': Invalid argument
fatal: unable to access '../../../../w:/work/my/repo.git/.git/config': Invalid argument
fatal: index file open failed: Invalid argument
Cannot rebase: You have unstaged changes.
Please commit or stash them.
rebase refs/remotes/trunk: command returned error: 1
==========

Please note that I use the official git repository as-is, this one (no additional patches):
git://git.kernel.org/pub/scm/git/git.git

e02ca72f70ed8f0268a81f72cb3230c72e538e77 is the first bad commit commit e02ca72f70ed8f0268a81f72cb3230c72e538e77

Quoted 7 lines
Author: Jiang Xin
Date:   Tue Jun 25 23:53:43 2013 +0800

path.c: refactor relative_path(), not only strip prefix

Thanks,
--Tvangeste

Quoted 7 lines

The suspect commit and symptom look consistent. You started from a directory whose absolute path is "w:/work/..." and the updated code mistakenly thoguht that something that begins with "w" (not '/') is not an absolute, so added a series of ../ to make it relative, or something silly like that.

Jiang?

I tested 'relative_path' function using 'test-path-utils', and got the following result:

    $ ./test-path-utils relative_path 'C:/a/b' 'D:/x/y'
    ../../../C:/a/b
    $ ./test-path-utils relative_path '/a/b' 'x/y'
    ../..//a/b
    $ ./test-path-utils relative_path 'a/b' '/x/y'
    ../../../a/b

For the first case, in and prefix are on different ROOT, and for the other two cases, one path is a relative path, and another is an absolute path.

I write a patch to test whether two paths (in and prefix) have the same root. The result after applied the patch:

    $ ./test-path-utils relative_path 'C:/a/b' 'C:/x/y'
    ../../a/b
    $ ./test-path-utils relative_path 'C:/a/b' 'D:/x/y'
    C:/a/b
    $ ./test-path-utils relative_path '/a/b' 'x/y'
    /a/b
    $ ./test-path-utils relative_path 'a/b' '/x/y'
    a/b
path.c +19 -0
index 7f3324a..51f5d28 100644
@@ -441,6 +441,25 @@ int adjust_shared_perm(const char *path)
        return 0;
 }
 
+static int have_same_root(const char *path1, const char *path2)
+{
+       /* for POSIX:
+
+          return ((path1 && is_dir_sep(*path1)) ^
+                  (path2 && is_dir_sep(*path2))) == 0;
+       */
+       return path1 && path2 && *path1 && *path2 && (
+               (is_dir_sep(*path1) &&
+                is_dir_sep(*path2)) ||
+               (*(path1+1) == ':' &&
+                *(path2+1) == ':' &&
+                !strncasecmp(path1, path2, 1)) ||
+               (!is_dir_sep(*path1) &&
+                !is_dir_sep(*path2) &&
+                *(path1+1) != ':' &&
+                *(path2+1) != ':'));
+}
+
 /*
  * Give path as relative to prefix.
  *
@@ -461,6 +480,9 @@ const char *relative_path(const char *in, const

char *prefix,
else if (!prefix_len)
return in;

+ if (!have_same_root(in, prefix))
+ return in;
+

Should I write the function have_same_root as inline function or macro like 'is_dir_sep'?

Signature · 1 line
Jiang Xin
#9 lore raw

Am 11.09.2013 05:19, schrieb Jiang Xin:

Quoted 46 lines

I tested 'relative_path' function using 'test-path-utils', and got the following result:

    $ ./test-path-utils relative_path 'C:/a/b' 'D:/x/y'
    ../../../C:/a/b
    $ ./test-path-utils relative_path '/a/b' 'x/y'
    ../..//a/b
    $ ./test-path-utils relative_path 'a/b' '/x/y'
    ../../../a/b

For the first case, in and prefix are on different ROOT, and for the other two cases, one path is a relative path, and another is an absolute path.

I write a patch to test whether two paths (in and prefix) have the same root. The result after applied the patch:

    $ ./test-path-utils relative_path 'C:/a/b' 'C:/x/y'
    ../../a/b
    $ ./test-path-utils relative_path 'C:/a/b' 'D:/x/y'
    C:/a/b
    $ ./test-path-utils relative_path '/a/b' 'x/y'
    /a/b
    $ ./test-path-utils relative_path 'a/b' '/x/y'
    a/b
path.c +7 -0
index 7f3324a..51f5d28 100644
@@ -441,6 +441,25 @@ int adjust_shared_perm(const char *path)
        return 0;
 }
 
+static int have_same_root(const char *path1, const char *path2)
+{
+       /* for POSIX:
+
+          return ((path1 && is_dir_sep(*path1)) ^
+                  (path2 && is_dir_sep(*path2))) == 0;
+       */

path.c int adjust_shared_perm(const char *path) · line 460

Quoted 10 lines
path.c +10 -0
+       return path1 && path2 && *path1 && *path2 && (
+               (is_dir_sep(*path1) &&
+                is_dir_sep(*path2)) ||
+               (*(path1+1) == ':' &&
+                *(path2+1) == ':' &&
+                !strncasecmp(path1, path2, 1)) ||
+               (!is_dir_sep(*path1) &&
+                !is_dir_sep(*path2) &&
+                *(path1+1) != ':' &&
+                *(path2+1) != ':'));

I think this can be simplified to

	return path1 && path2 &&
		is_absolute_path(path1) &&
		is_absolute_path(path2) &&
		!strncasecmp(path1, path2, 1);

which would not mistake a path D:/foo on Unix as an absolute path.

path.c int adjust_shared_perm(const char *path) · line 461

Quoted 1 line
path.c +1 -0
+}

-- Hannes

Signature · 18 lines
-- 
*** Please reply-to-all at all times ***
*** (do not pretend to know who is subscribed and who is not) ***
*** Please avoid top-posting. ***
The msysGit Wiki is here: https://github.com/msysgit/msysgit/wiki - Github accounts are free.

You received this message because you are subscribed to the Google
Groups "msysGit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, and view previous threads, visit this group at
http://groups.google.com/group/msysgit?hl=en_US?hl=en

--- 
You received this message because you are subscribed to the Google Groups "msysGit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.
#10 lore raw
In reply to Junio C Hamano
Quoted 6 lines · Junio C Hamano <gitster <at> pobox.com> writes:
Quoted 3 lines · Tvangeste <i.4m.l33t <at> yandex.ru> writes:

Hi,

After bisecting this problem I ended up with the mentioned commit that

completely breaks git-svn for me on

Quoted 7 lines

Windows (mingw/msys version).

Quoted 6 lines

==========
#> git svn rebase
warning: unable to access '': Invalid argument
warning: unable to access '': Invalid argument
fatal: unable to access '../../../../w:/work/my/repo.git/.git/config':

Invalid argument

Quoted 7 lines
Quoted 7 lines

fatal: index file open failed: Invalid argument Cannot rebase: You have unstaged changes. Please commit or stash them.
rebase refs/remotes/trunk: command returned error: 1
==========

Please note that I use the official git repository as-is, this one (no

additional patches):

Quoted 12 lines
Quoted 11 lines

git://git.kernel.org/pub/scm/git/git.git

e02ca72f70ed8f0268a81f72cb3230c72e538e77 is the first bad commit commit e02ca72f70ed8f0268a81f72cb3230c72e538e77

Author: Jiang Xin
Date:   Tue Jun 25 23:53:43 2013 +0800

path.c: refactor relative_path(), not only strip prefix

Thanks,
--Tvangeste

Quoted 8 lines

The suspect commit and symptom look consistent. You started from a directory whose absolute path is "w:/work/..." and the updated code mistakenly thoguht that something that begins with "w" (not '/') is not an absolute, so added a series of ../ to make it relative, or something silly like that.

Jiang?

Indeed, this patch seems to change relative_path in a way that breaks git initialization, not just on Windows.

Previously, relative_path was always called with two absolute paths, and it only returned a relative path if the first was a subdir of the second (so a better name would probably have been 'relative_path_if_subdir'). The purpose was to improve performance by making GIT_DIR shorter if it was a subdir of GIT_WORK_TREE.

After this patch, relative_path always tries to return a relative path, even if both absolute paths are completely disjunct. This not only defeats the purpose (by making GIT_DIR longer, thus hurting performance), it is also not possible in general. POSIX explicitly allows for '//hostname' notation referring to network resources that are not explicitly mounted under '/'. I.e. given two absolute paths '//hostname1/a' and '//hostname2/b', there is no relative path from a to b or vice versa.

Additionally, GIT_DIR now may or may not have a trailing slash, which gives me a slightly uneasy feeling...

#11 lore raw

2013/9/11 Karsten Blees <[email protected]>:

Quoted 11 lines
Quoted 8 lines · Junio C Hamano <gitster <at> pobox.com> writes:

The suspect commit and symptom look consistent. You started from a directory whose absolute path is "w:/work/..." and the updated code mistakenly thoguht that something that begins with "w" (not '/') is not an absolute, so added a series of ../ to make it relative, or something silly like that.

Jiang?

Quoted 8 lines

Indeed, this patch seems to change relative_path in a way that breaks git initialization, not just on Windows.

Previously, relative_path was always called with two absolute paths, and it only returned a relative path if the first was a subdir of the second (so a better name would probably have been 'relative_path_if_subdir'). The purpose was to improve performance by making GIT_DIR shorter if it was a subdir of GIT_WORK_TREE.

Yes, it's what commit v1.5.6-1-g044bbbc says.

Quoted 3 lines

After this patch, relative_path always tries to return a relative path, even if both absolute paths are completely disjunct. This not only defeats the purpose (by making GIT_DIR longer, thus hurting performance), it is also not

Sometimes longer, sometimes shorter maybe.

Quoted 4 lines

possible in general. POSIX explicitly allows for '//hostname' notation referring to network resources that are not explicitly mounted under '/'. I.e. given two absolute paths '//hostname1/a' and '//hostname2/b', there is no relative path from a to b or vice versa.

Yes, path like "//hostname/path" can be used on Windows.
My hack "have_same_root" does not cover this case, so using a "simple_relative_path" function instead of "relative_path" in setup.c may be the better.

Signature · 1 line
Jiang Xin
#12 lore raw

PATCH 1/2 relative_path should honor dos_drive_prefix

In reply to Junio C Hamano

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have dos driver prefix. ($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.

---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
path.c +20 -0
index 7f3324a..ffcdea1 100644
@@ -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 && !strncasecmp(path1, path2, 1)) ||
+	       (!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]))
t/t0060-path-utils.sh +4 -0
index 76c7792..c3c3b33 100755
@@ -208,6 +208,10 @@ relative_path a/b/	a/b		./
 relative_path a		a/b		../
 relative_path x/y	a/b		../../x/y
 relative_path a/c	a/b		../c
+relative_path a/b	/x/y		a/b
+relative_path /a/b	x/y		/a/b	POSIX
+relative_path d:/a/b	D:/a/c		../b	MINGW
+relative_path C:/a/b	D:/a/c		C:/a/b	MINGW
 relative_path a/b	"<empty>"	a/b
 relative_path a/b 	"<null>"	a/b
 relative_path "<empty>"	/a/b		./
Signature · 1 line
1.8.3.rc2.14.g5ac1b82
#13 lore raw

PATCH 2/2 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 to simple_relative_path, and call it in setup.c.

---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
cache.h +1 -0
index 8e42256..ab17f1d 100644
@@ -738,6 +738,7 @@ const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
+const char *simple_relative_path(const char *in, const char *prefix);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
path.c +45 -0

51 lines not on this page · raw message

setup.c +1 -4
index 0d9ea62..f4be6ff 100644
@@ -360,7 +360,6 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
-	struct strbuf sb = STRBUF_INIT;
 	const char *work_tree, *git_dir;
 	static int initialized = 0;
 
@@ -380,10 +379,8 @@ void setup_work_tree(void)
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-	set_git_dir(relative_path(git_dir, work_tree, &sb));
+	set_git_dir(simple_relative_path(git_dir, work_tree));
 	initialized = 1;
-
-	strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
Signature · 1 line
1.8.3.rc2.14.g5ac1b82
#14 lore raw

Re: [PATCH 2/2] Use simpler relative_path when set_git_dir

Am 12.09.2013 11:12, schrieb Jiang Xin:

Quoted 37 lines

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 to simple_relative_path, and call it in setup.c.

---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
cache.h +1 -0
index 8e42256..ab17f1d 100644
@@ -738,6 +738,7 @@ const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
+const char *simple_relative_path(const char *in, const char *prefix);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
path.c +0 -0
index ffcdea1..0f0c92f 100644

path.c const char *relative_path(const char *in, const char *prefix, · line 561

Quoted 4 lines
path.c +1 -0
@@ -558,6 +558,51 @@ const char *relative_path(const char *in, const char *prefix,
 }
 
 /*
+ * A simpler implementation of relative_path

So we have a heavy-duty function relative_path(), but it is not capable of doing the "simple" operations that this function does?

There must be something wrong.

This function were easier to sell if it were named remove_optional_prefix() or something.

Quoted 43 lines
path.c +42 -0
+ *
+ * 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 *simple_relative_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;
+}
...
#15 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

In reply to Jiang Xin
Quoted 23 lines · Jiang Xin

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have dos driver prefix. ($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.

---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
path.c +0 -0
index 7f3324a..ffcdea1 100644

path.c int adjust_shared_perm(const char *path) · line 450

Quoted 10 lines
path.c +7 -0
@@ -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 && !strncasecmp(path1, path2, 1)) ||
                                       ^^^^^^^^^^^

I wonder: should strncasecmp() be replaced with strncmp_icase() ?

See dir.c:
int strncmp_icase(const char *a, const char *b, size_t count)

{
	return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
}

/Torsten

Quoted 39 lines
path.c +13 -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]))
t/t0060-path-utils.sh +4 -0
index 76c7792..c3c3b33 100755
@@ -208,6 +208,10 @@ relative_path a/b/	a/b		./
 relative_path a		a/b		../
 relative_path x/y	a/b		../../x/y
 relative_path a/c	a/b		../c
+relative_path a/b	/x/y		a/b
+relative_path /a/b	x/y		/a/b	POSIX
+relative_path d:/a/b	D:/a/c		../b	MINGW
+relative_path C:/a/b	D:/a/c		C:/a/b	MINGW
 relative_path a/b	"<empty>"	a/b
 relative_path a/b 	"<null>"	a/b
 relative_path "<empty>"	/a/b		./
#16 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

Am 12.09.2013 16:13, schrieb Torsten Bögershausen:

Quoted 10 lines · Jiang Xin

path.c int adjust_shared_perm(const char *path) · line 450

Quoted 7 lines
path.c +7 -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 && !strncasecmp(path1, path2, 1)) ||
                                       ^^^^^^^^^^^

I wonder: should strncasecmp() be replaced with strncmp_icase() ?

I don't think so: On POSIX, it is irrelevant, because the call will only compare a slash to a slash. On Windows, it compares the drive letters (or a slash); it is *always* case-insensitive, even if the volume mounted is NTFS with case-sensitivity enabled and core.ignorecase is false.

Quoted 5 lines

See dir.c:
int strncmp_icase(const char *a, const char *b, size_t count)

{
	return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
}

-- Hannes

#17 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

Quoted 12 lines · Johannes Sixt

Am 12.09.2013 16:13, schrieb Torsten Bögershausen:

Quoted 10 lines · Jiang Xin

path.c int adjust_shared_perm(const char *path) · line 450

Quoted 7 lines
path.c +7 -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 && !strncasecmp(path1, path2, 1)) ||
                                       ^^^^^^^^^^^

I wonder: should strncasecmp() be replaced with strncmp_icase() ?

Quoted 4 lines

I don't think so: On POSIX, it is irrelevant, because the call will only compare a slash to a slash. On Windows, it compares the drive letters (or a slash); it is *always* case-insensitive, even if the volume mounted is NTFS with case-sensitivity enabled and core.ignorecase is false.

Ah, you are right, of course. We could even do

	tolower(path1[0]) == tolower(path2[0])

which might be more explicit.

For systems that need POSIX escape hatch for Apollo Domain ;-), we would need a bit more work. When both path1 and path2 begin with a double-dash, we would need to check if they match up to the next slash, so that

- //host1/usr/src and //host1/usr/lib share the same root and the former can be made to ../src relative to the latter;

- //host1/usr/src and //host2/usr/lib are of separate roots.

or something.

#18 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

2013/9/13 Junio C Hamano <[email protected]>:

Quoted 6 lines

For systems that need POSIX escape hatch for Apollo Domain ;-), we would need a bit more work. When both path1 and path2 begin with a double-dash, we would need to check if they match up to the next slash, so that

Quoted 8 lines

- //host1/usr/src and //host1/usr/lib share the same root and the former can be made to ../src relative to the latter;

- //host1/usr/src and //host2/usr/lib are of separate roots.

or something.

But how could we know which platform supports network pathnames and needs such implementation.

Signature · 1 line
Jiang Xin
#19 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

Quoted 9 lines · Jiang Xin

2013/9/13 Junio C Hamano <[email protected]>:

Quoted 8 lines

For systems that need POSIX escape hatch for Apollo Domain ;-), we would need a bit more work. When both path1 and path2 begin with a double-dash, we would need to check if they match up to the next slash, so that

- //host1/usr/src and //host1/usr/lib share the same root and the former can be made to ../src relative to the latter;

Quoted 7 lines
Quoted 5 lines

- //host1/usr/src and //host2/usr/lib are of separate roots.

or something.

But how could we know which platform supports network pathnames and needs such implementation.

Similar to the has_dos_drive_prefix:

For Windows/Mingw we do like this

mingw.h
#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')

And all other platforms defines has_dos_drive_prefix() to be 0 here git-compat-util.h
#ifndef has_dos_drive_prefix
#define has_dos_drive_prefix(path) 0
#endif

mingw.h:
#define has_unc_path_prefix(path) ((path)[0] == '/' && (path)[1] == '/') (Or may be)
#define has_unc_path_prefix(path) (is_dir_sep((path)[0]) && is_dir_sep((path)[1]))

#20 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

In reply to Jiang Xin
Quoted 10 lines · Jiang Xin

2013/9/13 Junio C Hamano <[email protected]>:

Quoted 9 lines

For systems that need POSIX escape hatch for Apollo Domain ;-), we would need a bit more work. When both path1 and path2 begin with a double-dash, we would need to check if they match up to the next slash, so that

- //host1/usr/src and //host1/usr/lib share the same root and the former can be made to ../src relative to the latter;

Quoted 6 lines
Quoted 3 lines

- //host1/usr/src and //host2/usr/lib are of separate roots.

or something.

But how could we know which platform supports network pathnames and needs such implementation.

Near the end of

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12

is this:

If a pathname begins with two successive <slash> characters, the first component following the leading <slash> characters may be interpreted in an implementation-defined manner, although more than two leading <slash> characters shall be treated as a single <slash> character.

Two points to note are

(1) Only paths that begin with exactly two slashes are special.

(2) As it is "implementation-defined", we are not even allowed to treat that //host1/usr/src and //host1/usr/lib as sharing "the same root", and make the former to ../src relative to the latter.

So in the strictest sense, we do not have to bother. As long as we make sure we do not molest anything that begins with exactly two slashes.

#21 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

2013/9/13 Junio C Hamano <[email protected]>:

Quoted 35 lines
Quoted 10 lines · Jiang Xin

2013/9/13 Junio C Hamano <[email protected]>:

Quoted 9 lines

For systems that need POSIX escape hatch for Apollo Domain ;-), we would need a bit more work. When both path1 and path2 begin with a double-dash, we would need to check if they match up to the next slash, so that

- //host1/usr/src and //host1/usr/lib share the same root and the former can be made to ../src relative to the latter;

Quoted 6 lines
Quoted 3 lines

- //host1/usr/src and //host2/usr/lib are of separate roots.

or something.

But how could we know which platform supports network pathnames and needs such implementation.

Near the end of

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12

is this:

If a pathname begins with two successive <slash> characters, the first component following the leading <slash> characters may be interpreted in an implementation-defined manner, although more than two leading <slash> characters shall be treated as a single <slash> character.

Two points to note are

(1) Only paths that begin with exactly two slashes are special.

Quoted 9 lines

(2) As it is "implementation-defined", we are not even allowed to treat that //host1/usr/src and //host1/usr/lib as sharing "the same root", and make the former to ../src relative to the latter.

So in the strictest sense, we do not have to bother. As long as we make sure we do not molest anything that begins with exactly two slashes.

I have checked the behavior of UNC path on Windows (msysGit):

* I can cd to a UNC path:

cd //server1/share1/path

* can cd to other share:

    cd ../../share2/path

* and can cd to other server's share:

    cd ../../../server2/share/path

That means relative_path(path1, path2) support UNC paths out of the box. We only need to check both path1 and path2 are UNC paths, or both not.

So, funciton “have_same_root" will write like this:

+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);
+       if (is_abs1 && is_abs2) {

+ if (is_unc_path(path1) ^ is_unc_path(path2))
+ return 0;
+ return tolower(path1[0]) == tolower(path2[0]);
+ } else {
+ return !is_abs1 && !is_abs2;
+ }
+}

Signature · 1 line
Jiang Xin
#22 lore raw

PATCH v3 1/3 test: use unambigous leading path (/foo) for mingw

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such DOS drive on MINGW platform. Use an umambigous leading path "/foo" instead.

Also change two leading slashes (//) to three leading slashes (///), otherwize it will be recognized as UNC path on MINGW platform.

---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
t/t0060-path-utils.sh +28 -28
index 3a48de2..92976e0 100755
@@ -190,33 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
 	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
-relative_path /a/b/c/	/a/b/		c/
-relative_path /a/b/c/	/a/b		c/
-relative_path /a//b//c/	//a/b//		c/	POSIX
-relative_path /a/b	/a/b		./
-relative_path /a/b/	/a/b		./
-relative_path /a	/a/b		../
-relative_path /		/a/b/		../../
-relative_path /a/c	/a/b/		../c
-relative_path /a/c	/a/b		../c
-relative_path /x/y	/a/b/		../../x/y
-relative_path /a/b	"<empty>"	/a/b
-relative_path /a/b 	"<null>"	/a/b
-relative_path a/b/c/	a/b/		c/
-relative_path a/b/c/	a/b		c/
-relative_path a/b//c	a//b		c
-relative_path a/b/	a/b/		./
-relative_path a/b/	a/b		./
-relative_path a		a/b		../
-relative_path x/y	a/b		../../x/y
-relative_path a/c	a/b		../c
-relative_path a/b	"<empty>"	a/b
-relative_path a/b 	"<null>"	a/b
-relative_path "<empty>"	/a/b		./
-relative_path "<empty>"	"<empty>"	./
-relative_path "<empty>"	"<null>"	./
-relative_path "<null>"	"<empty>"	./
-relative_path "<null>"	"<null>"	./
-relative_path "<null>"	/a/b		./
+relative_path /foo/a/b/c/	/foo/a/b/	c/
+relative_path /foo/a/b/c/	/foo/a/b	c/
+relative_path /foo/a//b//c/	///foo/a/b//	c/		POSIX
+relative_path /foo/a/b		/foo/a/b	./
+relative_path /foo/a/b/		/foo/a/b	./
+relative_path /foo/a		/foo/a/b	../
+relative_path /			/foo/a/b/	../../../
+relative_path /foo/a/c		/foo/a/b/	../c
+relative_path /foo/a/c		/foo/a/b	../c
+relative_path /foo/x/y		/foo/a/b/	../../x/y
+relative_path /foo/a/b		"<empty>"	/foo/a/b
+relative_path /foo/a/b 		"<null>"	/foo/a/b
+relative_path foo/a/b/c/	foo/a/b/	c/
+relative_path foo/a/b/c/	foo/a/b		c/
+relative_path foo/a/b//c	foo/a//b	c
+relative_path foo/a/b/		foo/a/b/	./
+relative_path foo/a/b/		foo/a/b		./
+relative_path foo/a		foo/a/b		../
+relative_path foo/x/y		foo/a/b		../../x/y
+relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		"<empty>"	foo/a/b
+relative_path foo/a/b 		"<null>"	foo/a/b
+relative_path "<empty>"		/foo/a/b	./
+relative_path "<empty>"		"<empty>"	./
+relative_path "<empty>"		"<null>"	./
+relative_path "<null>"		"<empty>"	./
+relative_path "<null>"		"<null>"	./
+relative_path "<null>"		/foo/a/b	./
 
 test_done
Signature · 1 line
1.8.4.460.g2f083d1
#23 lore raw

PATCH v3 2/3 relative_path should honor DOS and UNC paths

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have DOS driver prefix (such as "C:/windows"). And the "relative_path" function won't work properly if either "in" or "prefix" is a UNC path (like "//host/share"). ($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 and UNC paths, and add test cases for it in t0060.

---
 compat/mingw.h        |  9 +++++++++
 git-compat-util.h     |  4 ++++
 path.c                | 25 +++++++++++++++++++++++++
 t/t0060-path-utils.sh |  8 ++++++++
 4 files changed, 46 insertions(+)
compat/mingw.h +9 -0
index bd0a88b..06e9f49 100644
@@ -311,6 +311,15 @@ int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format
 
 #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
 #define is_dir_sep(c) ((c) == '/' || (c) == '\\')
+static inline int is_unc_path(const char *path)
+{
+	if (!is_dir_sep(*path) || !is_dir_sep(*(path+1)) || is_dir_sep(*(path+2)))
+		return 0;
+	for (path += 2; *path; path++)
+		if (is_dir_sep(*path))
+			return 1;
+	return 0;
+}
 static inline char *mingw_find_last_dir_sep(const char *path)
 {
 	char *ret = NULL;
git-compat-util.h +4 -0
index 9549de6..93c2206 100644
@@ -274,6 +274,10 @@ extern char *gitbasename(char *);
 #define is_dir_sep(c) ((c) == '/')
 #endif
 
+#ifndef is_unc_path
+#define is_unc_path(path) 0
+#endif
+
 #ifndef find_last_dir_sep
 #define find_last_dir_sep(path) strrchr(path, '/')
 #endif
path.c +25 -0
index 9fd28bcd..544d10d 100644
@@ -434,6 +434,21 @@ 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);
+	if (is_abs1 && is_abs2) {
+		if (is_unc_path(path1) ^ is_unc_path(path2))
+			return 0;
+		return tolower(path1[0]) == tolower(path2[0]);
+	} else {
+		return !is_abs1 && !is_abs2;
+	}
+}
+
 /*
  * Give path as relative to prefix.
  *
@@ -454,6 +469,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]))
t/t0060-path-utils.sh +8 -0
index 92976e0..830b6d5 100755
@@ -210,6 +210,14 @@ relative_path foo/a/b/		foo/a/b		./
 relative_path foo/a		foo/a/b		../
 relative_path foo/x/y		foo/a/b		../../x/y
 relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		/foo/x/y	foo/a/b
+relative_path /foo/a/b		foo/x/y		/foo/a/b
+relative_path d:/a/b		D:/a/c		../b		MINGW
+relative_path C:/a/b		D:/a/c		C:/a/b		MINGW
+relative_path //host1/a/b	//host1/a/c	../b
+relative_path //host1/a/b	//host2/a/c	../../../host1/a/b
+relative_path //host1/a/b	/foo/a/c	//host1/a/b	MINGW
+relative_path /foo/a/b		//host2/a/c	/foo/a/b	MINGW
 relative_path foo/a/b		"<empty>"	foo/a/b
 relative_path foo/a/b 		"<null>"	foo/a/b
 relative_path "<empty>"		/foo/a/b	./
Signature · 1 line
1.8.4.460.g2f083d1
#24 lore raw

Re: [PATCH v3 2/3] relative_path should honor DOS and UNC paths

compat/mingw.h int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format · line 317

Quoted 12 lines · Jiang Xin
compat/mingw.h +4 -0
index bd0a88b..06e9f49 100644
@@ -311,6 +311,15 @@ int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format
 
 #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
 #define is_dir_sep(c) ((c) == '/' || (c) == '\\')
+static inline int is_unc_path(const char *path)
+{
+	if (!is_dir_sep(*path) || !is_dir_sep(*(path+1)) || is_dir_sep(*(path+2)))
+		return 0;

If path[1] == '\0', it would be !is_dir_sep() and we end up inspecting past the end of the string?

A message that is not in the archive (sent off-list or not yet received): [email protected]

#26 lore raw

Re: [PATCH v3 2/3] relative_path should honor DOS and UNC paths

Quoted 9 lines · Torsten Bögershausen
Quoted 8 lines · Junio C Hamano
Quoted 6 lines · Jiang Xin
compat/mingw.h +0 -0
index bd0a88b..06e9f49 100644
@@ -311,6 +311,15 @@ int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format
 
Quoted 9 lines
Quoted 9 lines

compat/mingw.h int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format · line 317

Quoted 6 lines
compat/mingw.h +4 -0
 #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
 #define is_dir_sep(c) ((c) == '/' || (c) == '\\')
+static inline int is_unc_path(const char *path)
+{
+	if (!is_dir_sep(*path) || !is_dir_sep(*(path+1)) || is_dir_sep(*(path+2)))
+		return 0;

If path[1] == '\0', it would be !is_dir_sep() and we end up inspecting past the end of the string?

Yes
(If there was a previous mail, it was incomplete, sorry)

I think we want to catch "2 (back)slashes followed by a letter"
<http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx>

#define is_unc_path(path) ((is_dir_sep(path)[0]) && is_dir_sep((path)[1]) && isalpha((path[2])))

Then we need
#define is_relative_path(path) (((path)[0]) && !is_dir_sep((path)[1]))

And may be like this:

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);
	if (is_abs1 && is_abs2) {
		if (is_unc_path(path1) && !is_relative_path(path2))
			return 0;
		if (!is_relative_path(path1) && is_unc_path(path2))
			return 0;
		return tolower(path1[0]) == tolower(path2[0]);
	} else {
		return !is_abs1 && !is_abs2;
	}
}

Could that work?

#27 lore raw

Re: [PATCH v3 2/3] relative_path should honor DOS and UNC paths

In reply to Junio C Hamano

2013/9/18 Junio C Hamano <[email protected]>:

Quoted 8 lines
Quoted 6 lines · Jiang Xin
compat/mingw.h +0 -0
index bd0a88b..06e9f49 100644
@@ -311,6 +311,15 @@ int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format
 
Quoted 6 lines

compat/mingw.h int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format · line 317

Quoted 6 lines
compat/mingw.h +4 -0
 #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
 #define is_dir_sep(c) ((c) == '/' || (c) == '\\')
+static inline int is_unc_path(const char *path)
+{
+     if (!is_dir_sep(*path) || !is_dir_sep(*(path+1)) || is_dir_sep(*(path+2)))
+             return 0;

A UNC path must start with two slashes, but not three or more slashes.

Quoted 3 lines

If path[1] == '\0', it would be !is_dir_sep() and we end up inspecting past the end of the string?

The funciton "is_unc_path" will return false (0), if path is "", "/", "//", "///three/slashes/", or "/usr/local". So the problem is ?

Signature · 1 line
Jiang Xin
#28 lore raw

Re: [PATCH v3 2/3] relative_path should honor DOS and UNC paths

Quoted 9 lines · Jiang Xin

2013/9/18 Junio C Hamano <[email protected]>:

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+     if (!is_dir_sep(*path) || !is_dir_sep(*(path+1)) || is_dir_sep(*(path+2)))
+             return 0;

If path[1] == '\0', it would be !is_dir_sep() and we end up inspecting past the end of the string?

The funciton "is_unc_path" will return false (0), if path is "", "/", "//", "///three/slashes/", or "/usr/local". So the problem is ?

If path[1] == '\0' (e.g. path="/"), !is_dir_sep(path[1]) is true, not false (as I misread earlier), so we hit an early return and will not peek path[2]. So no problem. Sorry for the noise.

But I agree with J6t and Torsten in near-by thread that the simpler one that does not worry about // should be done as a separate patch and //, if we decide to do it, should build on top.

Thanks.

#29 lore raw

PATCH v3 3/3 Use simpler relative_path when set_git_dir

In reply to Jiang Xin

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.

---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
cache.h +1 -0
index a47b9c0..73fa334 100644
@@ -747,6 +747,7 @@ int is_directory(const char *);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
+const char *remove_leading_path(const char *in, const char *prefix);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
 int normalize_path_copy(char *dst, const char *src);
path.c +45 -0

51 lines not on this page · raw message

setup.c +1 -4
index f08dd64..dbf4138 100644
@@ -227,7 +227,6 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
-	struct strbuf sb = STRBUF_INIT;
 	const char *work_tree, *git_dir;
 	static int initialized = 0;
 
@@ -247,10 +246,8 @@ void setup_work_tree(void)
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-	set_git_dir(relative_path(git_dir, work_tree, &sb));
+	set_git_dir(remove_leading_path(git_dir, work_tree));
 	initialized = 1;
-
-	strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
Signature · 1 line
1.8.4.460.g2f083d1
#30 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

In reply to Jiang Xin

Am 17.09.2013 10:24, schrieb Jiang Xin:

Quoted 8 lines

I have checked the behavior of UNC path on Windows (msysGit):

* I can cd to a UNC path:

cd //server1/share1/path

* can cd to other share:

Quoted 8 lines
    cd ../../share2/path

* and can cd to other server's share:

    cd ../../../server2/share/path

That means relative_path(path1, path2) support UNC paths out of the box. We only need to check both path1 and path2 are UNC paths, or both not.

Your tests are flawed. You issued the commands in bash, which (or rather MSYS) does everything for you that you need to make it work. But in reality it does not, because the system cannot apply .. to //server/share:

$ git ls-remote //srv/public/../repos/his/setups.git
fatal: '//srv/public/../repos/his/setups.git' does not appear to be a
git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists.

even though the repository (and //srv/public, let me assure) exists:

$ git ls-remote //srv/repos/his/setups.git
bea489b0611a72c41f133343fdccbd3e2b9f80b5        HEAD
...

The situation does not change with your latest round (v3).

Please let me suggest not to scratch where there is no itch. ;) Your round v2 was good enough.

If you really want to check UNC paths, then you must compare two path components after the the double-slash, not just one.

Furthermore, you should audit all code that references is_absolute_path(), relative_path(), normalize_path_copy(), and possibly a few others whether the functions or call sites need improvement. That's worth a separate patch.

-- Hannes

#31 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

Quoted 44 lines · Johannes Sixt

Am 17.09.2013 10:24, schrieb Jiang Xin:

Quoted 8 lines

I have checked the behavior of UNC path on Windows (msysGit):

* I can cd to a UNC path:

cd //server1/share1/path

* can cd to other share:

Quoted 8 lines
    cd ../../share2/path

* and can cd to other server's share:

    cd ../../../server2/share/path

That means relative_path(path1, path2) support UNC paths out of the box. We only need to check both path1 and path2 are UNC paths, or both not.

Your tests are flawed. You issued the commands in bash, which (or rather MSYS) does everything for you that you need to make it work. But in reality it does not, because the system cannot apply .. to //server/share:

$ git ls-remote //srv/public/../repos/his/setups.git
fatal: '//srv/public/../repos/his/setups.git' does not appear to be a
git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists.

even though the repository (and //srv/public, let me assure) exists:

$ git ls-remote //srv/repos/his/setups.git
bea489b0611a72c41f133343fdccbd3e2b9f80b5        HEAD
...

The situation does not change with your latest round (v3).

Please let me suggest not to scratch where there is no itch. ;) Your round v2 was good enough.

If you really want to check UNC paths, then you must compare two path components after the the double-slash, not just one.

Quoted 6 lines

Furthermore, you should audit all code that references is_absolute_path(), relative_path(), normalize_path_copy(), and possibly a few others whether the functions or call sites need improvement. That's worth a separate patch.

-- Hannes

I tend to agree here.
The V2 patch fixed a regression.
This should be one commit on its own:
Documentation/SubmittingPatches:
(1) Make separate commits for logically separate changes.

Fixing a bug is a good thing, thanks for working on this,

The support for UNC paths is a new feature, and this deserves a seperate commit. /Torsten

#32 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

In reply to Johannes Sixt

2013/9/18 Johannes Sixt <[email protected]>:

Quoted 27 lines

Am 17.09.2013 10:24, schrieb Jiang Xin:

Quoted 8 lines

I have checked the behavior of UNC path on Windows (msysGit):

* I can cd to a UNC path:

cd //server1/share1/path

* can cd to other share:

Quoted 8 lines
    cd ../../share2/path

* and can cd to other server's share:

    cd ../../../server2/share/path

That means relative_path(path1, path2) support UNC paths out of the box. We only need to check both path1 and path2 are UNC paths, or both not.

Your tests are flawed. You issued the commands in bash, which (or rather MSYS) does everything for you that you need to make it work. But in reality it does not, because the system cannot apply .. to //server/share:

$ git ls-remote //srv/public/../repos/his/setups.git
fatal: '//srv/public/../repos/his/setups.git' does not appear to be a
git repository
fatal: Could not read from remote repository.
Quoted 8 lines

Please make sure you have the correct access rights and the repository exists.

even though the repository (and //srv/public, let me assure) exists:

$ git ls-remote //srv/repos/his/setups.git
bea489b0611a72c41f133343fdccbd3e2b9f80b5        HEAD
...

After see this link (provided by Torsten):

    <http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx>

I find the following commands could work:

    $ git ls-remote //srv/repos/his/setups.git
    $ git ls-remote //srv/repos/his/../his/setups.git
    $ git ls-remote //?/UNC/srv/repos/his/setups.git
    $ git ls-remote //?/UNC/srv/repos/../repos/his/setups.git

But no luck for this one:

    $ git ls-remote //srv/repos/../repos/his/setups.git

I trace it using gdb, and find it failed in "stat()/mingw_stat()" call of function "enter_repo" in path.c. But I can not find out why "git ls-remote //?/UNC/srv/repos/../repos/his/setups.git" could work (success in shell, failed in gdb).

Quoted 6 lines

Please let me suggest not to scratch where there is no itch. ;) Your round v2 was good enough.

If you really want to check UNC paths, then you must compare two path components after the the double-slash, not just one.

I have already try this (honor two path components after //) during the reroll for patch v3. But I am not satisfied with it, and it seems like over-engineered: Rename "have_same_root" to "get_common_root_prefix_width", and it return -1 for no same_root found, otherwize return the length of root_prefix_width.

Since restored the default behavior of setup.c in commit ("Use simpler relative_path when set_git_dir"), function "relative_path" are only used in "quote.c" and "builtin/clean.c", and two paths provided to "relative_path" are always (I can not find exception) relative paths. So no itch exist I think.

Signature · 1 line
Jiang Xin
#33 lore raw

v4 cover relative path regression fix

Remove implementations on UNC names in patch v3.
So patch v4 is the same like v2, but with minor
update for commit logs.

Jiang Xin (3):
test: use unambigous leading path (/foo) for mingw relative_path should honor dos-driver-prefix Use simpler relative_path when set_git_dir

 cache.h               |  1 +
 path.c                | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++
 setup.c               |  5 +---
 t/t0060-path-utils.sh | 60 +++++++++++++++++++++++++----------------------
 4 files changed, 99 insertions(+), 32 deletions(-)
Signature · 1 line
1.8.4.460.gbed9cb4
#34 lore raw

PATCH v4 1/3 test: use unambigous leading path (/foo) for mingw

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such DOS drive on MINGW platform. Use an umambigous leading path "/foo" instead.

Also change two leading slashes (//) to three leading slashes (///), otherwize it will be recognized as UNC name on MINGW platform.

---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
t/t0060-path-utils.sh +28 -28
index 3a48de2..92976e0 100755
@@ -190,33 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
 	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
-relative_path /a/b/c/	/a/b/		c/
-relative_path /a/b/c/	/a/b		c/
-relative_path /a//b//c/	//a/b//		c/	POSIX
-relative_path /a/b	/a/b		./
-relative_path /a/b/	/a/b		./
-relative_path /a	/a/b		../
-relative_path /		/a/b/		../../
-relative_path /a/c	/a/b/		../c
-relative_path /a/c	/a/b		../c
-relative_path /x/y	/a/b/		../../x/y
-relative_path /a/b	"<empty>"	/a/b
-relative_path /a/b 	"<null>"	/a/b
-relative_path a/b/c/	a/b/		c/
-relative_path a/b/c/	a/b		c/
-relative_path a/b//c	a//b		c
-relative_path a/b/	a/b/		./
-relative_path a/b/	a/b		./
-relative_path a		a/b		../
-relative_path x/y	a/b		../../x/y
-relative_path a/c	a/b		../c
-relative_path a/b	"<empty>"	a/b
-relative_path a/b 	"<null>"	a/b
-relative_path "<empty>"	/a/b		./
-relative_path "<empty>"	"<empty>"	./
-relative_path "<empty>"	"<null>"	./
-relative_path "<null>"	"<empty>"	./
-relative_path "<null>"	"<null>"	./
-relative_path "<null>"	/a/b		./
+relative_path /foo/a/b/c/	/foo/a/b/	c/
+relative_path /foo/a/b/c/	/foo/a/b	c/
+relative_path /foo/a//b//c/	///foo/a/b//	c/		POSIX
+relative_path /foo/a/b		/foo/a/b	./
+relative_path /foo/a/b/		/foo/a/b	./
+relative_path /foo/a		/foo/a/b	../
+relative_path /			/foo/a/b/	../../../
+relative_path /foo/a/c		/foo/a/b/	../c
+relative_path /foo/a/c		/foo/a/b	../c
+relative_path /foo/x/y		/foo/a/b/	../../x/y
+relative_path /foo/a/b		"<empty>"	/foo/a/b
+relative_path /foo/a/b 		"<null>"	/foo/a/b
+relative_path foo/a/b/c/	foo/a/b/	c/
+relative_path foo/a/b/c/	foo/a/b		c/
+relative_path foo/a/b//c	foo/a//b	c
+relative_path foo/a/b/		foo/a/b/	./
+relative_path foo/a/b/		foo/a/b		./
+relative_path foo/a		foo/a/b		../
+relative_path foo/x/y		foo/a/b		../../x/y
+relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		"<empty>"	foo/a/b
+relative_path foo/a/b 		"<null>"	foo/a/b
+relative_path "<empty>"		/foo/a/b	./
+relative_path "<empty>"		"<empty>"	./
+relative_path "<empty>"		"<null>"	./
+relative_path "<null>"		"<empty>"	./
+relative_path "<null>"		"<null>"	./
+relative_path "<null>"		/foo/a/b	./
 
 test_done
Signature · 1 line
1.8.4.460.gbed9cb4
#35 lore raw

Re: [PATCH v4 1/3] test: use unambigous leading path (/foo) for mingw

Quoted 7 lines · Jiang Xin

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such DOS drive on MINGW platform. Use an umambigous leading path "/foo" instead.

Also change two leading slashes (//) to three leading slashes (///), otherwize it will be recognized as UNC name on MINGW platform.

Note that the path mangling comes from MSYS [1], not MinGW, so you should place "MINGW" with "MSYS" in several places. As a side-note, the official spelling is "MinGW", not "MINGW".

t/t0060-path-utils.sh test_expect_success SYMLINKS 'real path works on symlinks' '

Quoted 3 lines
t/t0060-path-utils.sh +1 -1
-relative_path /a/b/c/	/a/b/		c/
 
+relative_path /foo/a/b/c/	/foo/a/b/	c/

Wouldn't it have been more straight-forward to just replace "a" with "foo", "b" with "bar" and "c" with "baz" (or whatever)? So that the first line would say

relative_path /foo/bar/baz/ /foo/bar/ baz/

Thanks for the fix!

[1] http://www.mingw.org/wiki/Posix_path_conversion

Signature · 1 line
Sebastian Schuberth
#36 lore raw

Re: [PATCH v4 1/3] test: use unambigous leading path (/foo) for mingw

2013/10/11 Sebastian Schuberth <[email protected]>:

Quoted 5 lines
Quoted 5 lines

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such DOS drive on MINGW platform. Use an umambigous leading path "/foo" instead.

Quoted 7 lines
Quoted 2 lines

Also change two leading slashes (//) to three leading slashes (///), otherwize it will be recognized as UNC name on MINGW platform.

Note that the path mangling comes from MSYS [1], not MinGW, so you should place "MINGW" with "MSYS" in several places. As a side-note, the official spelling is "MinGW", not "MINGW".

I will make a reroll. s/MINGW/MSYS/i

Quoted 12 lines

t/t0060-path-utils.sh test_expect_success SYMLINKS 'real path works on symlinks' '

Quoted 4 lines
t/t0060-path-utils.sh +1 -1
-relative_path /a/b/c/  /a/b/           c/
 
 
+relative_path /foo/a/b/c/      /foo/a/b/       c/

Wouldn't it have been more straight-forward to just replace "a" with "foo", "b" with "bar" and "c" with "baz" (or whatever)? So that the first line would say

relative_path /foo/bar/baz/ /foo/bar/ baz/

These test cases have been used in some commit logs, such as
commit: v1.8.3-rc2-13-gad66df2. And for me (a non-English speaker) a,b,c,x,y,z are more readable than bar, baz, qux, ...

Signature · 1 line
Jiang Xin
#37 lore raw

v5 cover relative path regression fix

In reply to Sebastian Schuberth

Update since v4:

* Update commit logs with the help from Sebastian Schuberth:

s/MINGW/MSYS/i
s/dos-driver-prefix/dos-drive-prefix/

Jiang Xin (3):
test: use unambigous leading path (/foo) for MSYS relative_path should honor dos-drive-prefix Use simpler relative_path when set_git_dir

 cache.h               |  1 +
 path.c                | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++
 setup.c               |  5 +---
 t/t0060-path-utils.sh | 60 +++++++++++++++++++++++++----------------------
 4 files changed, 99 insertions(+), 32 deletions(-)
Signature · 1 line
1.8.4
#38 lore raw

PATCH v5 1/3 test: use unambigous leading path (/foo) for MSYS

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such DOS drive on MSYS platform. Use an umambigous leading path "/foo" instead.

Also change two leading slashes (//) to three leading slashes (///), otherwize it will be recognized as UNC name on MSYS platform.

---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
t/t0060-path-utils.sh +28 -28
index 3a48de2..92976e0 100755
@@ -190,33 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
 	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
-relative_path /a/b/c/	/a/b/		c/
-relative_path /a/b/c/	/a/b		c/
-relative_path /a//b//c/	//a/b//		c/	POSIX
-relative_path /a/b	/a/b		./
-relative_path /a/b/	/a/b		./
-relative_path /a	/a/b		../
-relative_path /		/a/b/		../../
-relative_path /a/c	/a/b/		../c
-relative_path /a/c	/a/b		../c
-relative_path /x/y	/a/b/		../../x/y
-relative_path /a/b	"<empty>"	/a/b
-relative_path /a/b 	"<null>"	/a/b
-relative_path a/b/c/	a/b/		c/
-relative_path a/b/c/	a/b		c/
-relative_path a/b//c	a//b		c
-relative_path a/b/	a/b/		./
-relative_path a/b/	a/b		./
-relative_path a		a/b		../
-relative_path x/y	a/b		../../x/y
-relative_path a/c	a/b		../c
-relative_path a/b	"<empty>"	a/b
-relative_path a/b 	"<null>"	a/b
-relative_path "<empty>"	/a/b		./
-relative_path "<empty>"	"<empty>"	./
-relative_path "<empty>"	"<null>"	./
-relative_path "<null>"	"<empty>"	./
-relative_path "<null>"	"<null>"	./
-relative_path "<null>"	/a/b		./
+relative_path /foo/a/b/c/	/foo/a/b/	c/
+relative_path /foo/a/b/c/	/foo/a/b	c/
+relative_path /foo/a//b//c/	///foo/a/b//	c/		POSIX
+relative_path /foo/a/b		/foo/a/b	./
+relative_path /foo/a/b/		/foo/a/b	./
+relative_path /foo/a		/foo/a/b	../
+relative_path /			/foo/a/b/	../../../
+relative_path /foo/a/c		/foo/a/b/	../c
+relative_path /foo/a/c		/foo/a/b	../c
+relative_path /foo/x/y		/foo/a/b/	../../x/y
+relative_path /foo/a/b		"<empty>"	/foo/a/b
+relative_path /foo/a/b 		"<null>"	/foo/a/b
+relative_path foo/a/b/c/	foo/a/b/	c/
+relative_path foo/a/b/c/	foo/a/b		c/
+relative_path foo/a/b//c	foo/a//b	c
+relative_path foo/a/b/		foo/a/b/	./
+relative_path foo/a/b/		foo/a/b		./
+relative_path foo/a		foo/a/b		../
+relative_path foo/x/y		foo/a/b		../../x/y
+relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		"<empty>"	foo/a/b
+relative_path foo/a/b 		"<null>"	foo/a/b
+relative_path "<empty>"		/foo/a/b	./
+relative_path "<empty>"		"<empty>"	./
+relative_path "<empty>"		"<null>"	./
+relative_path "<null>"		"<empty>"	./
+relative_path "<null>"		"<null>"	./
+relative_path "<null>"		/foo/a/b	./
 
 test_done
Signature · 1 line
1.8.4
#39 lore raw

Re: [PATCH v5 1/3] test: use unambigous leading path (/foo) for MSYS

Quoted 5 lines · Jiang Xin

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such DOS drive on MSYS platform. Use an umambigous leading path "/foo" instead.

Quoted 8 lines

Also change two leading slashes (//) to three leading slashes (///), otherwize it will be recognized as UNC name on MSYS platform.

---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)

Thanks, ack.

Signature · 1 line
Sebastian Schuberth
#40 lore raw

Re: [PATCH v5 1/3] test: use unambigous leading path (/foo) for MSYS

In reply to Jiang Xin
Quoted 7 lines · Jiang Xin

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such DOS drive on MSYS platform. Use an umambigous leading path "/foo" instead.

Also change two leading slashes (//) to three leading slashes (///), otherwize it will be recognized as UNC name on MSYS platform.

s/otherwize/otherwise/

Quoted 2 lines
#41 lore raw

PATCH v5 2/3 relative_path should honor dos-drive-prefix

In reply to Jiang Xin

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.

---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
path.c +20 -0
index 7f3324a..0c16dc5 100644
@@ -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]))
t/t0060-path-utils.sh +4 -0
index 92976e0..40dfa2d 100755
@@ -210,6 +210,10 @@ relative_path foo/a/b/		foo/a/b		./
 relative_path foo/a		foo/a/b		../
 relative_path foo/x/y		foo/a/b		../../x/y
 relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		/foo/x/y	foo/a/b
+relative_path /foo/a/b		foo/x/y		/foo/a/b
+relative_path d:/a/b		D:/a/c		../b		MINGW
+relative_path C:/a/b		D:/a/c		C:/a/b		MINGW
 relative_path foo/a/b		"<empty>"	foo/a/b
 relative_path foo/a/b 		"<null>"	foo/a/b
 relative_path "<empty>"		/foo/a/b	./
Signature · 1 line
1.8.4
#42 lore raw

PATCH v5 3/3 Use simpler relative_path when set_git_dir

In reply to Jiang Xin

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.

---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
cache.h +1 -0
index 8e42256..94475bd 100644
@@ -737,6 +737,7 @@ int is_directory(const char *);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
+const char *remove_leading_path(const char *in, const char *prefix);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
path.c +45 -0

51 lines not on this page · raw message

setup.c +1 -4
index 0d9ea62..dad39c1 100644
@@ -360,7 +360,6 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
-	struct strbuf sb = STRBUF_INIT;
 	const char *work_tree, *git_dir;
 	static int initialized = 0;
 
@@ -380,10 +379,8 @@ void setup_work_tree(void)
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-	set_git_dir(relative_path(git_dir, work_tree, &sb));
+	set_git_dir(remove_leading_path(git_dir, work_tree));
 	initialized = 1;
-
-	strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
Signature · 1 line
1.8.4
#43 lore raw

PATCH v4 2/3 relative_path should honor dos-driver-prefix

In reply to Jiang Xin

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have DOS driver 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-driver-prefix, and add test cases for it in t0060.

---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
path.c +20 -0
index 7f3324a..0c16dc5 100644
@@ -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]))
t/t0060-path-utils.sh +4 -0
index 92976e0..40dfa2d 100755
@@ -210,6 +210,10 @@ relative_path foo/a/b/		foo/a/b		./
 relative_path foo/a		foo/a/b		../
 relative_path foo/x/y		foo/a/b		../../x/y
 relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		/foo/x/y	foo/a/b
+relative_path /foo/a/b		foo/x/y		/foo/a/b
+relative_path d:/a/b		D:/a/c		../b		MINGW
+relative_path C:/a/b		D:/a/c		C:/a/b		MINGW
 relative_path foo/a/b		"<empty>"	foo/a/b
 relative_path foo/a/b 		"<null>"	foo/a/b
 relative_path "<empty>"		/foo/a/b	./
Signature · 1 line
1.8.4.460.gbed9cb4
#44 lore raw

Re: [PATCH v4 2/3] relative_path should honor dos-driver-prefix

Quoted 3 lines · Jiang Xin

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have DOS driver prefix (such as "C:/windows"). ($gmane/234434)

s/driver/drive/

Quoted 5 lines

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-driver-prefix, and add test cases for it in t0060.

s/dos-driver-prefix/DOS drive prefix/

Signature · 1 line
Sebastian Schuberth
#45 lore raw

PATCH v4 3/3 Use simpler relative_path when set_git_dir

In reply to Jiang Xin

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.

---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
cache.h +1 -0
index 8e42256..94475bd 100644
@@ -737,6 +737,7 @@ int is_directory(const char *);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
+const char *remove_leading_path(const char *in, const char *prefix);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
path.c +45 -0

51 lines not on this page · raw message

setup.c +1 -4
index 0d9ea62..dad39c1 100644
@@ -360,7 +360,6 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
-	struct strbuf sb = STRBUF_INIT;
 	const char *work_tree, *git_dir;
 	static int initialized = 0;
 
@@ -380,10 +379,8 @@ void setup_work_tree(void)
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-	set_git_dir(relative_path(git_dir, work_tree, &sb));
+	set_git_dir(remove_leading_path(git_dir, work_tree));
 	initialized = 1;
-
-	strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
Signature · 1 line
1.8.4.460.gbed9cb4
#46 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

In reply to Torsten Bögershausen
Quoted 9 lines · Torsten Bögershausen

path.c int adjust_shared_perm(const char *path) · line 450

Quoted 7 lines
path.c +7 -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 && !strncasecmp(path1, path2, 1)) ||
                                       ^^^^^^^^^^^

I wonder: should strncasecmp() be replaced with strncmp_icase() ?

True.

Quoted 5 lines

See dir.c:
int strncmp_icase(const char *a, const char *b, size_t count)

{
	return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
}
#47 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

In reply to Jiang Xin

Am 12.09.2013 11:12, schrieb Jiang Xin:

Quoted 10 lines

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have dos driver prefix. ($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.

I still don't think that cd'ing through the root is a Good Thing for absolute paths, as it is not possible to do so in general.

POSIX says the meaning of '//' is implementation-defined [1].

Cygwin supports //hostname/share/directory/file. You cannot cd to the hostname (i.e. root would be //hostname/share).

On Windows, we have drive letters, UNC paths and namespaces:
C:\directory\file
\\hostname\share\directory\file (same as cygwin) \\?\C:\directory\file \\?\UNC\hostname\share\directory\file

I'm not sure about '//' support on other git platforms (the most likely candidate would probably be HP-UX, as HP bought Apollo/DomainOS, which supported '//hostname/directory/file back in 1981).

You could of course handle all these special cases. However, with the POSIX definition above, the only reliable and future-proof way to check if we can cd out of a path component of an _absolute_ path is to actually try it until we get an error. And we probably don't want to do actual IO in relative_path (which is pure string manipulation so far).

[1] http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_12

author
#48 lore raw

Re: [PATCH 1/2] relative_path should honor dos_drive_prefix

In reply to Jiang Xin

Thank you, this fixes the problem with git svn rebase on Windows for me.

--Tvangeste

Quoted 74 lines · Jiang Xin

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have dos driver prefix. ($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.

---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
path.c +20 -0
index 7f3324a..ffcdea1 100644
@@ -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 && !strncasecmp(path1, path2, 1)) ||
+              (!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]))
t/t0060-path-utils.sh +4 -1
index 76c7792..c3c3b33 100755
@@ -208,6 +208,10 @@ relative_path a/b/ a/b             ./
 relative_path a                a/b             ../
 relative_path x/y      a/b             ../../x/y
 relative_path a/c      a/b             ../c
+relative_path a/b      /x/y            a/b
+relative_path /a/b     x/y             /a/b    POSIX
+relative_path d:/a/b   D:/a/c          ../b    MINGW
+relative_path C:/a/b   D:/a/c          C:/a/b  MINGW
 relative_path a/b      "<empty>"       a/b
 relative_path a/b      "<null>"        a/b
 relative_path "<empty>"        /a/b            ./
--

1.8.3.rc2.14.g5ac1b82

#49 lore raw

v2 cover fixes for relative_path

In reply to Junio C Hamano

Updates since v1:

* New patch 1/3 on t0060, which use umambigous leading path (/foo).
* Call tolower instead of strncasecmp in patch 2/3.
* Rename simple_relative_path to remove_leading_path in patch 3/3.

Jiang Xin (3):
test: use unambigous leading path (/foo) for mingw relative_path should honor dos_drive_prefix Use simpler relative_path when set_git_dir

 cache.h               |  1 +
 path.c                | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++
 setup.c               |  5 +---
 t/t0060-path-utils.sh | 60 +++++++++++++++++++++++++----------------------
 4 files changed, 99 insertions(+), 32 deletions(-)
Signature · 1 line
1.8.4.459.gd80d422
#50 lore raw

PATCH v2 1/3 test: use unambigous leading path (/foo) for mingw

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such doc drive on MINGW platform. Use an umambigous leading path "/foo" instead.

---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
t/t0060-path-utils.sh +28 -28
index 3a48de2..82a6f21 100755
@@ -190,33 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
 	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
-relative_path /a/b/c/	/a/b/		c/
-relative_path /a/b/c/	/a/b		c/
-relative_path /a//b//c/	//a/b//		c/	POSIX
-relative_path /a/b	/a/b		./
-relative_path /a/b/	/a/b		./
-relative_path /a	/a/b		../
-relative_path /		/a/b/		../../
-relative_path /a/c	/a/b/		../c
-relative_path /a/c	/a/b		../c
-relative_path /x/y	/a/b/		../../x/y
-relative_path /a/b	"<empty>"	/a/b
-relative_path /a/b 	"<null>"	/a/b
-relative_path a/b/c/	a/b/		c/
-relative_path a/b/c/	a/b		c/
-relative_path a/b//c	a//b		c
-relative_path a/b/	a/b/		./
-relative_path a/b/	a/b		./
-relative_path a		a/b		../
-relative_path x/y	a/b		../../x/y
-relative_path a/c	a/b		../c
-relative_path a/b	"<empty>"	a/b
-relative_path a/b 	"<null>"	a/b
-relative_path "<empty>"	/a/b		./
-relative_path "<empty>"	"<empty>"	./
-relative_path "<empty>"	"<null>"	./
-relative_path "<null>"	"<empty>"	./
-relative_path "<null>"	"<null>"	./
-relative_path "<null>"	/a/b		./
+relative_path /foo/a/b/c/	/foo/a/b/	c/
+relative_path /foo/a/b/c/	/foo/a/b	c/
+relative_path /foo/a//b//c/	//foo/a/b//	c/		POSIX
+relative_path /foo/a/b		/foo/a/b	./
+relative_path /foo/a/b/		/foo/a/b	./
+relative_path /foo/a		/foo/a/b	../
+relative_path /			/foo/a/b/	../../../
+relative_path /foo/a/c		/foo/a/b/	../c
+relative_path /foo/a/c		/foo/a/b	../c
+relative_path /foo/x/y		/foo/a/b/	../../x/y
+relative_path /foo/a/b		"<empty>"	/foo/a/b
+relative_path /foo/a/b 		"<null>"	/foo/a/b
+relative_path foo/a/b/c/	foo/a/b/	c/
+relative_path foo/a/b/c/	foo/a/b		c/
+relative_path foo/a/b//c	foo/a//b	c
+relative_path foo/a/b/		foo/a/b/	./
+relative_path foo/a/b/		foo/a/b		./
+relative_path foo/a		foo/a/b		../
+relative_path foo/x/y		foo/a/b		../../x/y
+relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		"<empty>"	foo/a/b
+relative_path foo/a/b 		"<null>"	foo/a/b
+relative_path "<empty>"		/foo/a/b	./
+relative_path "<empty>"		"<empty>"	./
+relative_path "<empty>"		"<null>"	./
+relative_path "<null>"		"<empty>"	./
+relative_path "<null>"		"<null>"	./
+relative_path "<null>"		/foo/a/b	./
 
 test_done
Signature · 1 line
1.8.4.459.gd80d422
#51 lore raw

Re: [PATCH v2 1/3] test: use unambigous leading path (/foo) for mingw

Quoted 4 lines · Jiang Xin

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such doc drive on MINGW platform. Use an umambigous leading path "/foo" instead.

"DOS drive", you mean?

Are they really spelled as /a or /x (not e.g. //a or something)?

Just double-checking.

Quoted 71 lines
---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
t/t0060-path-utils.sh +28 -28
index 3a48de2..82a6f21 100755
@@ -190,33 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
 	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
-relative_path /a/b/c/	/a/b/		c/
-relative_path /a/b/c/	/a/b		c/
-relative_path /a//b//c/	//a/b//		c/	POSIX
-relative_path /a/b	/a/b		./
-relative_path /a/b/	/a/b		./
-relative_path /a	/a/b		../
-relative_path /		/a/b/		../../
-relative_path /a/c	/a/b/		../c
-relative_path /a/c	/a/b		../c
-relative_path /x/y	/a/b/		../../x/y
-relative_path /a/b	"<empty>"	/a/b
-relative_path /a/b 	"<null>"	/a/b
-relative_path a/b/c/	a/b/		c/
-relative_path a/b/c/	a/b		c/
-relative_path a/b//c	a//b		c
-relative_path a/b/	a/b/		./
-relative_path a/b/	a/b		./
-relative_path a		a/b		../
-relative_path x/y	a/b		../../x/y
-relative_path a/c	a/b		../c
-relative_path a/b	"<empty>"	a/b
-relative_path a/b 	"<null>"	a/b
-relative_path "<empty>"	/a/b		./
-relative_path "<empty>"	"<empty>"	./
-relative_path "<empty>"	"<null>"	./
-relative_path "<null>"	"<empty>"	./
-relative_path "<null>"	"<null>"	./
-relative_path "<null>"	/a/b		./
+relative_path /foo/a/b/c/	/foo/a/b/	c/
+relative_path /foo/a/b/c/	/foo/a/b	c/
+relative_path /foo/a//b//c/	//foo/a/b//	c/		POSIX
+relative_path /foo/a/b		/foo/a/b	./
+relative_path /foo/a/b/		/foo/a/b	./
+relative_path /foo/a		/foo/a/b	../
+relative_path /			/foo/a/b/	../../../
+relative_path /foo/a/c		/foo/a/b/	../c
+relative_path /foo/a/c		/foo/a/b	../c
+relative_path /foo/x/y		/foo/a/b/	../../x/y
+relative_path /foo/a/b		"<empty>"	/foo/a/b
+relative_path /foo/a/b 		"<null>"	/foo/a/b
+relative_path foo/a/b/c/	foo/a/b/	c/
+relative_path foo/a/b/c/	foo/a/b		c/
+relative_path foo/a/b//c	foo/a//b	c
+relative_path foo/a/b/		foo/a/b/	./
+relative_path foo/a/b/		foo/a/b		./
+relative_path foo/a		foo/a/b		../
+relative_path foo/x/y		foo/a/b		../../x/y
+relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		"<empty>"	foo/a/b
+relative_path foo/a/b 		"<null>"	foo/a/b
+relative_path "<empty>"		/foo/a/b	./
+relative_path "<empty>"		"<empty>"	./
+relative_path "<empty>"		"<null>"	./
+relative_path "<null>"		"<empty>"	./
+relative_path "<null>"		"<null>"	./
+relative_path "<null>"		/foo/a/b	./
 
 test_done
#52 lore raw

Re: [PATCH v2 1/3] test: use unambigous leading path (/foo) for mingw

Quoted 7 lines · Junio C Hamano
Quoted 4 lines · Jiang Xin

In test cases for relative_path, path with one leading character (such as /a, /x) may be recogonized as "a:/" or "x:/" if there is such doc drive on MINGW platform. Use an umambigous leading path "/foo" instead.

Quoted 5 lines

"DOS drive", you mean?

Are they really spelled as /a or /x (not e.g. //a or something)?

Just double-checking.

Yes,
there is a directoctory structure in / like this:

/usr
/bin
/lib
Then we have the drive letters mapped to single letters:
/c/Documents and Settings
/c/temp

As an alternative
c:/temp can be used
or the DOS style
"c:\temp"

And the // or "\\" is used for the UNC names (Universal Name Convention) //Servername/ShareName/Directory

/Torsten

#53 lore raw

Re: [PATCH v2 1/3] test: use unambigous leading path (/foo) for mingw

Quoted 9 lines · Torsten Bögershausen

Yes,
there is a directoctory structure in / like this:

/usr
/bin
/lib
Then we have the drive letters mapped to single letters:
/c/Documents and Settings
/c/temp

Thanks.

#54 lore raw

PATCH v2 2/3 relative_path should honor dos_drive_prefix

In reply to Jiang Xin

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have dos driver prefix. ($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.

---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
path.c +20 -0
index 9fd28bcd..65d376d 100644
@@ -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]))
t/t0060-path-utils.sh +4 -0
index 82a6f21..0187d11 100755
@@ -210,6 +210,10 @@ relative_path foo/a/b/		foo/a/b		./
 relative_path foo/a		foo/a/b		../
 relative_path foo/x/y		foo/a/b		../../x/y
 relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		/foo/x/y	foo/a/b
+relative_path /foo/a/b		foo/x/y		/foo/a/b
+relative_path d:/a/b		D:/a/c		../b		MINGW
+relative_path C:/a/b		D:/a/c		C:/a/b		MINGW
 relative_path foo/a/b		"<empty>"	foo/a/b
 relative_path foo/a/b 		"<null>"	foo/a/b
 relative_path "<empty>"		/foo/a/b	./
Signature · 1 line
1.8.4.459.gd80d422
#55 lore raw

Re: [PATCH v2 2/3] relative_path should honor dos_drive_prefix

Quoted 26 lines · Jiang Xin

Tvangeste found that the "relative_path" function could not work properly on Windows if "in" and "prefix" have dos driver prefix. ($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.

---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
path.c +0 -0
index 9fd28bcd..65d376d 100644
@@ -434,6 +434,16 @@ int adjust_shared_perm(const char *path)
 	return 0;
 }
 

path.c int adjust_shared_perm(const char *path) · line 446

Quoted 10 lines
path.c +10 -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);
+}
+

I think the name of the fuction is somewhat misleading, as we are not sure if they really have the same root.
And that is investigated further down.

may_have_same_root() could be a better name.

[snip]

Quoted 8 lines
path.c +0 -0
 	while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
 		if (is_dir_sep(prefix[i])) {
 			while (is_dir_sep(prefix[i]))
t/t0060-path-utils.sh +0 -0
index 82a6f21..0187d11 100755

t/t0060-path-utils.sh relative_path foo/a/b/ foo/a/b ./ · line 216

Quoted 7 lines
t/t0060-path-utils.sh +4 -0
@@ -210,6 +210,10 @@ relative_path foo/a/b/		foo/a/b		./
 relative_path foo/a		foo/a/b		../
 relative_path foo/x/y		foo/a/b		../../x/y
 relative_path foo/a/c		foo/a/b		../c
+relative_path foo/a/b		/foo/x/y	foo/a/b
+relative_path /foo/a/b		foo/x/y		/foo/a/b
+relative_path d:/a/b		D:/a/c		../b		MINGW
+relative_path C:/a/b		D:/a/c		C:/a/b		MINGW

Side question:
What happens if we feed in a relative path with a dos drive? like "c:foo" which is different from c:/foo.

#56 lore raw

PATCH v2 3/3 Use simpler relative_path when set_git_dir

In reply to Jiang Xin

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.

---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
cache.h +1 -0
index a47b9c0..73fa334 100644
@@ -747,6 +747,7 @@ int is_directory(const char *);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
+const char *remove_leading_path(const char *in, const char *prefix);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
 int normalize_path_copy(char *dst, const char *src);
path.c +45 -0

51 lines not on this page · raw message

setup.c +1 -4
index f08dd64..dbf4138 100644
@@ -227,7 +227,6 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
-	struct strbuf sb = STRBUF_INIT;
 	const char *work_tree, *git_dir;
 	static int initialized = 0;
 
@@ -247,10 +246,8 @@ void setup_work_tree(void)
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-	set_git_dir(relative_path(git_dir, work_tree, &sb));
+	set_git_dir(remove_leading_path(git_dir, work_tree));
 	initialized = 1;
-
-	strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
Signature · 1 line
1.8.4.459.gd80d422