diff options
Diffstat (limited to 'git-submodule.sh')
| -rwxr-xr-x | git-submodule.sh | 638 |
1 files changed, 463 insertions, 175 deletions
diff --git a/git-submodule.sh b/git-submodule.sh index 0462e529d9..2365149d0b 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -1,45 +1,97 @@ #!/bin/sh # -# git-submodules.sh: add, init, update or list git submodules +# git-submodule.sh: add, init, update or list git submodules # # Copyright (c) 2007 Lars Hjemli dashless=$(basename "$0" | sed -e 's/-/ /') -USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path> +USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>] or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...] or: $dashless [--quiet] init [--] [<path>...] - or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] + or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] or: $dashless [--quiet] foreach [--recursive] <command> - or: $dashless [--quiet] sync [--] [<path>...]" + or: $dashless [--quiet] sync [--recursive] [--] [<path>...]" OPTIONS_SPEC= . git-sh-setup +. git-sh-i18n . git-parse-remote require_work_tree command= branch= +force= reference= cached= +recursive= +init= files= nofetch= update= prefix= +custom_name= -# Resolve relative url by appending to parent'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 () { remote=$(get_default_remote) remoteurl=$(git config "remote.$remote.url") || - die "remote ($remote) does not have a url defined in .git/config" + 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#../}" - remoteurl="${remoteurl%/*}" + 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#./}" @@ -48,7 +100,8 @@ resolve_relative_url () break;; esac done - echo "$remoteurl/${url%/}" + remoteurl="$remoteurl$sep${url%/}" + echo "${is_relative:+${up_path}}${remoteurl#./}" } # @@ -57,7 +110,46 @@ resolve_relative_url () # module_list() { - git ls-files --error-unmatch --stage -- "$@" | grep '^160000 ' + ( + git ls-files --error-unmatch --stage -- "$@" || + echo "unmatched pathspec exists" + ) | + perl -e ' + my %unmerged = (); + my ($null_sha1) = ("0" x 40); + my @out = (); + my $unmatched = 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" + then + exit 1 + fi } # @@ -68,12 +160,13 @@ module_list() 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 "No submodule mapping found in .gitmodules for path '$path'" - echo "$name" + test -z "$name" && + die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")" + echo "$name" } # @@ -86,31 +179,64 @@ module_name() # module_clone() { - path=$1 - url=$2 - reference="$3" - - # If there already is a directory at the submodule path, - # expect it to be empty (since that is the default checkout - # action) and try to remove it. - # Note: if $path is a symlink to a directory the test will - # succeed but the rmdir will fail. We might want to fix this. - if test -d "$path" + sm_path=$1 + name=$2 + url=$3 + reference="$4" + quiet= + if test -n "$GIT_QUIET" then - rmdir "$path" 2>/dev/null || - die "Directory '$path' exists, but is neither empty nor a git repository" + quiet=-q fi - test -e "$path" && - die "A file already exist at path '$path'" + 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 -n "$reference" + if test -d "$gitdir" then - git-clone "$reference" -n "$url" "$path" + mkdir -p "$sm_path" + rm -f "$gitdir/index" else - git-clone -n "$url" "$path" - fi || - die "Clone of '$url' into submodule path '$path' failed" + mkdir -p "$gitdir_base" + ( + clear_local_git_env + git clone $quiet -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)/ + # normalize Windows-style absolute paths to POSIX-style absolute paths + case $a in [a-zA-Z]:/*) a=/${a%%:*}${a#*:} ;; esac + case $b in [a-zA-Z]:/*) b=/${b%%:*}${b#*:} ;; esac + # 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=$(echo $b | sed -e 's|[^/][^/]*|..|g') + echo "gitdir: $rel/$a" >"$sm_path/.git" + + rel=$(echo $a | sed -e 's|[^/][^/]*|..|g') + (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b") } # @@ -131,6 +257,9 @@ cmd_add() branch=$2 shift ;; + -f | --force) + force=$1 + ;; -q|--quiet) GIT_QUIET=1 ;; @@ -141,6 +270,10 @@ cmd_add() ;; --reference=*) reference="$1" + ;; + --name) + case "$2" in '') usage ;; esac + custom_name=$2 shift ;; --) @@ -158,9 +291,14 @@ cmd_add() done repo=$1 - path=$2 + sm_path=$2 - if test -z "$repo" -o -z "$path"; then + if test -z "$sm_path"; then + sm_path=$(echo "$repo" | + sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g') + fi + + if test -z "$repo" -o -z "$sm_path"; then usage fi @@ -175,13 +313,13 @@ cmd_add() realrepo=$repo ;; *) - die "repo URL: '$repo' must be absolute or begin with ./|../" + die "$(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")" ;; esac # normalize path: # multiple //; leading ./; /./; /../; trailing / - path=$(printf '%s/\n' "$path" | + sm_path=$(printf '%s/\n' "$sm_path" | sed -e ' s|//*|/|g s|^\(\./\)*|| @@ -191,49 +329,69 @@ cmd_add() tstart s|/*$|| ') - git ls-files --error-unmatch "$path" > /dev/null 2>&1 && - die "'$path' already exists in the index" + git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 && + die "$(eval_gettext "'\$sm_path' already exists in the index")" + + if test -z "$force" && ! git add --dry-run --ignore-missing "$sm_path" > /dev/null 2>&1 + then + eval_gettextln "The following path is ignored by one of your .gitignore files: +\$sm_path +Use -f if you really want to add it." >&2 + exit 1 + fi + + if test -n "$custom_name" + then + sm_name="$custom_name" + else + sm_name="$sm_path" + fi # perhaps the path exists and is already a git repo, else clone it - if test -e "$path" + if test -e "$sm_path" then - if test -d "$path"/.git -o -f "$path"/.git + if test -d "$sm_path"/.git -o -f "$sm_path"/.git then - echo "Adding existing repo at '$path' to the index" + eval_gettextln "Adding existing repo at '\$sm_path' to the index" else - die "'$path' already exists and is not a valid git repo" + die "$(eval_gettext "'\$sm_path' already exists and is not a valid git repo")" fi - case "$repo" in - ./*|../*) - url=$(resolve_relative_url "$repo") || exit - ;; - *) - url="$repo" - ;; - esac - git config submodule."$path".url "$url" else - - module_clone "$path" "$realrepo" "$reference" || exit + if test -d ".git/modules/$sm_name" + then + if test -z "$force" + then + echo >&2 "$(eval_gettext "A git directory for '\$sm_name' is found locally with remote(s):")" + GIT_DIR=".git/modules/$sm_name" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^," ", -e s,' (fetch)',, >&2 + echo >&2 "$(eval_gettext "If you want to reuse this local git directory instead of cloning again from")" + echo >&2 " $realrepo" + echo >&2 "$(eval_gettext "use the '--force' option. If the local git directory is not the correct repo")" + die "$(eval_gettext "or you are unsure what this means choose another name with the '--name' option.")" + else + echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")" + fi + fi + module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit ( - unset GIT_DIR - cd "$path" && + clear_local_git_env + cd "$sm_path" && # ash fails to wordsplit ${branch:+-b "$branch"...} case "$branch" in '') git checkout -f -q ;; - ?*) git checkout -f -q -b "$branch" "origin/$branch" ;; + ?*) git checkout -f -q -B "$branch" "origin/$branch" ;; esac - ) || die "Unable to checkout submodule '$path'" + ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")" fi + git config submodule."$sm_name".url "$realrepo" - git add "$path" || - die "Failed to add submodule '$path'" + git add $force "$sm_path" || + die "$(eval_gettext "Failed to add submodule '\$sm_path'")" - git config -f .gitmodules submodule."$path".path "$path" && - git config -f .gitmodules submodule."$path".url "$repo" && - git add .gitmodules || - die "Failed to register submodule '$path'" + git config -f .gitmodules submodule."$sm_name".path "$sm_path" && + git config -f .gitmodules submodule."$sm_name".url "$repo" && + git add --force .gitmodules || + die "$(eval_gettext "Failed to register submodule '\$sm_path'")" } # @@ -264,24 +422,33 @@ cmd_foreach() shift done + toplevel=$(pwd) + + # dup stdin so that it can be restored when running the external + # command in the subshell (and a recursive call to this function) + exec 3<&0 + module_list | - while read mode sha1 stage path + while read mode sha1 stage sm_path do - if test -e "$path"/.git + die_if_unmatched "$mode" + if test -e "$sm_path"/.git then - say "Entering '$prefix$path'" - name=$(module_name "$path") + say "$(eval_gettext "Entering '\$prefix\$sm_path'")" + name=$(module_name "$sm_path") ( - prefix="$prefix$path/" - unset GIT_DIR - cd "$path" && + prefix="$prefix$sm_path/" + clear_local_git_env + # we make $path available to scripts ... + path=$sm_path + cd "$sm_path" && eval "$@" && if test -n "$recursive" then cmd_foreach "--recursive" "$@" fi - ) || - die "Stopping at '$path'; script returned non-zero status." + ) <&3 3<&- || + die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")" fi done } @@ -315,33 +482,36 @@ cmd_init() done module_list "$@" | - while read mode sha1 stage path + while read mode sha1 stage sm_path do - # Skip already registered paths - name=$(module_name "$path") || exit - url=$(git config submodule."$name".url) - test -z "$url" || continue + die_if_unmatched "$mode" + name=$(module_name "$sm_path") || exit - url=$(git config -f .gitmodules submodule."$name".url) - test -z "$url" && - die "No url found for submodule path '$path' in .gitmodules" + # 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) + test -z "$url" && + die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")" - # Possibly a url relative to parent - case "$url" in - ./*|../*) - url=$(resolve_relative_url "$url") || exit - ;; - esac + # 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'")" - git config submodule."$name".url "$url" || - die "Failed to register url for submodule path '$path'" + say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")" + fi + # Copy "update" setting when it is not set yet upd="$(git config -f .gitmodules submodule."$name".update)" test -z "$upd" || + test -n "$(git config submodule."$name".update)" || git config submodule."$name".update "$upd" || - die "Failed to register update mode for submodule path '$path'" - - say "Submodule '$name' ($url) registered for path '$path'" + die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")" done } @@ -353,43 +523,43 @@ cmd_init() cmd_update() { # parse $args after "submodule ... update". - orig_args="$@" + orig_flags= while test $# -ne 0 do case "$1" in -q|--quiet) - shift GIT_QUIET=1 ;; -i|--init) init=1 - shift ;; -N|--no-fetch) - shift nofetch=1 ;; + -f|--force) + force=$1 + ;; -r|--rebase) - shift update="rebase" ;; --reference) case "$2" in '') usage ;; esac reference="--reference=$2" - shift 2 + orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")" + shift ;; --reference=*) reference="$1" - shift ;; -m|--merge) - shift update="merge" ;; --recursive) - shift recursive=1 ;; + --checkout) + update="checkout" + ;; --) shift break @@ -401,6 +571,8 @@ cmd_update() break ;; esac + orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")" + shift done if test -n "$init" @@ -408,86 +580,150 @@ cmd_update() cmd_init "--" "$@" || return fi - module_list "$@" | - while read mode sha1 stage path + cloned_modules= + module_list "$@" | { + err= + while read mode sha1 stage sm_path do - name=$(module_name "$path") || exit + die_if_unmatched "$mode" + if test "$stage" = U + then + echo >&2 "Skipping unmerged submodule $sm_path" + continue + fi + name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) - update_module=$(git config submodule."$name".update) + if ! test -z "$update" + then + update_module=$update + else + update_module=$(git config submodule."$name".update) + fi + + if test "$update_module" = "none" + then + echo "Skipping submodule '$sm_path'" + continue + fi + if test -z "$url" then # Only mention uninitialized submodules when its # path have been specified test "$#" != "0" && - say "Submodule path '$path' not initialized" && - say "Maybe you want to use 'update --init'?" + say "$(eval_gettext "Submodule path '\$sm_path' not initialized +Maybe you want to use 'update --init'?")" continue fi - if ! test -d "$path"/.git -o -f "$path"/.git + if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git then - module_clone "$path" "$url" "$reference"|| exit + module_clone "$sm_path" "$name" "$url" "$reference" || exit + cloned_modules="$cloned_modules;$name" subsha1= else - subsha1=$(unset GIT_DIR; cd "$path" && + subsha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD) || - die "Unable to find current revision in submodule path '$path'" - fi - - if ! test -z "$update" - then - update_module=$update + die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")" fi - if test "$subsha1" != "$sha1" + if test "$subsha1" != "$sha1" -o -n "$force" then - force= - if test -z "$subsha1" + subforce=$force + # If we don't already have a -f flag and the submodule has never been checked out + if test -z "$subsha1" -a -z "$force" then - force="-f" + subforce="-f" fi if test -z "$nofetch" then - (unset GIT_DIR; cd "$path" && - git-fetch) || - die "Unable to fetch in submodule path '$path'" + # 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)) || + die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" fi + # Is this something we just cloned? + case ";$cloned_modules;" in + *";$name;"*) + # then there is no local change to integrate + update_module= ;; + esac + + must_die_on_failure= case "$update_module" in rebase) command="git rebase" - action="rebase" - msg="rebased onto" + die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$sm_path'")" + say_msg="$(eval_gettext "Submodule path '\$sm_path': rebased into '\$sha1'")" + must_die_on_failure=yes ;; merge) command="git merge" - action="merge" - msg="merged in" + die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$sm_path'")" + say_msg="$(eval_gettext "Submodule path '\$sm_path': merged in '\$sha1'")" + must_die_on_failure=yes ;; *) - command="git checkout $force -q" - action="checkout" - msg="checked out" + command="git checkout $subforce -q" + die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$sm_path'")" + say_msg="$(eval_gettext "Submodule path '\$sm_path': checked out '\$sha1'")" ;; esac - (unset GIT_DIR; cd "$path" && $command "$sha1") || - die "Unable to $action '$sha1' in submodule path '$path'" - say "Submodule path '$path': $msg '$sha1'" + if (clear_local_git_env; cd "$sm_path" && $command "$sha1") + then + say "$say_msg" + elif test -n "$must_die_on_failure" + then + die_with_status 2 "$die_msg" + else + err="${err};$die_msg" + continue + fi fi if test -n "$recursive" then - (unset GIT_DIR; cd "$path" && cmd_update $orig_args) || - die "Failed to recurse into submodule path '$path'" + (clear_local_git_env; cd "$sm_path" && eval cmd_update "$orig_flags") + res=$? + if test $res -gt 0 + then + die_msg="$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")" + if test $res -eq 1 + then + err="${err};$die_msg" + continue + else + die_with_status $res "$die_msg" + fi + fi fi done + + if test -n "$err" + then + OIFS=$IFS + IFS=';' + for e in $err + do + if test -n "$e" + then + echo >&2 "$e" + fi + done + IFS=$OIFS + exit 1 + fi + } } set_name_rev () { revname=$( ( - unset GIT_DIR + clear_local_git_env cd "$1" && { git describe "$2" 2>/dev/null || git describe --tags "$2" 2>/dev/null || @@ -548,26 +784,31 @@ cmd_summary() { test $summary_limit = 0 && return - if rev=$(git rev-parse -q --verify "$1^0") + if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"}) then head=$rev - shift + test $# = 0 || shift + elif test -z "$1" -o "$1" = "HEAD" + then + # before the first commit: compare with an empty tree + head=$(git hash-object -w -t tree --stdin </dev/null) + test -z "$1" || shift else - head=HEAD + head="HEAD" fi if [ -n "$files" ] then test -n "$cached" && - die "--cached cannot be used with --files" + die "$(gettext "The --cached option cannot be used with the --files option")" diff_cmd=diff-files head= fi cd_to_toplevel # Get modified modules cared by user - modules=$(git $diff_cmd $cached --raw $head -- "$@" | - egrep '^:([0-7]* )?160000' | + modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" | + sane_egrep '^:([0-7]* )?160000' | while read mod_src mod_dst sha1_src sha1_dst status name do # Always show modules deleted or type-changed (blob<->module) @@ -580,8 +821,8 @@ cmd_summary() { test -z "$modules" && return - git $diff_cmd $cached --raw $head -- $modules | - egrep '^:([0-7]* )?160000' | + git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules | + sane_egrep '^:([0-7]* )?160000' | cut -c2- | while read mod_src mod_dst sha1_src sha1_dst status name do @@ -599,7 +840,7 @@ cmd_summary() { ;; # removed *) # unexpected type - echo >&2 "unexpected mode $mod_dst" + eval_gettextln "unexpected mode \$mod_dst" >&2 continue ;; esac fi @@ -617,13 +858,13 @@ cmd_summary() { total_commits= case "$missing_src,$missing_dst" in t,) - errmsg=" Warn: $name doesn't contain commit $sha1_src" + errmsg="$(eval_gettext " Warn: \$name doesn't contain commit \$sha1_src")" ;; ,t) - errmsg=" Warn: $name doesn't contain commit $sha1_dst" + errmsg="$(eval_gettext " Warn: \$name doesn't contain commit \$sha1_dst")" ;; t,t) - errmsg=" Warn: $name doesn't contain commits $sha1_src and $sha1_dst" + errmsg="$(eval_gettext " Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")" ;; *) errmsg= @@ -638,7 +879,7 @@ cmd_summary() { range=$sha1_dst fi GIT_DIR="$name/.git" \ - git log --pretty=oneline --first-parent $range | wc -l + git rev-list --first-parent $range -- | wc -l ) total_commits=" ($(($total_commits + 0)))" ;; @@ -648,11 +889,13 @@ cmd_summary() { sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7) if test $status = T then + blob="$(gettext "blob")" + submodule="$(gettext "submodule")" if test $mod_dst = 160000 then - echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:" + echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:" else - echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:" + echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:" fi else echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:" @@ -683,7 +926,11 @@ cmd_summary() { echo done | if test -n "$for_status"; then - echo "# Modified submodules:" + if [ -n "$files" ]; then + gettextln "# Submodules changed but not updated:" + else + gettextln "# Submodule changes to be committed:" + fi echo "#" sed -e 's|^|# |' -e 's|^# $|#|' else @@ -703,7 +950,6 @@ cmd_summary() { cmd_status() { # parse $args after "submodule ... status". - orig_args="$@" while test $# -ne 0 do case "$1" in @@ -731,25 +977,31 @@ cmd_status() done module_list "$@" | - while read mode sha1 stage path + while read mode sha1 stage sm_path do - name=$(module_name "$path") || exit + die_if_unmatched "$mode" + name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) - displaypath="$prefix$path" - if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git + displaypath="$prefix$sm_path" + if test "$stage" = U + then + say "U$sha1 $displaypath" + continue + fi + if test -z "$url" || ! test -d "$sm_path"/.git -o -f "$sm_path"/.git then say "-$sha1 $displaypath" continue; fi - set_name_rev "$path" "$sha1" - if git diff-files --quiet -- "$path" + set_name_rev "$sm_path" "$sha1" + if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then say " $sha1 $displaypath$revname" else if test -z "$cached" then - sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD) - set_name_rev "$path" "$sha1" + sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD) + set_name_rev "$sm_path" "$sha1" fi say "+$sha1 $displaypath$revname" fi @@ -758,11 +1010,11 @@ cmd_status() then ( prefix="$displaypath/" - unset GIT_DIR - cd "$path" && - cmd_status $orig_args + clear_local_git_env + cd "$sm_path" && + eval cmd_status ) || - die "Failed to recurse into submodule path '$path'" + die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")" fi done } @@ -780,6 +1032,10 @@ cmd_sync() GIT_QUIET=1 shift ;; + --recursive) + recursive=1 + shift + ;; --) shift break @@ -794,27 +1050,51 @@ cmd_sync() done cd_to_toplevel module_list "$@" | - while read mode sha1 stage path + while read mode sha1 stage sm_path do - name=$(module_name "$path") + 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 ./*|../*) - url=$(resolve_relative_url "$url") || exit + # 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 - if test -e "$path"/.git + if git config "submodule.$name.url" >/dev/null 2>/dev/null then - ( - unset GIT_DIR - cd "$path" - remote=$(get_default_remote) - say "Synchronizing submodule url for '$name'" - git config remote."$remote".url "$url" - ) + say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")" + git config submodule."$name".url "$super_config_url" + + 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" + + if test -n "$recursive" + then + prefix="$prefix$sm_path/" + eval cmd_sync + fi + ) + fi fi done } @@ -859,7 +1139,15 @@ do done # No command word defaults to "status" -test -n "$command" || command=status +if test -z "$command" +then + if test $# = 0 + then + command=status + else + usage + fi +fi # "-b branch" is accepted only by "add" if test -n "$branch" && test "$command" != add |
