update=
prefix=
-# 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.
+# Print a string classifying a repository URL.
#
-# 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).
+# $1 = url
#
-# 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 ()
+url_type ()
{
- 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=
+ case "$url" in
+ *:*)
+ type='remote'
;;
- ./*|../*)
- is_relative=t
+ /*)
+ type='absolute'
;;
*)
- is_relative=t
- remoteurl="./$remoteurl"
+ type='relative'
;;
esac
+ printf '%s' "$type"
+}
+
+#
+# Join URLs, removing extra ./, ../, etc.
+#
+# $1 = base url
+# $2 = relative url
+#
+# If $2 is not relative, it will be returned unaltered.
+#
+join_urls ()
+{
+ base="$1"
+ relative="$2"
+
+ relative_type=$(url_type "$relative")
+ if test "$relative_type" != 'relative' -o -z "$base"
+ then
+ printf '%s' "$relative"
+ return
+ fi
+
+ base_type=$(url_type "$base")
+ if test "$base_type" = 'relative'
+ then
+ base="${base%/}/" # ensure a trailing slash, e.g. '..' -> '../'
+ case "$base" in
+ ./*|../*)
+ ;;
+ *)
+ base="./$base" # convert 'a/b/...' to './a/b/...'
+ ;;
+ esac
+ fi
- while test -n "$url"
+ base="${base%/}"
+ sep=/
+
+ while test -n "$relative"
do
- case "$url" in
+ relative="${relative%/}/" # ensure a trailing slash, e.g. '..' -> '../'
+ case "$relative" in
../*)
- url="${url#../}"
- case "$remoteurl" in
+ case "$base" in
+ ..|../..|../*/..)
+ break;;
*/*)
- remoteurl="${remoteurl%/*}"
+ base="${base%/*}"
;;
*:*)
- remoteurl="${remoteurl%:*}"
+ base="${base%:*}"
sep=:
;;
+ /|.)
+ die "$(eval_gettext "cannot strip one component off remote '\$base'")"
+ ;;
*)
- if test -z "$is_relative" || test "." = "$remoteurl"
- then
- die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
- else
- remoteurl=.
- fi
+ base=.
;;
esac
+ relative="${relative#../}"
;;
+ ./*)
+ relative="${relative#./}"
+ ;;
+ *)
+ break;;
+ esac
+ done
+ if test -z "$relative"
+ then
+ sep=
+ fi
+ url="$base$sep${relative%/}"
+ while test -n "url"
+ do
+ case "$url" in
./*)
url="${url#./}"
;;
break;;
esac
done
- remoteurl="$remoteurl$sep${url%/}"
- echo "${is_relative:+${up_path}}${remoteurl#./}"
+ printf '%s' "$url"
+}
+
+# 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 ()
+{
+ url="$1"
+ up_path="$2"
+
+ url_type=$(url_type "$url")
+ if test "$url_type" = 'relative'
+ then
+ remote=$(get_default_remote)
+ remote_url=$(git config "remote.$remote.url") ||
+ remote_url=$(pwd) # the repository is its own authoritative upstream
+ remote_type=$(url_type "$remote_url")
+ if test "$remote_type" = 'relative' -a -n "${up_path}"
+ then
+ remote_url=$(join_urls "${up_path}" "${remote_url}")
+ fi
+ url=$(join_urls "$remote_url" "$url")
+ fi
+ printf '%s' "$url"
+}
+
+#
+# Get submodules up_path (for resolve_relative_url)
+#
+# $1 = $sm_path, as returned by module_list()
+#
+get_up_path()
+{
+ sm_path="$1"
+ if test -n "$sm_path"
+ then
+ # rewrite foo/bar as ../.. to find path from
+ # submodule work tree to superproject work tree
+ up_path="$(printf '%s' "$sm_path" | sed "s/[^/][^/]*/../g")" &&
+ # guarantee a trailing /
+ up_path="${up_path%/}/" &&
+ printf '%s' "$up_path"
+ fi
}
#
printf '%s' "${value:-$default}"
}
+#
+# Print the .gitmodules-configured submodule URL.
+#
+# $1 = submodule name
+# $2 = $sm_path, as returned by module_list()
+#
+# This expands relative URLs from .gitmodules so they are relative to
+# the submodule's remote repository.
+#
+get_submodule_url_from_gitmodules()
+{
+ name="$1"
+ sm_path="$2"
+ url=$(git config -f .gitmodules submodule."$name".url)
+ url=$(resolve_relative_url "${url:-.}" "$up_path")
+ printf '%s' "$url"
+}
+
+#
+# Print the configured submodule URL.
+#
+# $1 = submodule name
+# $2 = $sm_path, as returned by module_list()
+#
+# This is similar to get_submodule_config() for submodule.<name>.url,
+# but it also expands relative URLs from .gitmodules so they are
+# relative to the submodule's working directory.
+#
+get_submodule_url()
+{
+ name="$1"
+ sm_path="$2"
+ url=$(git config submodule."$name".url)
+ if test -n "$url"
+ then
+ # already resolved relative to the superproject's default remote
+ up_path=$(get_up_path "$sm_path")
+ url=$(join_urls "$up_path" "$url")
+ else
+ # resolve relative to superproject's default remote
+ url=$(get_submodule_url_from_gitmodules "$name" "$sm_path")
+ fi
+ printf '%s' "$url"
+}
#
# Map submodule path to submodule name
fi
# assure repo is absolute or relative to parent
- case "$repo" in
- ./*|../*)
- # dereference source url relative to parent's url
- realrepo=$(resolve_relative_url "$repo") || exit
- ;;
- *:*|/*)
- # absolute url
- realrepo=$repo
- ;;
- *)
- die "$(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
- ;;
- esac
+ realrepo=$(resolve_relative_url "$repo") ||
+ die "$(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
# normalize path:
# multiple //; leading ./; /./; /../; trailing /
esac
) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
fi
- git config submodule."$sm_path".url "$realrepo"
git add $force "$sm_path" ||
die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
# Copy url setting when it is not set yet
if test -z "$(git config "submodule.$name.url")"
then
- url=$(git config -f .gitmodules submodule."$name".url)
+ url=$(get_submodule_url "$name")
test -z "$url" &&
- die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
+ die "$(eval_gettext "No url found for submodule path '\$sm_path'")"
- # Possibly a url relative to parent
- case "$url" in
- ./*|../*)
- url=$(resolve_relative_url "$url") || exit
- ;;
- esac
git config submodule."$name".url "$url" ||
die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
continue
fi
name=$(module_name "$sm_path") || exit
- url=$(git config submodule."$name".url)
+ url=$(get_submodule_url "$name" "$sm_path")
if ! test -z "$update"
then
update_module=$update
else
- update_module=$(git config submodule."$name".update)
+ update_module=$(get_submodule_config "$name" update)
fi
if test "$update_module" = "none"
if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
then
- module_clone "$sm_path" "$url" "$reference"|| exit
+ super_url=$(get_submodule_url "$name") # relative to superproject
+ module_clone "$sm_path" "$super_url" "$reference"|| exit
cloned_modules="$cloned_modules;$name"
subsha1=
else
do
die_if_unmatched "$mode"
name=$(module_name "$sm_path") || exit
- url=$(git config submodule."$name".url)
+ url=$(get_submodule_url "$name" "$sm_path")
displaypath="$prefix$sm_path"
if test "$stage" = U
then
do
die_if_unmatched "$mode"
name=$(module_name "$sm_path")
- url=$(git config -f .gitmodules --get submodule."$name".url)
-
- # Possibly a url relative to parent
- case "$url" in
- ./*|../*)
- # rewrite foo/bar as ../.. to find path from
- # submodule work tree to superproject work tree
- up_path="$(echo "$sm_path" | sed "s/[^/][^/]*/../g")" &&
- # 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") &&
- # path from superproject work tree to submodule origin repo
- super_config_url=$(resolve_relative_url "$url") || exit
- ;;
- *)
- sub_origin_url="$url"
- super_config_url="$url"
- ;;
- esac
+ # .gitmodules-style (unresolved) url for the submodule origin repo
+ super_config_url=$(get_submodule_config "$name" url)
+ # path from submodule work tree to submodule origin repo
+ sub_origin_url=$(get_submodule_url "$name" "$sm_path")
if git config "submodule.$name.url" >/dev/null 2>/dev/null
then
- say "$(eval_gettext "Synchronizing submodule url for '\$name'")"
+ say "$(eval_gettext "Synchronizing submodule url for '\$name' (superproject config)")"
git config submodule."$name".url "$super_config_url"
+ fi
- if test -e "$sm_path"/.git
- then
- (
- clear_local_git_env
- cd "$sm_path"
- remote=$(get_default_remote)
- git config remote."$remote".url "$sub_origin_url"
- )
- fi
+ if test -e "$sm_path"/.git
+ then
+ say "$(eval_gettext "Synchronizing submodule url for '\$name' (subproject config)")"
+ (
+ clear_local_git_env
+ cd "$sm_path"
+ remote=$(get_default_remote)
+ git config remote."$remote".url "$sub_origin_url"
+ )
fi
done
}