setup: make startup_info available everywhere
[gitweb.git] / git-submodule.sh
index 82e35582cd8e741280229f377b4f61d7d985bc6a..43c68deee9db2b243d39eba704878eb7551ada2d 100755 (executable)
@@ -154,48 +154,6 @@ relative_path ()
        echo "$result$target"
 }
 
-#
-# Get submodule info for registered submodules
-# $@ = path to limit submodule list
-#
-module_list()
-{
-       eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
-       (
-               git ls-files -z --error-unmatch --stage -- "$@" ||
-               echo "unmatched pathspec exists"
-       ) |
-       @@PERL@@ -e '
-       my %unmerged = ();
-       my ($null_sha1) = ("0" x 40);
-       my @out = ();
-       my $unmatched = 0;
-       $/ = "\0";
-       while (<STDIN>) {
-               if (/^unmatched pathspec/) {
-                       $unmatched = 1;
-                       next;
-               }
-               chomp;
-               my ($mode, $sha1, $stage, $path) =
-                       /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
-               next unless $mode eq "160000";
-               if ($stage ne "0") {
-                       if (!$unmerged{$path}++) {
-                               push @out, "$mode $null_sha1 U\t$path\n";
-                       }
-                       next;
-               }
-               push @out, "$_\n";
-       }
-       if ($unmatched) {
-               print "#unmatched\n";
-       } else {
-               print for (@out);
-       }
-       '
-}
-
 die_if_unmatched ()
 {
        if test "$1" = "#unmatched"
@@ -229,98 +187,6 @@ get_submodule_config () {
        printf '%s' "${value:-$default}"
 }
 
-
-#
-# Map submodule path to submodule name
-#
-# $1 = path
-#
-module_name()
-{
-       # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
-       sm_path="$1"
-       re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
-       name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
-               sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
-       test -z "$name" &&
-       die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")"
-       printf '%s\n' "$name"
-}
-
-#
-# Clone a submodule
-#
-# $1 = submodule path
-# $2 = submodule name
-# $3 = URL to clone
-# $4 = reference repository to reuse (empty for independent)
-# $5 = depth argument for shallow clones (empty for deep)
-#
-# Prior to calling, cmd_update checks that a possibly existing
-# path is not a git repository.
-# Likewise, cmd_add checks that path does not exist at all,
-# since it is the location of a new submodule.
-#
-module_clone()
-{
-       sm_path=$1
-       name=$2
-       url=$3
-       reference="$4"
-       depth="$5"
-       quiet=
-       if test -n "$GIT_QUIET"
-       then
-               quiet=-q
-       fi
-
-       gitdir=
-       gitdir_base=
-       base_name=$(dirname "$name")
-
-       gitdir=$(git rev-parse --git-dir)
-       gitdir_base="$gitdir/modules/$base_name"
-       gitdir="$gitdir/modules/$name"
-
-       if test -d "$gitdir"
-       then
-               mkdir -p "$sm_path"
-               rm -f "$gitdir/index"
-       else
-               mkdir -p "$gitdir_base"
-               (
-                       clear_local_git_env
-                       git clone $quiet ${depth:+"$depth"} -n ${reference:+"$reference"} \
-                               --separate-git-dir "$gitdir" "$url" "$sm_path"
-               ) ||
-               die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
-       fi
-
-       # We already are at the root of the work tree but cd_to_toplevel will
-       # resolve any symlinks that might be present in $PWD
-       a=$(cd_to_toplevel && cd "$gitdir" && pwd)/
-       b=$(cd_to_toplevel && cd "$sm_path" && pwd)/
-       # Remove all common leading directories after a sanity check
-       if test "${a#$b}" != "$a" || test "${b#$a}" != "$b"; then
-               die "$(eval_gettext "Gitdir '\$a' is part of the submodule path '\$b' or vice versa")"
-       fi
-       while test "${a%%/*}" = "${b%%/*}"
-       do
-               a=${a#*/}
-               b=${b#*/}
-       done
-       # Now chop off the trailing '/'s that were added in the beginning
-       a=${a%/}
-       b=${b%/}
-
-       # Turn each leading "*/" component into "../"
-       rel=$(printf '%s\n' "$b" | sed -e 's|[^/][^/]*|..|g')
-       printf '%s\n' "gitdir: $rel/$a" >"$sm_path/.git"
-
-       rel=$(printf '%s\n' "$a" | sed -e 's|[^/][^/]*|..|g')
-       (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
-}
-
 isnumber()
 {
        n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -481,7 +347,7 @@ Use -f if you really want to add it." >&2
                                echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
                        fi
                fi
-               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
+               git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" "$reference" "$depth" || exit
                (
                        clear_local_git_env
                        cd "$sm_path" &&
@@ -541,7 +407,7 @@ cmd_foreach()
        # command in the subshell (and a recursive call to this function)
        exec 3<&0
 
-       module_list |
+       git submodule--helper list --prefix "$wt_prefix"|
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
@@ -549,7 +415,7 @@ cmd_foreach()
                then
                        displaypath=$(relative_path "$sm_path")
                        say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
-                       name=$(module_name "$sm_path")
+                       name=$(git submodule--helper name "$sm_path")
                        (
                                prefix="$prefix$sm_path/"
                                clear_local_git_env
@@ -601,11 +467,11 @@ cmd_init()
                shift
        done
 
-       module_list "$@" |
+       git submodule--helper list --prefix "$wt_prefix" "$@" |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
-               name=$(module_name "$sm_path") || exit
+               name=$(git submodule--helper name "$sm_path") || exit
 
                displaypath=$(relative_path "$sm_path")
 
@@ -683,11 +549,11 @@ cmd_deinit()
                die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
        fi
 
-       module_list "$@" |
+       git submodule--helper list --prefix "$wt_prefix" "$@" |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
-               name=$(module_name "$sm_path") || exit
+               name=$(git submodule--helper name "$sm_path") || exit
 
                displaypath=$(relative_path "$sm_path")
 
@@ -725,6 +591,24 @@ cmd_deinit()
        done
 }
 
+is_tip_reachable () (
+       clear_local_git_env
+       cd "$1" &&
+       rev=$(git rev-list -n 1 "$2" --not --all 2>/dev/null) &&
+       test -z "$rev"
+)
+
+fetch_in_submodule () (
+       clear_local_git_env
+       cd "$1" &&
+       case "$2" in
+       '')
+               git fetch ;;
+       *)
+               git fetch $(get_default_remote) "$2" ;;
+       esac
+)
+
 #
 # Update each submodule path to correct revision, using clone and checkout as needed
 #
@@ -799,7 +683,7 @@ cmd_update()
        fi
 
        cloned_modules=
-       module_list "$@" | {
+       git submodule--helper list --prefix "$wt_prefix" "$@" | {
        err=
        while read mode sha1 stage sm_path
        do
@@ -809,7 +693,7 @@ cmd_update()
                        echo >&2 "Skipping unmerged submodule $prefix$sm_path"
                        continue
                fi
-               name=$(module_name "$sm_path") || exit
+               name=$(git submodule--helper name "$sm_path") || exit
                url=$(git config submodule."$name".url)
                branch=$(get_submodule_config "$name" branch master)
                if ! test -z "$update"
@@ -843,7 +727,7 @@ Maybe you want to use 'update --init'?")"
 
                if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
                then
-                       module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
+                       git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" "$reference" "$depth" || exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
@@ -879,10 +763,15 @@ Maybe you want to use 'update --init'?")"
                        then
                                # Run fetch only if $sha1 isn't present or it
                                # is not reachable from a ref.
-                               (clear_local_git_env; cd "$sm_path" &&
-                                       ( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
-                                        test -z "$rev") || git-fetch)) ||
+                               is_tip_reachable "$sm_path" "$sha1" ||
+                               fetch_in_submodule "$sm_path" ||
                                die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
+
+                               # Now we tried the usual fetch, but $sha1 may
+                               # not be reachable from any of the refs
+                               is_tip_reachable "$sm_path" "$sha1" ||
+                               fetch_in_submodule "$sm_path" "$sha1" ||
+                               die "$(eval_gettext "Fetched in submodule path '\$displaypath', but it did not contain $sha1. Direct fetching of that commit failed.")"
                        fi
 
                        # Is this something we just cloned?
@@ -1073,7 +962,7 @@ cmd_summary() {
                        # Respect the ignore setting for --for-status.
                        if test -n "$for_status"
                        then
-                               name=$(module_name "$sm_path")
+                               name=$(git submodule--helper name "$sm_path")
                                ignore_config=$(get_submodule_config "$name" ignore none)
                                test $status != A && test $ignore_config = all && continue
                        fi
@@ -1231,11 +1120,11 @@ cmd_status()
                shift
        done
 
-       module_list "$@" |
+       git submodule--helper list --prefix "$wt_prefix" "$@" |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
-               name=$(module_name "$sm_path") || exit
+               name=$(git submodule--helper name "$sm_path") || exit
                url=$(git config submodule."$name".url)
                displaypath=$(relative_path "$prefix$sm_path")
                if test "$stage" = U
@@ -1308,11 +1197,11 @@ cmd_sync()
                esac
        done
        cd_to_toplevel
-       module_list "$@" |
+       git submodule--helper list --prefix "$wt_prefix" "$@" |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
-               name=$(module_name "$sm_path")
+               name=$(git submodule--helper name "$sm_path")
                url=$(git config -f .gitmodules --get submodule."$name".url)
 
                # Possibly a url relative to parent