From: Stefan Beller <hidden> Date: 2016-06-15 23:07:25
This reimplements the helper function `resolve_relative_url` in shell
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.
This also improves the performance:
(Best out of 3) time ./t7400-submodule-basic.sh
Before:
real 0m9.575s
user 0m2.683s
sys 0m6.773s
After:
real 0m9.293s
user 0m2.691s
sys 0m6.549s
Signed-off-by: Stefan Beller <redacted>
---
This applies on origin/master, and I'd carry as its own feature branch
as I am nowhere near done with the groups feature after reading Jens feedback.
(It took me a while to identify this as a next best step.)
Thanks,
Stefan
builtin/submodule--helper.c | 120 ++++++++++++++++++++++++++++++++++++++++++++
git-submodule.sh | 81 ++----------------------------
2 files changed, 124 insertions(+), 77 deletions(-)
@@ -9,6 +9,125 @@#include"submodule-config.h"#include"string-list.h"#include"run-command.h"+#include"remote.h"+#include"refs.h"++staticconstchar*get_default_remote(void)+{+char*dest=NULL;+unsignedcharsha1[20];+intflag;+structstrbufsb=STRBUF_INIT;+constchar*refname=resolve_ref_unsafe("HEAD",0,sha1,&flag);++if(!refname)+die("No such ref: HEAD");++refname=shorten_unambiguous_ref(refname,0);+strbuf_addf(&sb,"branch.%s.remote",refname);+if(git_config_get_string(sb.buf,&dest))+return"origin";+else+returnxstrdup(dest);+}++/*+*Thefunctiontakesatmost2arguments.Thefirstargumentisthe+*URLthatnavigatestothesubmoduleoriginrepo.Whenrelative,thisURL+*isrelativetothesuperprojectoriginURLrepo.Thesecondup_path+*argument,ifspecified,istherelativepaththatnavigates+*fromthesubmoduleworkingtreetothesuperprojectworkingtree.+*+*TheoutputofthefunctionistheoriginURLofthesubmodule.+*+*TheoutputwilleitherbeanabsoluteURLorfilesystempath(ifthe+*superprojectoriginURLisanabsoluteURLorfilesystempath,+*respectively)orarelativefilesystempath(ifthesuperproject+*originURLisarelativefilesystempath).+*+*Whentheoutputisarelativefilesystempath,thepathiseither+*relativetothesubmoduleworkingtree,ifup_pathisspecified,orto+*thesuperprojectworkingtreeotherwise.+*/+staticconstchar*relative_url(constchar*url,constchar*up_path)+{+intis_relative=0;+size_tlen;+char*remoteurl=NULL;+char*sep="/";+constchar*out;+structstrbufsb=STRBUF_INIT;+constchar*remote=get_default_remote();+strbuf_addf(&sb,"remote.%s.url",remote);++if(git_config_get_string(sb.buf,&remoteurl))+/* the repository is its own authoritative upstream */+remoteurl=xgetcwd();++if(strip_suffix(remoteurl,"/",&len))+remoteurl[len]='\0';++if(strchr(remoteurl,':')||skip_prefix(remoteurl,"/",&out))+is_relative=0;+elseif(skip_prefix(remoteurl,"./",&out)||+skip_prefix(remoteurl,"../",&out))+is_relative=1;+else{+is_relative=1;+strbuf_reset(&sb);+strbuf_addf(&sb,"./%s",remoteurl);+remoteurl=strbuf_detach(&sb,NULL);+}++while(url){+if(skip_prefix(url,"../",&out)){+char*rfind;+url=out;++rfind=strrchr(remoteurl,'/');+if(rfind)+*rfind='\0';+else{+rfind=strrchr(remoteurl,':');+if(rfind){+*rfind='\0';+sep=":";+}else{+if(is_relative||!strcmp(".",remoteurl))+die(N_("cannot strip one component off url '%s'"),remoteurl);+else+remoteurl=".";+}+}+}elseif(skip_prefix(url,"./",&out))+url=out;+else+break;+}+strbuf_reset(&sb);+strbuf_addf(&sb,"%s%s%s",remoteurl,sep,url);++if(!skip_prefix(sb.buf,"./",&out))+out=sb.buf;+out=xstrdup(out);++strbuf_reset(&sb);+strbuf_addf(&sb,"%s%s",is_relative&&up_path?up_path:"",out);++free((char*)out);+returnstrbuf_detach(&sb,NULL);+}++staticintresolve_relative_url(intargc,constchar**argv,constchar*prefix)+{+if(argc==2)+printf("%s\n",relative_url(argv[1],NULL));+elseif(argc==3)+printf("%s\n",relative_url(argv[1],argv[2]));+else+die("BUG: resolve_relative_url only accepts one or two arguments");+return0;+}structmodule_list{conststructcache_entry**entries;
@@ -46,79 +46,6 @@ prefix=custom_name=depth=-# The function takes at most 2 arguments. The first argument is the-# URL that navigates to the submodule origin repo. When relative, this URL-# is relative to the superproject origin URL repo. The second up_path-# argument, if specified, is the relative path that navigates-# from the submodule working tree to the superproject working tree.-#-# The output of the function is the origin URL of the submodule.-#-# The output will either be an absolute URL or filesystem path (if the-# superproject origin URL is an absolute URL or filesystem path,-# respectively) or a relative file system path (if the superproject-# origin URL is a relative file system path).-#-# When the output is a relative file system path, the path is either-# relative to the submodule working tree, if up_path is specified, or to-# the superproject working tree otherwise.-resolve_relative_url()-{-remote=$(get_default_remote)-remoteurl=$(gitconfig"remote.$remote.url")||-remoteurl=$(pwd)# the repository is its own authoritative upstream-url="$1"-remoteurl=${remoteurl%/}-sep=/-up_path="$2"--case"$remoteurl"in-*:*|/*)-is_relative=-;;-./*|../*)-is_relative=t-;;-*)-is_relative=t-remoteurl="./$remoteurl"-;;-esac--whiletest-n"$url"-do-case"$url"in-../*)-url="${url#../}"-case"$remoteurl"in-*/*)-remoteurl="${remoteurl%/*}"-;;-*:*)-remoteurl="${remoteurl%:*}"-sep=:-;;-*)-iftest-z"$is_relative"||test"."="$remoteurl"-then-die"$(eval_gettext"cannot strip one component off url '\$remoteurl'")"-else-remoteurl=.-fi-;;-esac-;;-./*)-url="${url#./}"-;;-*)-break;;-esac-done-remoteurl="$remoteurl$sep${url%/}"-echo"${is_relative:+${up_path}}${remoteurl#./}"-}-# Resolve a path to be relative to another path. This is intended for# converting submodule paths when git-submodule is run in a subdirectory# and only handles paths where the directory separator is '/'.
@@ -281,7 +208,7 @@ cmd_add()die"$(gettext"Relative path can only be used from the toplevel of the working tree")"# dereference source url relative to parent's url-realrepo=$(resolve_relative_url"$repo")||exit+realrepo=$(gitsubmodule--helperresolve_relative_url"$repo")||exit;;*:*|/*)# absolute url
@@ -485,7 +412,7 @@ cmd_init()# Possibly a url relative to parentcase"$url"in./*|../*)-url=$(resolve_relative_url"$url")||exit+url=$(gitsubmodule--helperresolve_relative_url"$url")||exit;;esacgitconfigsubmodule."$name".url"$url"||
@@ -1190,9 +1117,9 @@ cmd_sync()# guarantee a trailing /up_path=${up_path%/}/&&# path from submodule work tree to submodule origin repo-sub_origin_url=$(resolve_relative_url"$url""$up_path")&&+sub_origin_url=$(gitsubmodule--helperresolve_relative_url"$url""$up_path")&&# path from superproject work tree to submodule origin repo-super_config_url=$(resolve_relative_url"$url")||exit+super_config_url=$(gitsubmodule--helperresolve_relative_url"$url")||exit;;*)sub_origin_url="$url"
From: Johannes Sixt <hidden> Date: 2016-06-15 23:07:25
Am 10.12.2015 um 02:07 schrieb Stefan Beller:
This reimplements the helper function `resolve_relative_url` in shell
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.
I see lots of '/', but no is_dir_sep() in the C version. Did you
consider that local URLs can use a backslash as path separator on
Windows? In the shell version, this did not matter because bash converts
the backslashes to forward slashes for us. But when rewritten in C, this
does not happen.
Valid URLs are
D:\foo\bar.git
\\server\share\foo\bar
..\..\foo\bar
and all of them with some or all backslashes replaced by forward slashes.
See also connect.c:url_is_local_not_ssh, which ensures that the first
example above is considered a local path with a drive letter, not a
remote ssh path.
quoted hunk
This also improves the performance:
(Best out of 3) time ./t7400-submodule-basic.sh
Before:
real 0m9.575s
user 0m2.683s
sys 0m6.773s
After:
real 0m9.293s
user 0m2.691s
sys 0m6.549s
Signed-off-by: Stefan Beller <redacted>
---
This applies on origin/master, and I'd carry as its own feature branch
as I am nowhere near done with the groups feature after reading Jens feedback.
(It took me a while to identify this as a next best step.)
Thanks,
Stefan
builtin/submodule--helper.c | 120 ++++++++++++++++++++++++++++++++++++++++++++
git-submodule.sh | 81 ++----------------------------
2 files changed, 124 insertions(+), 77 deletions(-)
@@ -9,6 +9,125 @@#include"submodule-config.h"#include"string-list.h"#include"run-command.h"+#include"remote.h"+#include"refs.h"++staticconstchar*get_default_remote(void)+{+char*dest=NULL;+unsignedcharsha1[20];+intflag;+structstrbufsb=STRBUF_INIT;+constchar*refname=resolve_ref_unsafe("HEAD",0,sha1,&flag);++if(!refname)+die("No such ref: HEAD");++refname=shorten_unambiguous_ref(refname,0);+strbuf_addf(&sb,"branch.%s.remote",refname);+if(git_config_get_string(sb.buf,&dest))+return"origin";+else+returnxstrdup(dest);+}++/*+*Thefunctiontakesatmost2arguments.Thefirstargumentisthe+*URLthatnavigatestothesubmoduleoriginrepo.Whenrelative,thisURL+*isrelativetothesuperprojectoriginURLrepo.Thesecondup_path+*argument,ifspecified,istherelativepaththatnavigates+*fromthesubmoduleworkingtreetothesuperprojectworkingtree.+*+*TheoutputofthefunctionistheoriginURLofthesubmodule.+*+*TheoutputwilleitherbeanabsoluteURLorfilesystempath(ifthe+*superprojectoriginURLisanabsoluteURLorfilesystempath,+*respectively)orarelativefilesystempath(ifthesuperproject+*originURLisarelativefilesystempath).+*+*Whentheoutputisarelativefilesystempath,thepathiseither+*relativetothesubmoduleworkingtree,ifup_pathisspecified,orto+*thesuperprojectworkingtreeotherwise.+*/+staticconstchar*relative_url(constchar*url,constchar*up_path)+{+intis_relative=0;+size_tlen;+char*remoteurl=NULL;+char*sep="/";+constchar*out;+structstrbufsb=STRBUF_INIT;+constchar*remote=get_default_remote();+strbuf_addf(&sb,"remote.%s.url",remote);++if(git_config_get_string(sb.buf,&remoteurl))+/* the repository is its own authoritative upstream */+remoteurl=xgetcwd();++if(strip_suffix(remoteurl,"/",&len))+remoteurl[len]='\0';++if(strchr(remoteurl,':')||skip_prefix(remoteurl,"/",&out))+is_relative=0;+elseif(skip_prefix(remoteurl,"./",&out)||+skip_prefix(remoteurl,"../",&out))+is_relative=1;+else{+is_relative=1;+strbuf_reset(&sb);+strbuf_addf(&sb,"./%s",remoteurl);+remoteurl=strbuf_detach(&sb,NULL);+}++while(url){+if(skip_prefix(url,"../",&out)){+char*rfind;+url=out;++rfind=strrchr(remoteurl,'/');+if(rfind)+*rfind='\0';+else{+rfind=strrchr(remoteurl,':');+if(rfind){+*rfind='\0';+sep=":";+}else{+if(is_relative||!strcmp(".",remoteurl))+die(N_("cannot strip one component off url '%s'"),remoteurl);+else+remoteurl=".";+}+}+}elseif(skip_prefix(url,"./",&out))+url=out;+else+break;+}+strbuf_reset(&sb);+strbuf_addf(&sb,"%s%s%s",remoteurl,sep,url);++if(!skip_prefix(sb.buf,"./",&out))+out=sb.buf;+out=xstrdup(out);++strbuf_reset(&sb);+strbuf_addf(&sb,"%s%s",is_relative&&up_path?up_path:"",out);++free((char*)out);+returnstrbuf_detach(&sb,NULL);+}++staticintresolve_relative_url(intargc,constchar**argv,constchar*prefix)+{+if(argc==2)+printf("%s\n",relative_url(argv[1],NULL));+elseif(argc==3)+printf("%s\n",relative_url(argv[1],argv[2]));+else+die("BUG: resolve_relative_url only accepts one or two arguments");+return0;+}structmodule_list{conststructcache_entry**entries;
@@ -46,79 +46,6 @@ prefix=custom_name=depth=-# The function takes at most 2 arguments. The first argument is the-# URL that navigates to the submodule origin repo. When relative, this URL-# is relative to the superproject origin URL repo. The second up_path-# argument, if specified, is the relative path that navigates-# from the submodule working tree to the superproject working tree.-#-# The output of the function is the origin URL of the submodule.-#-# The output will either be an absolute URL or filesystem path (if the-# superproject origin URL is an absolute URL or filesystem path,-# respectively) or a relative file system path (if the superproject-# origin URL is a relative file system path).-#-# When the output is a relative file system path, the path is either-# relative to the submodule working tree, if up_path is specified, or to-# the superproject working tree otherwise.-resolve_relative_url()-{-remote=$(get_default_remote)-remoteurl=$(gitconfig"remote.$remote.url")||-remoteurl=$(pwd)# the repository is its own authoritative upstream-url="$1"-remoteurl=${remoteurl%/}-sep=/-up_path="$2"--case"$remoteurl"in-*:*|/*)-is_relative=-;;-./*|../*)-is_relative=t-;;-*)-is_relative=t-remoteurl="./$remoteurl"-;;-esac--whiletest-n"$url"-do-case"$url"in-../*)-url="${url#../}"-case"$remoteurl"in-*/*)-remoteurl="${remoteurl%/*}"-;;-*:*)-remoteurl="${remoteurl%:*}"-sep=:-;;-*)-iftest-z"$is_relative"||test"."="$remoteurl"-then-die"$(eval_gettext"cannot strip one component off url '\$remoteurl'")"-else-remoteurl=.-fi-;;-esac-;;-./*)-url="${url#./}"-;;-*)-break;;-esac-done-remoteurl="$remoteurl$sep${url%/}"-echo"${is_relative:+${up_path}}${remoteurl#./}"-}-# Resolve a path to be relative to another path. This is intended for# converting submodule paths when git-submodule is run in a subdirectory# and only handles paths where the directory separator is '/'.
@@ -281,7 +208,7 @@ cmd_add()die"$(gettext"Relative path can only be used from the toplevel of the working tree")"# dereference source url relative to parent's url-realrepo=$(resolve_relative_url"$repo")||exit+realrepo=$(gitsubmodule--helperresolve_relative_url"$repo")||exit;;*:*|/*)# absolute url
@@ -485,7 +412,7 @@ cmd_init()# Possibly a url relative to parentcase"$url"in./*|../*)-url=$(resolve_relative_url"$url")||exit+url=$(gitsubmodule--helperresolve_relative_url"$url")||exit;;esacgitconfigsubmodule."$name".url"$url"||
@@ -1190,9 +1117,9 @@ cmd_sync()# guarantee a trailing /up_path=${up_path%/}/&&# path from submodule work tree to submodule origin repo-sub_origin_url=$(resolve_relative_url"$url""$up_path")&&+sub_origin_url=$(gitsubmodule--helperresolve_relative_url"$url""$up_path")&&# path from superproject work tree to submodule origin repo-super_config_url=$(resolve_relative_url"$url")||exit+super_config_url=$(gitsubmodule--helperresolve_relative_url"$url")||exit;;*)sub_origin_url="$url"
From: Stefan Beller <hidden> Date: 2016-06-15 23:07:29
On Wed, Dec 9, 2015 at 10:48 PM, Johannes Sixt [off-list ref] wrote:
Am 10.12.2015 um 02:07 schrieb Stefan Beller:
quoted
This reimplements the helper function `resolve_relative_url` in shell
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.
I see lots of '/', but no is_dir_sep() in the C version. Did you consider
that local URLs can use a backslash as path separator on Windows? In the
shell version, this did not matter because bash converts the backslashes to
forward slashes for us. But when rewritten in C, this does not happen.
I see. That's a pity. :(
Valid URLs are
D:\foo\bar.git
\\server\share\foo\bar
..\..\foo\bar
I am staring at the code in desperation as backslashes
in Linux are valid for file names, i.e.:
"/tmp/testfile\" is a valid filename
Now look at the code where the first slash occurs, it's
if (strip_suffix(remoteurl, "/", &len))
remoteurl[len] = '\0';
The intention is to strip off the last character if it is a directory separator.
So in the unix world we want to keep "/tmp/testfile\" as it is,
whereas in Windows
we want to chop off the backslash (because there is no file with a
backslash allowed,
it is the directory separator?)
So what I think I am going to do for next round is something like
static int has_same_dir_prefix(const char *str, const char **out)
{
#ifdef GIT_WINDOWS_NATIVE
return skip_prefix(str, "./", out)
|| skip_prefix(str, ".\\", out);
#else
return skip_prefix(str, "./", out);
#endif
}
static int has_upper_dir_prefix(const char *str, const char **out)
{
#ifdef GIT_WINDOWS_NATIVE
return skip_prefix(str, "../", out)
|| skip_prefix(str, "..\\", out);
#else
return skip_prefix(str, "../", out);
#endif
}
in the submodule helper function, or alternatively in the wrapper.c ?
and then rely on these functions being accurate.
Would that approach make sense?
Thanks,
Stefan
and all of them with some or all backslashes replaced by forward slashes.
See also connect.c:url_is_local_not_ssh, which ensures that the first
example above is considered a local path with a drive letter, not a remote
ssh path.
quoted
This also improves the performance:
(Best out of 3) time ./t7400-submodule-basic.sh
Before:
real 0m9.575s
user 0m2.683s
sys 0m6.773s
After:
real 0m9.293s
user 0m2.691s
sys 0m6.549s
Signed-off-by: Stefan Beller <redacted>
---
This applies on origin/master, and I'd carry as its own feature branch
as I am nowhere near done with the groups feature after reading Jens
feedback.
(It took me a while to identify this as a next best step.)
Thanks,
Stefan
builtin/submodule--helper.c | 120
++++++++++++++++++++++++++++++++++++++++++++
git-submodule.sh | 81 ++----------------------------
2 files changed, 124 insertions(+), 77 deletions(-)
@@ -46,79 +46,6 @@ prefix=custom_name=depth=-# The function takes at most 2 arguments. The first argument is the-# URL that navigates to the submodule origin repo. When relative, this
URL
-# is relative to the superproject origin URL repo. The second up_path
-# argument, if specified, is the relative path that navigates
-# from the submodule working tree to the superproject working tree.
-#
-# The output of the function is the origin URL of the submodule.
-#
-# The output will either be an absolute URL or filesystem path (if the
-# superproject origin URL is an absolute URL or filesystem path,
-# respectively) or a relative file system path (if the superproject
-# origin URL is a relative file system path).
-#
-# When the output is a relative file system path, the path is either
-# relative to the submodule working tree, if up_path is specified, or to
-# the superproject working tree otherwise.
-resolve_relative_url ()
-{
- remote=$(get_default_remote)
- remoteurl=$(git config "remote.$remote.url") ||
- remoteurl=$(pwd) # the repository is its own authoritative
upstream
- url="$1"
- remoteurl=${remoteurl%/}
- sep=/
- up_path="$2"
-
- case "$remoteurl" in
- *:*|/*)
- is_relative=
- ;;
- ./*|../*)
- is_relative=t
- ;;
- *)
- is_relative=t
- remoteurl="./$remoteurl"
- ;;
- esac
-
- while test -n "$url"
- do
- case "$url" in
- ../*)
- url="${url#../}"
- case "$remoteurl" in
- */*)
- remoteurl="${remoteurl%/*}"
- ;;
- *:*)
- remoteurl="${remoteurl%:*}"
- sep=:
- ;;
- *)
- if test -z "$is_relative" || test "." =
"$remoteurl"
- then
- die "$(eval_gettext "cannot strip
one component off url '\$remoteurl'")"
- else
- remoteurl=.
- fi
- ;;
- esac
- ;;
- ./*)
- url="${url#./}"
- ;;
- *)
- break;;
- esac
- done
- remoteurl="$remoteurl$sep${url%/}"
- echo "${is_relative:+${up_path}}${remoteurl#./}"
-}
-
# Resolve a path to be relative to another path. This is intended for
# converting submodule paths when git-submodule is run in a subdirectory
# and only handles paths where the directory separator is '/'.
@@ -281,7 +208,7 @@ cmd_add() die "$(gettext "Relative path can only be used from the
toplevel of the working tree")"
# dereference source url relative to parent's url
- realrepo=$(resolve_relative_url "$repo") || exit
+ realrepo=$(git submodule--helper resolve_relative_url
"$repo") || exit
;;
*:*|/*)
# absolute url
@@ -485,7 +412,7 @@ cmd_init() # Possibly a url relative to parent case "$url" in ./*|../*)- url=$(resolve_relative_url "$url") || exit+ url=$(git submodule--helper
From: Stefan Beller <hidden> Date: 2016-06-15 23:07:30
A new version of the patch, which spells out more its intent and
may actually work in Windows.
Any comment welcome,
Thanks,
Stefan
Stefan Beller (1):
submodule: Port resolve_relative_url from shell to C
builtin/submodule--helper.c | 151 ++++++++++++++++++++++++++++++++++++++++++++
git-submodule.sh | 81 ++----------------------
2 files changed, 155 insertions(+), 77 deletions(-)
interdiff to previous version:
From: Stefan Beller <hidden> Date: 2016-06-15 23:07:30
This reimplements the helper function `resolve_relative_url` in shell
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.
This also improves the performance:
(Best out of 3) time ./t7400-submodule-basic.sh
Before:
real 0m9.575s
user 0m2.683s
sys 0m6.773s
After:
real 0m9.293s
user 0m2.691s
sys 0m6.549s
That's about 3%.
Signed-off-by: Stefan Beller <redacted>
---
builtin/submodule--helper.c | 151 ++++++++++++++++++++++++++++++++++++++++++++
git-submodule.sh | 81 ++----------------------
2 files changed, 155 insertions(+), 77 deletions(-)
@@ -9,6 +9,156 @@#include"submodule-config.h"#include"string-list.h"#include"run-command.h"+#include"remote.h"+#include"refs.h"++staticconstchar*get_default_remote(void)+{+char*dest=NULL;+unsignedcharsha1[20];+intflag;+structstrbufsb=STRBUF_INIT;+constchar*refname=resolve_ref_unsafe("HEAD",0,sha1,&flag);++if(!refname)+die("No such ref: HEAD");++refname=shorten_unambiguous_ref(refname,0);+strbuf_addf(&sb,"branch.%s.remote",refname);+if(git_config_get_string(sb.buf,&dest))+return"origin";+else+returnxstrdup(dest);+}++staticinthas_same_dir_prefix(constchar*str,constchar**out)+{+#ifdef GIT_WINDOWS_NATIVE+returnskip_prefix(str,"./",out)+||skip_prefix(str,".\\",out);+#else+returnskip_prefix(str,"./",out);+#endif+}++staticinthas_upper_dir_prefix(constchar*str,constchar**out)+{+#ifdef GIT_WINDOWS_NATIVE+returnskip_prefix(str,"../",out)+||skip_prefix(str,"..\\",out);+#else+returnskip_prefix(str,"../",out);+#endif+}++staticchar*last_dir_separator(constchar*str)+{+#ifdef GIT_WINDOWS_NATIVE+returnstrrchr(str,"/")+||strrchr(str,"\\");+#else+returnstrrchr(str,'/');+#endif+}++/*+*Thefunctiontakesatmost2arguments.Thefirstargumentisthe+*URLthatnavigatestothesubmoduleoriginrepo.Whenrelative,thisURL+*isrelativetothesuperprojectoriginURLrepo.Thesecondup_path+*argument,ifspecified,istherelativepaththatnavigates+*fromthesubmoduleworkingtreetothesuperprojectworkingtree.+*+*TheoutputofthefunctionistheoriginURLofthesubmodule.+*+*TheoutputwilleitherbeanabsoluteURLorfilesystempath(ifthe+*superprojectoriginURLisanabsoluteURLorfilesystempath,+*respectively)orarelativefilesystempath(ifthesuperproject+*originURLisarelativefilesystempath).+*+*Whentheoutputisarelativefilesystempath,thepathiseither+*relativetothesubmoduleworkingtree,ifup_pathisspecified,orto+*thesuperprojectworkingtreeotherwise.+*/+staticconstchar*relative_url(constchar*url,constchar*up_path)+{+intis_relative=0;+size_tlen;+char*remoteurl=NULL;+char*sep="/";+constchar*out;+structstrbufsb=STRBUF_INIT;+constchar*remote=get_default_remote();+strbuf_addf(&sb,"remote.%s.url",remote);++if(git_config_get_string(sb.buf,&remoteurl))+/* the repository is its own authoritative upstream */+remoteurl=xgetcwd();++len=strlen(remoteurl);+if(is_dir_sep(remoteurl[len]))+remoteurl[len]='\0';++if(strchr(remoteurl,':')||is_dir_sep(*remoteurl))+is_relative=0;+elseif(has_same_dir_prefix(remoteurl,&out)||+has_upper_dir_prefix(remoteurl,&out))+is_relative=1;+else{+is_relative=1;+strbuf_reset(&sb);+strbuf_addf(&sb,"./%s",remoteurl);+remoteurl=strbuf_detach(&sb,NULL);+}++while(url){+if(has_upper_dir_prefix(url,&out)){+char*rfind;+url=out;++rfind=last_dir_separator(remoteurl);+if(rfind)+*rfind='\0';+else{+rfind=strrchr(remoteurl,':');+if(rfind){+*rfind='\0';+sep=":";+}else{+if(is_relative||!strcmp(".",remoteurl))+die(N_("cannot strip one component off url '%s'"),remoteurl);+else+remoteurl=".";+}+}+}elseif(has_same_dir_prefix(url,&out))+url=out;+else+break;+}+strbuf_reset(&sb);+strbuf_addf(&sb,"%s%s%s",remoteurl,sep,url);++if(!has_same_dir_prefix(sb.buf,&out))+out=sb.buf;+out=xstrdup(out);++strbuf_reset(&sb);+strbuf_addf(&sb,"%s%s",is_relative&&up_path?up_path:"",out);++free((char*)out);+returnstrbuf_detach(&sb,NULL);+}++staticintresolve_relative_url(intargc,constchar**argv,constchar*prefix)+{+if(argc==2)+printf("%s\n",relative_url(argv[1],NULL));+elseif(argc==3)+printf("%s\n",relative_url(argv[1],argv[2]));+else+die("BUG: resolve_relative_url only accepts one or two arguments");+return0;+}structmodule_list{conststructcache_entry**entries;
@@ -46,79 +46,6 @@ prefix=custom_name=depth=-# The function takes at most 2 arguments. The first argument is the-# URL that navigates to the submodule origin repo. When relative, this URL-# is relative to the superproject origin URL repo. The second up_path-# argument, if specified, is the relative path that navigates-# from the submodule working tree to the superproject working tree.-#-# The output of the function is the origin URL of the submodule.-#-# The output will either be an absolute URL or filesystem path (if the-# superproject origin URL is an absolute URL or filesystem path,-# respectively) or a relative file system path (if the superproject-# origin URL is a relative file system path).-#-# When the output is a relative file system path, the path is either-# relative to the submodule working tree, if up_path is specified, or to-# the superproject working tree otherwise.-resolve_relative_url()-{-remote=$(get_default_remote)-remoteurl=$(gitconfig"remote.$remote.url")||-remoteurl=$(pwd)# the repository is its own authoritative upstream-url="$1"-remoteurl=${remoteurl%/}-sep=/-up_path="$2"--case"$remoteurl"in-*:*|/*)-is_relative=-;;-./*|../*)-is_relative=t-;;-*)-is_relative=t-remoteurl="./$remoteurl"-;;-esac--whiletest-n"$url"-do-case"$url"in-../*)-url="${url#../}"-case"$remoteurl"in-*/*)-remoteurl="${remoteurl%/*}"-;;-*:*)-remoteurl="${remoteurl%:*}"-sep=:-;;-*)-iftest-z"$is_relative"||test"."="$remoteurl"-then-die"$(eval_gettext"cannot strip one component off url '\$remoteurl'")"-else-remoteurl=.-fi-;;-esac-;;-./*)-url="${url#./}"-;;-*)-break;;-esac-done-remoteurl="$remoteurl$sep${url%/}"-echo"${is_relative:+${up_path}}${remoteurl#./}"-}-# Resolve a path to be relative to another path. This is intended for# converting submodule paths when git-submodule is run in a subdirectory# and only handles paths where the directory separator is '/'.
@@ -281,7 +208,7 @@ cmd_add()die"$(gettext"Relative path can only be used from the toplevel of the working tree")"# dereference source url relative to parent's url-realrepo=$(resolve_relative_url"$repo")||exit+realrepo=$(gitsubmodule--helperresolve_relative_url"$repo")||exit;;*:*|/*)# absolute url
@@ -485,7 +412,7 @@ cmd_init()# Possibly a url relative to parentcase"$url"in./*|../*)-url=$(resolve_relative_url"$url")||exit+url=$(gitsubmodule--helperresolve_relative_url"$url")||exit;;esacgitconfigsubmodule."$name".url"$url"||
@@ -1190,9 +1117,9 @@ cmd_sync()# guarantee a trailing /up_path=${up_path%/}/&&# path from submodule work tree to submodule origin repo-sub_origin_url=$(resolve_relative_url"$url""$up_path")&&+sub_origin_url=$(gitsubmodule--helperresolve_relative_url"$url""$up_path")&&# path from superproject work tree to submodule origin repo-super_config_url=$(resolve_relative_url"$url")||exit+super_config_url=$(gitsubmodule--helperresolve_relative_url"$url")||exit;;*)sub_origin_url="$url"
A new version of the patch, which spells out more its intent and
may actually work in Windows.
Any comment welcome,
Thanks,
Stefan
Stefan Beller (1):
submodule: Port resolve_relative_url from shell to C
builtin/submodule--helper.c | 151 ++++++++++++++++++++++++++++++++++++++++++++
git-submodule.sh | 81 ++----------------------
2 files changed, 155 insertions(+), 77 deletions(-)
interdiff to previous version:
From: Eric Sunshine <hidden> Date: 2016-06-15 23:07:30
A rather superficial review...
On Wed, Dec 16, 2015 at 7:26 PM, Stefan Beller [off-list ref] wrote:
This reimplements the helper function `resolve_relative_url` in shell
s/This reimplements/Reimplement/
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
I guess you mean "As then we..." or something?
quoted hunk
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.
Signed-off-by: Stefan Beller <redacted>
---
Cleaner would be to move the #if's outside the functions:
#ifdef GIT_WINDOWS_NATIVE
/* Windows implementations */
static int has_same_dir_prefix(...) {...}
static int has_upper_dir_prefix(...) {...}
static char *last_dir_separator(...) {...}
#else
/* POSIX implementations */
static int has_same_dir_prefix(...) {...}
static int has_upper_dir_prefix(...) {...}
static char *last_dir_separator(...) {...}
#endif
'sep' only ever holds a single character, so why not declare it 'char'
rather than 'char *'? (And, adjust the format string of strbuf_addf(),
of course.)
Can we please use hyphens rather than underscores, and name this
"resolve-relative-url" instead? Quoting from a review[1] of an earlier
version of git-submodule--helper:
... these subcommands would be better spelled with a hyphen than
an underscore. If I recall correctly, the arguments for using
underscore were (1) a less noisy diff, (2) these are internal
commands nobody will be typing anyhow. However, (1) the diff
noise will be the same with hyphens, and (2) people will want to
test these commands manually anyhow, and its easier to type
hyphens and easier to remember them since the precedent for
hyphens in command-names is already well established.
Also, the reason that the original shell code used underscores
was because hyphens are not valid characters in shell function
names, but that's an implementation detail which shouldn't be
allowed to bleed over to user-facing interface design (and these
subcommands are user-facing).
[1]: http://article.gmane.org/gmane.comp.version-control.git/276947
From: Johannes Sixt <hidden> Date: 2016-06-15 23:07:30
Am 17.12.2015 um 01:26 schrieb Stefan Beller:
This reimplements the helper function `resolve_relative_url` in shell
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.
This also improves the performance:
(Best out of 3) time ./t7400-submodule-basic.sh
Before:
real 0m9.575s
user 0m2.683s
sys 0m6.773s
After:
real 0m9.293s
user 0m2.691s
sys 0m6.549s
That's about 3%.
I appreciate this effort as it should help us on Windows. Although the
numbers (and my own timings) suggest that this is only a small step
forward. That's not surprising as the patch removes only two forks.
As to the implementation, find a patch below that removes the ifdefs
and a few other suggestions. It is a mechanical conversion without
understanding what relative_url() does. I have the gut feeling that the
two strbuf_addf towards the end of the function can be contracted and
the temporarily allocate copy in 'out' can be removed.
If there were a few examples in the comment above the function, it
would be much simpler to understand.
As the set of prefixes you are looking is probably bounded, it may not
be worth generalizing this. But I wondered if something like:
/*
* Like skip_prefix, but consider any "/" in the prefix as a
* directory separator for the platform.
*/
int skip_prefix_fs(const char *str, const char *prefix, const char **out)
{
while (1) {
if (!*prefix) {
*out = str;
return 1;
} else if (*prefix == '/') {
if (!is_dir_sep(*str))
return 0;
} else {
if (*str != *prefix)
return 0;
}
str++;
prefix++;
}
}
...
/* works on all platforms! */
if (skip_prefix_fs(foo, "./", &out))
...
would be helpful. I don't know if there are other opportunities in the
code base that could make use of this. If it's just these two sites,
it's probably not worth it.
-Peff
From: Stefan Beller <hidden> Date: 2016-06-15 23:07:41
I forgot to send out the draft last year.
On Thu, Dec 17, 2015 at 10:55 AM, Johannes Sixt [off-list ref] wrote:
quoted
That's about 3%.
I appreciate this effort as it should help us on Windows. Although the
numbers (and my own timings) suggest that this is only a small step
forward. That's not surprising as the patch removes only two forks.
ok. Probably the timings are not as important here anyways.
As to the implementation, find a patch below that removes the ifdefs
and a few other suggestions. It is a mechanical conversion without
understanding what relative_url() does. I have the gut feeling that the
two strbuf_addf towards the end of the function can be contracted and
the temporarily allocate copy in 'out' can be removed.
I have the gut feeling, too. But I could not quite write down the solution.
If there were a few examples in the comment above the function, it
would be much simpler to understand.
I agree. So rewording the comment in the next reroll.