threads / discuss / 34897

Regression in e02ca72: git svn rebase is broken on Windows

Subject: Regression in e02ca72: git svn rebase is broken on Windows

## tl;dr

48 messages between Sep 10, 2013 and Oct 14, 2013.

replies: 47people: 9as markdown or json

Tvangeste· Sep 10, 2013, 13:14 UTC · lore
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
Johannes Schindelin· Sep 10, 2013, 16:13 UTC · re: Tvangeste · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

Hi Tvangeste,
On Tue, 10 Sep 2013, Tvangeste wrote:
> 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

Tvangeste· Sep 10, 2013, 17:43 UTC · re: Johannes Schindelin · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

10.09.2013, 18:13, "Johannes Schindelin" <Johannes.Schindelin@gmx.de>:
> 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
Johannes Schindelin· Sep 10, 2013, 18:02 UTC · re: Tvangeste · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

Hi Tvangeste,
On Tue, 10 Sep 2013, Tvangeste wrote:
> 10.09.2013, 18:13, "Johannes Schindelin" <Johannes.Schindelin@gmx.de>:
> > 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.

> 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

-- 
-- 
*** 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 msysgit@googlegroups.com
To unsubscribe from this group, send email to
msysgit+unsubscribe@googlegroups.com
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 msysgit+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Tvangeste· Sep 10, 2013, 20:53 UTC · re: Johannes Schindelin · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

10.09.2013, 20:02, "Johannes Schindelin" <Johannes.Schindelin@gmx.de>:
>  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
Johannes Sixt· Sep 10, 2013, 16:52 UTC · re: Tvangeste · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

Am 10.09.2013 15:14, schrieb Tvangeste:
Show 13 quoted 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
Tvangeste· Sep 10, 2013, 17:44 UTC · re: Johannes Sixt · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

10.09.2013, 18:53, "Johannes Sixt" <j6t@kdbg.org>:
> 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
Junio C Hamano· Sep 10, 2013, 17:06 UTC · re: Tvangeste · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

Tvangeste <i.4m.l33t@yandex.ru> writes:
Show 27 quoted lines
> 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

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?
Karsten Blees· Sep 10, 2013, 22:17 UTC · re: Junio C Hamano · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

Junio C Hamano <gitster <at> pobox.com> writes:
Show 6 quoted 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
Show 7 quoted lines
> 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
Show 7 quoted 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):
Show 20 quoted 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
> 
> 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...

Jiang Xin· Sep 11, 2013, 04:41 UTC · re: Karsten Blees · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

2013/9/11 Karsten Blees <karsten.blees@gmail.com>:
Show 19 quoted 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?
>>
>
> 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.
> 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.
> 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.

-- 
Jiang Xin
Jiang Xin· Sep 11, 2013, 03:19 UTC · re: Junio C Hamano · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

2013/9/11 Junio C Hamano <gitster@pobox.com>:
Show 37 quoted lines
> Tvangeste <i.4m.l33t@yandex.ru> writes:
>
>> 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
>
> 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
diff --git a/path.c b/path.c
index 7f3324a..51f5d28 100644
--- a/path.c
+++ b/path.c
@@ -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'?
-- 
Jiang Xin
Johannes Sixt· Sep 11, 2013, 05:43 UTC · re: Jiang Xin · lore

Re: Regression in e02ca72: git svn rebase is broken on Windows

Am 11.09.2013 05:19, schrieb Jiang Xin:
Show 56 quoted 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
> 
> 
> diff --git a/path.c b/path.c
> index 7f3324a..51f5d28 100644
> --- a/path.c
> +++ b/path.c
> @@ -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) != ':'));
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.
> +}
-- Hannes
-- 
-- 
*** 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 msysgit@googlegroups.com
To unsubscribe from this group, send email to
msysgit+unsubscribe@googlegroups.com
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 msysgit+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Jiang Xin· Sep 12, 2013, 09:12 UTC · re: Junio C Hamano · lore

[PATCH 1/2] relative_path should honor dos_drive_prefix

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

Reported-by: Tvangeste <i.4m.l33t@yandex.ru>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
diff --git a/path.c b/path.c
index 7f3324a..ffcdea1 100644
--- a/path.c
+++ b/path.c
@@ -441,6 +441,16 @@ int adjust_shared_perm(const char *path)
 	return 0;
 }
 
+static int have_same_root(const char *path1, const char *path2)
+{
+	int is_abs1, is_abs2;
+
+	is_abs1 = is_absolute_path(path1);
+	is_abs2 = is_absolute_path(path2);
+	return (is_abs1 && is_abs2 && !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]))
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 76c7792..c3c3b33 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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
Jiang Xin· Sep 12, 2013, 09:12 UTC · re: Jiang Xin · lore

[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.

Suggested-by: Karsten Blees <karsten.blees@gmail.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/cache.h b/cache.h
index 8e42256..ab17f1d 100644
--- a/cache.h
+++ b/cache.h
@@ -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);
diff --git a/path.c b/path.c
index ffcdea1..0f0c92f 100644
--- a/path.c
+++ b/path.c
@@ -558,6 +558,51 @@ const char *relative_path(const char *in, const char *prefix,
 }
 
 /*
+ * A simpler implementation of relative_path
+ *
+ * Get relative path by removing "prefix" from "in". This function
+ * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
+ * to increase performance when traversing the path to work_tree.
+ */
+const char *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;
+}
+
+/*
  * It is okay if dst == src, but they should not overlap otherwise.
  *
  * Performs the following normalizations on src, storing the result in dst:
diff --git a/setup.c b/setup.c
index 0d9ea62..f4be6ff 100644
--- a/setup.c
+++ b/setup.c
@@ -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)
-- 
1.8.3.rc2.14.g5ac1b82
Johannes Sixt· Sep 12, 2013, 17:36 UTC · re: Jiang Xin · lore

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

Am 12.09.2013 11:12, schrieb Jiang Xin:
Show 41 quoted 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.
> 
> Suggested-by: Karsten Blees <karsten.blees@gmail.com>
> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
> ---
>  cache.h |  1 +
>  path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  setup.c |  5 +----
>  3 files changed, 47 insertions(+), 4 deletions(-)
> 
> diff --git a/cache.h b/cache.h
> index 8e42256..ab17f1d 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -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);
> diff --git a/path.c b/path.c
> index ffcdea1..0f0c92f 100644
> --- a/path.c
> +++ b/path.c
> @@ -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.

Show 43 quoted lines
> + *
> + * 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;
> +}
> ...
Tvangeste· Sep 12, 2013, 09:32 UTC · re: Jiang Xin · lore

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

Thank you, this fixes the problem with `git svn rebase` on Windows for me.
--Tvangeste
On Thu, Sep 12, 2013 at 11:12 AM, Jiang Xin <worldhello.net@gmail.com> wrote:
Show 74 quoted 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.
>
> Reported-by: Tvangeste <i.4m.l33t@yandex.ru>
> Helped-by: Johannes Sixt <j6t@kdbg.org>
> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
> ---
>  path.c                | 20 ++++++++++++++++++++
>  t/t0060-path-utils.sh |  4 ++++
>  2 files changed, 24 insertions(+)
>
> diff --git a/path.c b/path.c
> index 7f3324a..ffcdea1 100644
> --- a/path.c
> +++ b/path.c
> @@ -441,6 +441,16 @@ int adjust_shared_perm(const char *path)
>         return 0;
>  }
>
> +static int have_same_root(const char *path1, const char *path2)
> +{
> +       int is_abs1, is_abs2;
> +
> +       is_abs1 = is_absolute_path(path1);
> +       is_abs2 = is_absolute_path(path2);
> +       return (is_abs1 && is_abs2 && !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]))
> diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
> index 76c7792..c3c3b33 100755
> --- a/t/t0060-path-utils.sh
> +++ b/t/t0060-path-utils.sh
> @@ -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
>
Torsten Bögershausen· Sep 12, 2013, 14:13 UTC · re: Jiang Xin · lore

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

On 2013-09-12 11.12, Jiang Xin wrote:
Show 33 quoted 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.
> 
> Reported-by: Tvangeste <i.4m.l33t@yandex.ru>
> Helped-by: Johannes Sixt <j6t@kdbg.org>
> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
> ---
>  path.c                | 20 ++++++++++++++++++++
>  t/t0060-path-utils.sh |  4 ++++
>  2 files changed, 24 insertions(+)
> 
> diff --git a/path.c b/path.c
> index 7f3324a..ffcdea1 100644
> --- a/path.c
> +++ b/path.c
> @@ -441,6 +441,16 @@ int adjust_shared_perm(const char *path)
>  	return 0;
>  }
>  
> +static int have_same_root(const char *path1, const char *path2)
> +{
> +	int is_abs1, is_abs2;
> +
> +	is_abs1 = is_absolute_path(path1);
> +	is_abs2 = is_absolute_path(path2);
> +	return (is_abs1 && is_abs2 && !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
Show 39 quoted lines
> +	       (!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]))
> diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
> index 76c7792..c3c3b33 100755
> --- a/t/t0060-path-utils.sh
> +++ b/t/t0060-path-utils.sh
> @@ -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		./
> 
Junio C Hamano· Sep 12, 2013, 15:48 UTC · re: Torsten Bögershausen · lore

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

Torsten Bögershausen <tboegi@web.de> writes:
Show 9 quoted lines
>> +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.
Show 5 quoted 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);
> }
Johannes Sixt· Sep 12, 2013, 17:22 UTC · re: Torsten Bögershausen · lore

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

Am 12.09.2013 16:13, schrieb Torsten Bögershausen:
Show 10 quoted lines
> On 2013-09-12 11.12, Jiang Xin wrote:
>> +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.

Show 5 quoted 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
Junio C Hamano· Sep 12, 2013, 19:11 UTC · re: Johannes Sixt · lore

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

Johannes Sixt <j6t@kdbg.org> writes:
Show 16 quoted lines
> Am 12.09.2013 16:13, schrieb Torsten Bögershausen:
>> On 2013-09-12 11.12, Jiang Xin wrote:
>>> +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.
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.
Jiang Xin· Sep 13, 2013, 04:55 UTC · re: Junio C Hamano · lore

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

2013/9/13 Junio C Hamano <gitster@pobox.com>:
Show 14 quoted 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;
>
>  - //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.

-- 
Jiang Xin
Junio C Hamano· Sep 13, 2013, 05:53 UTC · re: Jiang Xin · lore

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

Jiang Xin <worldhello.net@gmail.com> writes:
Show 16 quoted lines
> 2013/9/13 Junio C Hamano <gitster@pobox.com>:
>>
>> 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.
>
> 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.

Jiang Xin· Sep 17, 2013, 08:24 UTC · re: Junio C Hamano · lore

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

2013/9/13 Junio C Hamano <gitster@pobox.com>:
Show 44 quoted lines
> Jiang Xin <worldhello.net@gmail.com> writes:
>
>> 2013/9/13 Junio C Hamano <gitster@pobox.com>:
>>>
>>> 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.
>>
>> 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.
>
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;
+       }
+}
-- 
Jiang Xin
Jiang Xin· Sep 17, 2013, 08:30 UTC · re: Jiang Xin · lore

[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.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 3a48de2..92976e0 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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
-- 
1.8.4.460.g2f083d1
Jiang Xin· Sep 17, 2013, 08:30 UTC · re: Jiang Xin · lore

[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.

Reported-by: Tvangeste <i.4m.l33t@yandex.ru>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 compat/mingw.h        |  9 +++++++++
 git-compat-util.h     |  4 ++++
 path.c                | 25 +++++++++++++++++++++++++
 t/t0060-path-utils.sh |  8 ++++++++
 4 files changed, 46 insertions(+)
diff --git a/compat/mingw.h b/compat/mingw.h
index bd0a88b..06e9f49 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -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;
diff --git a/git-compat-util.h b/git-compat-util.h
index 9549de6..93c2206 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -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
diff --git a/path.c b/path.c
index 9fd28bcd..544d10d 100644
--- a/path.c
+++ b/path.c
@@ -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]))
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 92976e0..830b6d5 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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	./
-- 
1.8.4.460.g2f083d1
Junio C Hamano· Sep 17, 2013, 16:12 UTC · re: Jiang Xin · lore

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

Jiang Xin <worldhello.net@gmail.com> writes:
Show 12 quoted lines
> diff --git a/compat/mingw.h b/compat/mingw.h
> index bd0a88b..06e9f49 100644
> --- a/compat/mingw.h
> +++ b/compat/mingw.h
> @@ -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?

Jiang Xin· Sep 18, 2013, 09:02 UTC · re: Junio C Hamano · lore

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

2013/9/18 Junio C Hamano <gitster@pobox.com>:
Show 14 quoted lines
> Jiang Xin <worldhello.net@gmail.com> writes:
>
>> diff --git a/compat/mingw.h b/compat/mingw.h
>> index bd0a88b..06e9f49 100644
>> --- a/compat/mingw.h
>> +++ b/compat/mingw.h
>> @@ -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;
A UNC path must start with two slashes, but not three or more slashes.
>
> 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 ?

-- 
Jiang Xin
Junio C Hamano· Sep 18, 2013, 16:00 UTC · re: Jiang Xin · lore

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

Jiang Xin <worldhello.net@gmail.com> writes:
Show 9 quoted lines
> 2013/9/18 Junio C Hamano <gitster@pobox.com>:
>>> +     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.
Jiang Xin· Sep 17, 2013, 08:30 UTC · re: Jiang Xin · lore

[PATCH v3 3/3] Use simpler relative_path when set_git_dir

Using a relative_path as git_dir first appears in v1.5.6-1-g044bbbc. It will make git_dir shorter only if git_dir is inside work_tree, and this will increase performance. But my last refactor effort on relative_path function (commit v1.8.3-rc2-12-ge02ca72) changed that. Always use relative_path as git_dir may bring troubles like $gmane/234434.

Because new relative_path is a combination of original relative_path from path.c and original path_relative from quote.c, so in order to restore the origin implementation, save the original relative_path as remove_leading_path, and call it in setup.c.

Suggested-by: Karsten Blees <karsten.blees@gmail.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/cache.h b/cache.h
index a47b9c0..73fa334 100644
--- a/cache.h
+++ b/cache.h
@@ -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);
diff --git a/path.c b/path.c
index 544d10d..5ad3554 100644
--- a/path.c
+++ b/path.c
@@ -556,6 +556,51 @@ const char *relative_path(const char *in, const char *prefix,
 }
 
 /*
+ * A simpler implementation of relative_path
+ *
+ * Get relative path by removing "prefix" from "in". This function
+ * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
+ * to increase performance when traversing the path to work_tree.
+ */
+const char *remove_leading_path(const char *in, const char *prefix)
+{
+	static char buf[PATH_MAX + 1];
+	int i = 0, j = 0;
+
+	if (!prefix || !prefix[0])
+		return in;
+	while (prefix[i]) {
+		if (is_dir_sep(prefix[i])) {
+			if (!is_dir_sep(in[j]))
+				return in;
+			while (is_dir_sep(prefix[i]))
+				i++;
+			while (is_dir_sep(in[j]))
+				j++;
+			continue;
+		} else if (in[j] != prefix[i]) {
+			return in;
+		}
+		i++;
+		j++;
+	}
+	if (
+	    /* "/foo" is a prefix of "/foo" */
+	    in[j] &&
+	    /* "/foo" is not a prefix of "/foobar" */
+	    !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j])
+	   )
+		return in;
+	while (is_dir_sep(in[j]))
+		j++;
+	if (!in[j])
+		strcpy(buf, ".");
+	else
+		strcpy(buf, in + j);
+	return buf;
+}
+
+/*
  * It is okay if dst == src, but they should not overlap otherwise.
  *
  * Performs the following normalizations on src, storing the result in dst:
diff --git a/setup.c b/setup.c
index f08dd64..dbf4138 100644
--- a/setup.c
+++ b/setup.c
@@ -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)
-- 
1.8.4.460.g2f083d1
Johannes Sixt· Sep 17, 2013, 19:32 UTC · re: Jiang Xin · lore

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

Am 17.09.2013 10:24, schrieb Jiang Xin:
Show 16 quoted 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:
> 
>     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
Torsten Bögershausen· Sep 18, 2013, 14:29 UTC · re: Johannes Sixt · lore

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

On 2013-09-17 21.32, Johannes Sixt wrote:
Show 50 quoted lines
> Am 17.09.2013 10:24, schrieb Jiang Xin:
>> 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.
> 
> 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

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

Jiang Xin· Sep 20, 2013, 01:26 UTC · re: Johannes Sixt · lore

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

2013/9/18 Johannes Sixt <j6t@kdbg.org>:
Show 35 quoted lines
> Am 17.09.2013 10:24, schrieb Jiang Xin:
>> 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.
>
> 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
> ...
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).

Show 6 quoted 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.

-- 
Jiang Xin
Jiang Xin· Sep 20, 2013, 02:38 UTC · re: Jiang Xin · lore

[PATCH v4 0/3] 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(-)
-- 
1.8.4.460.gbed9cb4
Jiang Xin· Sep 20, 2013, 02:38 UTC · re: Jiang Xin · lore

[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.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 3a48de2..92976e0 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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
-- 
1.8.4.460.gbed9cb4
Sebastian Schuberth· Oct 10, 2013, 20:32 UTC · re: Jiang Xin · lore

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

On 20.09.2013 04:38, Jiang Xin wrote:
> 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".

> -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
-- 
Sebastian Schuberth
Jiang Xin· Oct 14, 2013, 01:33 UTC · re: Sebastian Schuberth · lore

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

2013/10/11 Sebastian Schuberth <sschuberth@gmail.com>:
Show 12 quoted 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.
>>
>> 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
Show 12 quoted lines
>> -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, ...

-- 
Jiang Xin
Jiang Xin· Oct 14, 2013, 02:29 UTC · re: Sebastian Schuberth · lore

[PATCH v5 0/3] relative path regression fix

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(-)
-- 
1.8.4
Jiang Xin· Oct 14, 2013, 02:29 UTC · re: Jiang Xin · lore

[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.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 3a48de2..92976e0 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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
-- 
1.8.4
Sebastian Schuberth· Oct 14, 2013, 06:50 UTC · re: Jiang Xin · lore

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

On Mon, Oct 14, 2013 at 4:29 AM, Jiang Xin <worldhello.net@gmail.com> wrote:
Show 13 quoted 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 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.
>
> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  t/t0060-path-utils.sh | 56 +++++++++++++++++++++++++--------------------------
>  1 file changed, 28 insertions(+), 28 deletions(-)
Thanks, ack.
-- 
Sebastian Schuberth
Eric Sunshine· Oct 14, 2013, 19:40 UTC · re: Jiang Xin · lore

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

On Sun, Oct 13, 2013 at 10:29 PM, Jiang Xin <worldhello.net@gmail.com> wrote:
Show 7 quoted 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 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/
> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jiang Xin· Oct 14, 2013, 02:29 UTC · re: Jiang Xin · lore

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

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

E.g., When execute: test-path-utils relative_path "C:/a/b" "D:/x/y", should return "C:/a/b", but returns "../../C:/a/b", which is wrong.

So make relative_path honor DOS drive prefix, and add test cases for it in t0060.

Reported-by: Tvangeste <i.4m.l33t@yandex.ru>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
diff --git a/path.c b/path.c
index 7f3324a..0c16dc5 100644
--- a/path.c
+++ b/path.c
@@ -441,6 +441,16 @@ int adjust_shared_perm(const char *path)
 	return 0;
 }
 
+static int have_same_root(const char *path1, const char *path2)
+{
+	int is_abs1, is_abs2;
+
+	is_abs1 = is_absolute_path(path1);
+	is_abs2 = is_absolute_path(path2);
+	return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) ||
+	       (!is_abs1 && !is_abs2);
+}
+
 /*
  * Give path as relative to prefix.
  *
@@ -461,6 +471,16 @@ const char *relative_path(const char *in, const char *prefix,
 	else if (!prefix_len)
 		return in;
 
+	if (have_same_root(in, prefix)) {
+		/* bypass dos_drive, for "c:" is identical to "C:" */
+		if (has_dos_drive_prefix(in)) {
+			i = 2;
+			j = 2;
+		}
+	} else {
+		return in;
+	}
+
 	while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
 		if (is_dir_sep(prefix[i])) {
 			while (is_dir_sep(prefix[i]))
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 92976e0..40dfa2d 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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	./
-- 
1.8.4
Jiang Xin· Oct 14, 2013, 02:29 UTC · re: Jiang Xin · lore

[PATCH v5 3/3] Use simpler relative_path when set_git_dir

Using a relative_path as git_dir first appears in v1.5.6-1-g044bbbc. It will make git_dir shorter only if git_dir is inside work_tree, and this will increase performance. But my last refactor effort on relative_path function (commit v1.8.3-rc2-12-ge02ca72) changed that. Always use relative_path as git_dir may bring troubles like $gmane/234434.

Because new relative_path is a combination of original relative_path from path.c and original path_relative from quote.c, so in order to restore the origin implementation, save the original relative_path as remove_leading_path, and call it in setup.c.

Suggested-by: Karsten Blees <karsten.blees@gmail.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/cache.h b/cache.h
index 8e42256..94475bd 100644
--- a/cache.h
+++ b/cache.h
@@ -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);
diff --git a/path.c b/path.c
index 0c16dc5..fa62da5 100644
--- a/path.c
+++ b/path.c
@@ -558,6 +558,51 @@ const char *relative_path(const char *in, const char *prefix,
 }
 
 /*
+ * A simpler implementation of relative_path
+ *
+ * Get relative path by removing "prefix" from "in". This function
+ * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
+ * to increase performance when traversing the path to work_tree.
+ */
+const char *remove_leading_path(const char *in, const char *prefix)
+{
+	static char buf[PATH_MAX + 1];
+	int i = 0, j = 0;
+
+	if (!prefix || !prefix[0])
+		return in;
+	while (prefix[i]) {
+		if (is_dir_sep(prefix[i])) {
+			if (!is_dir_sep(in[j]))
+				return in;
+			while (is_dir_sep(prefix[i]))
+				i++;
+			while (is_dir_sep(in[j]))
+				j++;
+			continue;
+		} else if (in[j] != prefix[i]) {
+			return in;
+		}
+		i++;
+		j++;
+	}
+	if (
+	    /* "/foo" is a prefix of "/foo" */
+	    in[j] &&
+	    /* "/foo" is not a prefix of "/foobar" */
+	    !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j])
+	   )
+		return in;
+	while (is_dir_sep(in[j]))
+		j++;
+	if (!in[j])
+		strcpy(buf, ".");
+	else
+		strcpy(buf, in + j);
+	return buf;
+}
+
+/*
  * It is okay if dst == src, but they should not overlap otherwise.
  *
  * Performs the following normalizations on src, storing the result in dst:
diff --git a/setup.c b/setup.c
index 0d9ea62..dad39c1 100644
--- a/setup.c
+++ b/setup.c
@@ -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)
-- 
1.8.4
Jiang Xin· Sep 20, 2013, 02:38 UTC · re: Jiang Xin · lore

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

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.

Reported-by: Tvangeste <i.4m.l33t@yandex.ru>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 path.c                | 20 ++++++++++++++++++++
 t/t0060-path-utils.sh |  4 ++++
 2 files changed, 24 insertions(+)
diff --git a/path.c b/path.c
index 7f3324a..0c16dc5 100644
--- a/path.c
+++ b/path.c
@@ -441,6 +441,16 @@ int adjust_shared_perm(const char *path)
 	return 0;
 }
 
+static int have_same_root(const char *path1, const char *path2)
+{
+	int is_abs1, is_abs2;
+
+	is_abs1 = is_absolute_path(path1);
+	is_abs2 = is_absolute_path(path2);
+	return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) ||
+	       (!is_abs1 && !is_abs2);
+}
+
 /*
  * Give path as relative to prefix.
  *
@@ -461,6 +471,16 @@ const char *relative_path(const char *in, const char *prefix,
 	else if (!prefix_len)
 		return in;
 
+	if (have_same_root(in, prefix)) {
+		/* bypass dos_drive, for "c:" is identical to "C:" */
+		if (has_dos_drive_prefix(in)) {
+			i = 2;
+			j = 2;
+		}
+	} else {
+		return in;
+	}
+
 	while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
 		if (is_dir_sep(prefix[i])) {
 			while (is_dir_sep(prefix[i]))
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 92976e0..40dfa2d 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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	./
-- 
1.8.4.460.gbed9cb4
Sebastian Schuberth· Oct 10, 2013, 20:34 UTC · re: Jiang Xin · lore

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

On 20.09.2013 04:38, Jiang Xin wrote:
> 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/
Show 5 quoted 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/
-- 
Sebastian Schuberth
Jiang Xin· Sep 20, 2013, 02:38 UTC · re: Jiang Xin · lore

[PATCH v4 3/3] Use simpler relative_path when set_git_dir

Using a relative_path as git_dir first appears in v1.5.6-1-g044bbbc. It will make git_dir shorter only if git_dir is inside work_tree, and this will increase performance. But my last refactor effort on relative_path function (commit v1.8.3-rc2-12-ge02ca72) changed that. Always use relative_path as git_dir may bring troubles like $gmane/234434.

Because new relative_path is a combination of original relative_path from path.c and original path_relative from quote.c, so in order to restore the origin implementation, save the original relative_path as remove_leading_path, and call it in setup.c.

Suggested-by: Karsten Blees <karsten.blees@gmail.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 cache.h |  1 +
 path.c  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 setup.c |  5 +----
 3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/cache.h b/cache.h
index 8e42256..94475bd 100644
--- a/cache.h
+++ b/cache.h
@@ -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);
diff --git a/path.c b/path.c
index 0c16dc5..fa62da5 100644
--- a/path.c
+++ b/path.c
@@ -558,6 +558,51 @@ const char *relative_path(const char *in, const char *prefix,
 }
 
 /*
+ * A simpler implementation of relative_path
+ *
+ * Get relative path by removing "prefix" from "in". This function
+ * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
+ * to increase performance when traversing the path to work_tree.
+ */
+const char *remove_leading_path(const char *in, const char *prefix)
+{
+	static char buf[PATH_MAX + 1];
+	int i = 0, j = 0;
+
+	if (!prefix || !prefix[0])
+		return in;
+	while (prefix[i]) {
+		if (is_dir_sep(prefix[i])) {
+			if (!is_dir_sep(in[j]))
+				return in;
+			while (is_dir_sep(prefix[i]))
+				i++;
+			while (is_dir_sep(in[j]))
+				j++;
+			continue;
+		} else if (in[j] != prefix[i]) {
+			return in;
+		}
+		i++;
+		j++;
+	}
+	if (
+	    /* "/foo" is a prefix of "/foo" */
+	    in[j] &&
+	    /* "/foo" is not a prefix of "/foobar" */
+	    !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j])
+	   )
+		return in;
+	while (is_dir_sep(in[j]))
+		j++;
+	if (!in[j])
+		strcpy(buf, ".");
+	else
+		strcpy(buf, in + j);
+	return buf;
+}
+
+/*
  * It is okay if dst == src, but they should not overlap otherwise.
  *
  * Performs the following normalizations on src, storing the result in dst:
diff --git a/setup.c b/setup.c
index 0d9ea62..dad39c1 100644
--- a/setup.c
+++ b/setup.c
@@ -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)
-- 
1.8.4.460.gbed9cb4
Torsten Bögershausen· Sep 13, 2013, 13:59 UTC · re: Jiang Xin · lore

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

On 13.09.13 06:55, Jiang Xin wrote:
Show 16 quoted lines
> 2013/9/13 Junio C Hamano <gitster@pobox.com>:
>> 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.
>>
>>
> 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]))
 
Karsten Blees· Sep 12, 2013, 15:45 UTC · re: Jiang Xin · lore

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

Am 12.09.2013 11:12, schrieb Jiang Xin:
Show 10 quoted 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
Torsten Bögershausen· Sep 18, 2013, 15:09 UTC · lore

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

On 2013-09-18 16.37, Torsten Bögershausen wrote:
Show 18 quoted lines
> On 2013-09-17 18.12, Junio C Hamano wrote:
>> Jiang Xin <worldhello.net@gmail.com> writes:
>>
>>> diff --git a/compat/mingw.h b/compat/mingw.h
>>> index bd0a88b..06e9f49 100644
>>> --- a/compat/mingw.h
>>> +++ b/compat/mingw.h
>>> @@ -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?

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?

← back to recent threads