X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=git-submodule.sh;h=2365149d0b920ece4d076dafbebf23fef6888df1;hb=ee5a57bc5c5641c30bc626892ddc31ddd18ed7ad;hp=3d94a14079ccf745b3cf3d5d5a4e471f9b5542bc;hpb=189627cd588c2937de7b655595b8ed777f445c85;p=platform%2Fupstream%2Fgit.git diff --git a/git-submodule.sh b/git-submodule.sh index 3d94a14..2365149 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -1,17 +1,17 @@ #!/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] [-f|--force] [--reference ] [--] [] +USAGE="[--quiet] add [-b ] [-f|--force] [--name ] [--reference ] [--] [] or: $dashless [--quiet] status [--cached] [--recursive] [--] [...] or: $dashless [--quiet] init [--] [...] or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference ] [--merge] [--recursive] [--] [...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit ] [commit] [--] [...] or: $dashless [--quiet] foreach [--recursive] - or: $dashless [--quiet] sync [--] [...]" + or: $dashless [--quiet] sync [--recursive] [--] [...]" OPTIONS_SPEC= . git-sh-setup . git-sh-i18n @@ -29,8 +29,24 @@ 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) @@ -39,6 +55,21 @@ resolve_relative_url () 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 @@ -53,7 +84,12 @@ resolve_relative_url () sep=: ;; *) - die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")" + if test -z "$is_relative" || test "." = "$remoteurl" + then + die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")" + else + remoteurl=. + fi ;; esac ;; @@ -64,7 +100,8 @@ resolve_relative_url () break;; esac done - echo "$remoteurl$sep${url%/}" + remoteurl="$remoteurl$sep${url%/}" + echo "${is_relative:+${up_path}}${remoteurl#./}" } # @@ -73,26 +110,48 @@ resolve_relative_url () # module_list() { - git ls-files --error-unmatch --stage -- "$@" | + ( + 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 () { + 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}++) { - print "$mode $null_sha1 U\t$path\n"; + push @out, "$mode $null_sha1 U\t$path\n"; } next; } - print "$_\n"; + push @out, "$_\n"; + } + if ($unmatched) { + print "#unmatched\n"; + } else { + print for (@out); } ' } +die_if_unmatched () +{ + if test "$1" = "#unmatched" + then + exit 1 + fi +} + # # Map submodule path to submodule name # @@ -101,11 +160,12 @@ module_list() module_name() { # Do we have "submodule..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 '\$path'")" + die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")" echo "$name" } @@ -119,9 +179,10 @@ module_name() # module_clone() { - path=$1 - url=$2 - reference="$3" + sm_path=$1 + name=$2 + url=$3 + reference="$4" quiet= if test -n "$GIT_QUIET" then @@ -130,8 +191,6 @@ module_clone() gitdir= gitdir_base= - name=$(module_name "$path" 2>/dev/null) - test -n "$name" || name="$path" base_name=$(dirname "$name") gitdir=$(git rev-parse --git-dir) @@ -140,17 +199,22 @@ module_clone() if test -d "$gitdir" then - mkdir -p "$path" + mkdir -p "$sm_path" rm -f "$gitdir/index" else mkdir -p "$gitdir_base" - git clone $quiet -n ${reference:+"$reference"} \ - --separate-git-dir "$gitdir" "$url" "$path" || - die "$(eval_gettext "Clone of '\$url' into submodule path '\$path' failed")" + ( + 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 - a=$(cd "$gitdir" && pwd)/ - b=$(cd "$path" && pwd)/ + # 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 @@ -169,10 +233,10 @@ module_clone() # Turn each leading "*/" component into "../" rel=$(echo $b | sed -e 's|[^/][^/]*|..|g') - echo "gitdir: $rel/$a" >"$path/.git" + echo "gitdir: $rel/$a" >"$sm_path/.git" rel=$(echo $a | sed -e 's|[^/][^/]*|..|g') - (clear_local_git_env; cd "$path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b") + (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b") } # @@ -206,6 +270,10 @@ cmd_add() ;; --reference=*) reference="$1" + ;; + --name) + case "$2" in '') usage ;; esac + custom_name=$2 shift ;; --) @@ -223,14 +291,14 @@ cmd_add() done repo=$1 - path=$2 + sm_path=$2 - if test -z "$path"; then - path=$(echo "$repo" | + 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 "$path"; then + if test -z "$repo" -o -z "$sm_path"; then usage fi @@ -251,7 +319,7 @@ cmd_add() # normalize path: # multiple //; leading ./; /./; /../; trailing / - path=$(printf '%s/\n' "$path" | + sm_path=$(printf '%s/\n' "$sm_path" | sed -e ' s|//*|/|g s|^\(\./\)*|| @@ -261,49 +329,69 @@ cmd_add() tstart s|/*$|| ') - git ls-files --error-unmatch "$path" > /dev/null 2>&1 && - die "$(eval_gettext "'\$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 "$path" > /dev/null 2>&1 + 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: -\$path +\$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 - eval_gettextln "Adding existing repo at '\$path' to the index" + eval_gettextln "Adding existing repo at '\$sm_path' to the index" else - die "$(eval_gettext "'\$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 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 ( clear_local_git_env - cd "$path" && + 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" ;; esac - ) || die "$(eval_gettext "Unable to checkout submodule '\$path'")" + ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")" fi - git config submodule."$path".url "$realrepo" + git config submodule."$sm_name".url "$realrepo" - git add $force "$path" || - die "$(eval_gettext "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 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 '\$path'")" + die "$(eval_gettext "Failed to register submodule '\$sm_path'")" } # @@ -341,23 +429,26 @@ cmd_foreach() 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 "$(eval_gettext "Entering '\$prefix\$path'")" - name=$(module_name "$path") + say "$(eval_gettext "Entering '\$prefix\$sm_path'")" + name=$(module_name "$sm_path") ( - prefix="$prefix$path/" + prefix="$prefix$sm_path/" clear_local_git_env - cd "$path" && + # we make $path available to scripts ... + path=$sm_path + cd "$sm_path" && eval "$@" && if test -n "$recursive" then cmd_foreach "--recursive" "$@" fi ) <&3 3<&- || - die "$(eval_gettext "Stopping at '\$path'; script returned non-zero status.")" + die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")" fi done } @@ -391,15 +482,17 @@ 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 + die_if_unmatched "$mode" + name=$(module_name "$sm_path") || exit + + # 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 '\$path' in .gitmodules")" + die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")" # Possibly a url relative to parent case "$url" in @@ -408,7 +501,9 @@ cmd_init() ;; esac git config submodule."$name".url "$url" || - die "$(eval_gettext "Failed to register url for submodule path '\$path'")" + die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")" + + say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")" fi # Copy "update" setting when it is not set yet @@ -416,9 +511,7 @@ cmd_init() test -z "$upd" || test -n "$(git config submodule."$name".update)" || git config submodule."$name".update "$upd" || - die "$(eval_gettext "Failed to register update mode for submodule path '\$path'")" - - say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$path'")" + die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")" done } @@ -490,14 +583,15 @@ cmd_update() cloned_modules= module_list "$@" | { err= - while read mode sha1 stage path + while read mode sha1 stage sm_path do + die_if_unmatched "$mode" if test "$stage" = U then - echo >&2 "Skipping unmerged submodule $path" + echo >&2 "Skipping unmerged submodule $sm_path" continue fi - name=$(module_name "$path") || exit + name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) if ! test -z "$update" then @@ -508,7 +602,7 @@ cmd_update() if test "$update_module" = "none" then - echo "Skipping submodule '$path'" + echo "Skipping submodule '$sm_path'" continue fi @@ -517,23 +611,23 @@ cmd_update() # Only mention uninitialized submodules when its # path have been specified test "$#" != "0" && - say "$(eval_gettext "Submodule path '\$path' not initialized + 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=$(clear_local_git_env; cd "$path" && + subsha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD) || - die "$(eval_gettext "Unable to find current revision in submodule path '\$path'")" + 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 subforce=$force # If we don't already have a -f flag and the submodule has never been checked out @@ -546,10 +640,10 @@ 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 "$path" && + (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 '\$path'")" + die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" fi # Is this something we just cloned? @@ -563,24 +657,24 @@ Maybe you want to use 'update --init'?")" case "$update_module" in rebase) command="git rebase" - die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$path'")" - say_msg="$(eval_gettext "Submodule path '\$path': rebased into '\$sha1'")" + 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" - die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$path'")" - say_msg="$(eval_gettext "Submodule path '\$path': merged in '\$sha1'")" + 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 $subforce -q" - die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$path'")" - say_msg="$(eval_gettext "Submodule path '\$path': checked out '\$sha1'")" + 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 - if (clear_local_git_env; cd "$path" && $command "$sha1") + if (clear_local_git_env; cd "$sm_path" && $command "$sha1") then say "$say_msg" elif test -n "$must_die_on_failure" @@ -594,11 +688,11 @@ Maybe you want to use 'update --init'?")" if test -n "$recursive" then - (clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags") + (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 '\$path'")" + die_msg="$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")" if test $res -eq 1 then err="${err};$die_msg" @@ -706,7 +800,7 @@ cmd_summary() { if [ -n "$files" ] then test -n "$cached" && - die "$(gettext -- "--cached cannot be used with --files")" + die "$(gettext "The --cached option cannot be used with the --files option")" diff_cmd=diff-files head= fi @@ -856,7 +950,6 @@ cmd_summary() { cmd_status() { # parse $args after "submodule ... status". - orig_flags= while test $# -ne 0 do case "$1" in @@ -880,35 +973,35 @@ cmd_status() break ;; esac - orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")" shift 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" + displaypath="$prefix$sm_path" if test "$stage" = U then say "U$sha1 $displaypath" continue fi - if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git + 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 --ignore-submodules=dirty --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=$(clear_local_git_env; 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 @@ -918,10 +1011,10 @@ cmd_status() ( prefix="$displaypath/" clear_local_git_env - cd "$path" && - eval cmd_status "$orig_args" + cd "$sm_path" && + eval cmd_status ) || - die "$(eval_gettext "Failed to recurse into submodule path '\$path'")" + die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")" fi done } @@ -939,6 +1032,10 @@ cmd_sync() GIT_QUIET=1 shift ;; + --recursive) + recursive=1 + shift + ;; --) shift break @@ -953,30 +1050,49 @@ 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 git config "submodule.$name.url" >/dev/null 2>/dev/null then - say "$(eval_gettext "Synchronizing submodule url for '\$name'")" - git config submodule."$name".url "$url" + say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")" + git config submodule."$name".url "$super_config_url" - if test -e "$path"/.git + if test -e "$sm_path"/.git then ( clear_local_git_env - cd "$path" + cd "$sm_path" remote=$(get_default_remote) - git config remote."$remote".url "$url" + git config remote."$remote".url "$sub_origin_url" + + if test -n "$recursive" + then + prefix="$prefix$sm_path/" + eval cmd_sync + fi ) fi fi @@ -1023,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