Merge branch 'ps/submodule-sanitize-path-upon-add' into maint
authorJunio C Hamano <gitster@pobox.com>
Thu, 5 Mar 2015 21:13:09 +0000 (13:13 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 5 Mar 2015 21:13:10 +0000 (13:13 -0800)
"git submodule add" failed to squash "path/to/././submodule" to
"path/to/submodule".

* ps/submodule-sanitize-path-upon-add:
  git-submodule.sh: fix '/././' path normalization

1  2 
git-submodule.sh
t/t7400-submodule-basic.sh

diff --combined git-submodule.sh
@@@ -9,7 -9,7 +9,7 @@@ USAGE="[--quiet] add [-b <branch>] [-f|
     or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
     or: $dashless [--quiet] init [--] [<path>...]
     or: $dashless [--quiet] deinit [-f|--force] [--] <path>...
 -   or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
 +   or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]
     or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
     or: $dashless [--quiet] foreach [--recursive] <command>
     or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
@@@ -156,7 -156,7 +156,7 @@@ module_list(
                git ls-files -z --error-unmatch --stage -- "$@" ||
                echo "unmatched pathspec exists"
        ) |
 -      perl -e '
 +      @@PERL@@ -e '
        my %unmerged = ();
        my ($null_sha1) = ("0" x 40);
        my @out = ();
@@@ -235,18 -235,12 +235,18 @@@ module_name(
                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'")"
 -      echo "$name"
 +      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,
@@@ -291,6 -285,9 +291,6 @@@ module_clone(
        # 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")"
        b=${b%/}
  
        # Turn each leading "*/" component into "../"
 -      rel=$(echo $b | sed -e 's|[^/][^/]*|..|g')
 -      echo "gitdir: $rel/$a" >"$sm_path/.git"
 +      rel=$(printf '%s\n' "$b" | sed -e 's|[^/][^/]*|..|g')
 +      printf '%s\n' "gitdir: $rel/$a" >"$sm_path/.git"
  
 -      rel=$(echo $a | sed -e 's|[^/][^/]*|..|g')
 +      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")
  }
  
@@@ -389,11 -386,11 +389,11 @@@ cmd_add(
        sm_path=$2
  
        if test -z "$sm_path"; then
 -              sm_path=$(echo "$repo" |
 +              sm_path=$(printf '%s\n' "$repo" |
                        sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
        fi
  
 -      if test -z "$repo" -o -z "$sm_path"; then
 +      if test -z "$repo" || test -z "$sm_path"; then
                usage
        fi
  
                sed -e '
                        s|//*|/|g
                        s|^\(\./\)*||
-                       s|/\./|/|g
+                       s|/\(\./\)*|/|g
                        :start
                        s|\([^/]*\)/\.\./||
                        tstart
@@@ -450,7 -447,7 +450,7 @@@ Use -f if you really want to add it." >
        # perhaps the path exists and is already a git repo, else clone it
        if test -e "$sm_path"
        then
 -              if test -d "$sm_path"/.git -o -f "$sm_path"/.git
 +              if test -d "$sm_path"/.git || test -f "$sm_path"/.git
                then
                        eval_gettextln "Adding existing repo at '\$sm_path' to the index"
                else
@@@ -548,12 -545,7 +548,12 @@@ cmd_foreach(
                                sm_path=$(relative_path "$sm_path") &&
                                # we make $path available to scripts ...
                                path=$sm_path &&
 -                              eval "$@" &&
 +                              if test $# -eq 1
 +                              then
 +                                      eval "$1"
 +                              else
 +                                      "$@"
 +                              fi &&
                                if test -n "$recursive"
                                then
                                        cmd_foreach "--recursive" "$@"
@@@ -625,7 -617,7 +625,7 @@@ cmd_init(
                   test -z "$(git config submodule."$name".update)"
                then
                        case "$upd" in
 -                      rebase | merge | none)
 +                      checkout | rebase | merge | none)
                                ;; # known modes of updating
                        *)
                                echo >&2 "warning: unknown update mode '$upd' suggested for submodule '$name'"
@@@ -724,6 -716,7 +724,6 @@@ cmd_deinit(
  cmd_update()
  {
        # parse $args after "submodule ... update".
 -      orig_flags=
        while test $# -ne 0
        do
                case "$1" in
                --reference)
                        case "$2" in '') usage ;; esac
                        reference="--reference=$2"
 -                      orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
                        shift
                        ;;
                --reference=*)
                        break
                        ;;
                esac
 -              orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
                shift
        done
  
                        update_module=$update
                else
                        update_module=$(git config submodule."$name".update)
 +                      if test -z "$update_module"
 +                      then
 +                              update_module="checkout"
 +                      fi
                fi
  
                displaypath=$(relative_path "$prefix$sm_path")
@@@ -832,7 -823,7 +832,7 @@@ Maybe you want to use 'update --init'?"
                        continue
                fi
  
 -              if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
 +              if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
                then
                        module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
                        cloned_modules="$cloned_modules;$name"
                        die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")"
                fi
  
 -              if test "$subsha1" != "$sha1" -o -n "$force"
 +              if test "$subsha1" != "$sha1" || test -n "$force"
                then
                        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"
 +                      if test -z "$subsha1" && test -z "$force"
                        then
                                subforce="-f"
                        fi
                        case ";$cloned_modules;" in
                        *";$name;"*)
                                # then there is no local change to integrate
 -                              update_module= ;;
 +                              update_module=checkout ;;
                        esac
  
                        must_die_on_failure=
                        case "$update_module" in
 +                      checkout)
 +                              command="git checkout $subforce -q"
 +                              die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
 +                              say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
 +                              ;;
                        rebase)
                                command="git rebase"
                                die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$displaypath'")"
                                must_die_on_failure=yes
                                ;;
                        *)
 -                              command="git checkout $subforce -q"
 -                              die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
 -                              say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
 -                              ;;
 +                              die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
                        esac
  
                        if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
                                prefix="$prefix$sm_path/"
                                clear_local_git_env
                                cd "$sm_path" &&
 -                              eval cmd_update "$orig_flags"
 +                              eval cmd_update
                        )
                        res=$?
                        if test $res -gt 0
@@@ -1031,7 -1020,7 +1031,7 @@@ cmd_summary() 
        then
                head=$rev
                test $# = 0 || shift
 -      elif test -z "$1" -o "$1" = "HEAD"
 +      elif test -z "$1" || test "$1" = "HEAD"
        then
                # before the first commit: compare with an empty tree
                head=$(git hash-object -w -t tree --stdin </dev/null)
                while read mod_src mod_dst sha1_src sha1_dst status sm_path
                do
                        # Always show modules deleted or type-changed (blob<->module)
 -                      test $status = D -o $status = T && echo "$sm_path" && continue
 +                      if test "$status" = D || test "$status" = T
 +                      then
 +                              printf '%s\n' "$sm_path"
 +                              continue
 +                      fi
                        # Respect the ignore setting for --for-status.
                        if test -n "$for_status"
                        then
                                name=$(module_name "$sm_path")
                                ignore_config=$(get_submodule_config "$name" ignore none)
 -                              test $status != A -a $ignore_config = all && continue
 +                              test $status != A && test $ignore_config = all && continue
                        fi
                        # Also show added or modified modules which are checked out
                        GIT_DIR="$sm_path/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
 -                      echo "$sm_path"
 +                      printf '%s\n' "$sm_path"
                done
        )
  
                *)
                        errmsg=
                        total_commits=$(
 -                      if test $mod_src = 160000 -a $mod_dst = 160000
 +                      if test $mod_src = 160000 && test $mod_dst = 160000
                        then
                                range="$sha1_src...$sha1_dst"
                        elif test $mod_src = 160000
                        # i.e. deleted or changed to blob
                        test $mod_dst = 160000 && echo "$errmsg"
                else
 -                      if test $mod_src = 160000 -a $mod_dst = 160000
 +                      if test $mod_src = 160000 && test $mod_dst = 160000
                        then
                                limit=
                                test $summary_limit -gt 0 && limit="-$summary_limit"
@@@ -1234,11 -1219,7 +1234,11 @@@ cmd_status(
                        say "U$sha1 $displaypath"
                        continue
                fi
 -              if test -z "$url" || ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
 +              if test -z "$url" ||
 +              {
 +                      ! test -d "$sm_path"/.git &&
 +                      ! test -f "$sm_path"/.git
 +              }
                then
                        say "-$sha1 $displaypath"
                        continue;
@@@ -1311,7 -1292,7 +1311,7 @@@ cmd_sync(
                ./*|../*)
                        # rewrite foo/bar as ../.. to find path from
                        # submodule work tree to superproject work tree
 -                      up_path="$(echo "$sm_path" | sed "s/[^/][^/]*/../g")" &&
 +                      up_path="$(printf '%s\n' "$sm_path" | sed "s/[^/][^/]*/../g")" &&
                        # guarantee a trailing /
                        up_path=${up_path%/}/ &&
                        # path from submodule work tree to submodule origin repo
@@@ -1407,7 -1388,7 +1407,7 @@@ the
  fi
  
  # "--cached" is accepted only by "status" and "summary"
 -if test -n "$cached" && test "$command" != status -a "$command" != summary
 +if test -n "$cached" && test "$command" != status && test "$command" != summary
  then
        usage
  fi
@@@ -170,6 -170,23 +170,23 @@@ test_expect_success 'submodule add wit
        test_cmp expect head &&
        test_cmp empty untracked
  '
+ test_expect_success 'submodule add with /././ in path' '
+       echo "refs/heads/master" >expect &&
+       >empty &&
+       (
+               cd addtest &&
+               git submodule add "$submodurl" dotslashdotsubmod/././frotz/./ &&
+               git submodule init
+       ) &&
+       rm -f heads head untracked &&
+       inspect addtest/dotslashdotsubmod/frotz ../../.. &&
+       test_cmp expect heads &&
+       test_cmp expect head &&
+       test_cmp empty untracked
+ '
  
  test_expect_success 'submodule add with // in path' '
        echo "refs/heads/master" >expect &&
@@@ -249,7 -266,8 +266,7 @@@ test_expect_success 'submodule add in s
  '
  
  test_expect_success 'setup - add an example entry to .gitmodules' '
 -      GIT_CONFIG=.gitmodules \
 -      git config submodule.example.url git://example.com/init.git
 +      git config --file=.gitmodules submodule.example.url git://example.com/init.git
  '
  
  test_expect_success 'status should fail for unmapped paths' '
@@@ -263,7 -281,7 +280,7 @@@ test_expect_success 'setup - map path i
        path = init
  EOF
  
 -      GIT_CONFIG=.gitmodules git config submodule.example.path init &&
 +      git config --file=.gitmodules submodule.example.path init &&
  
        test_cmp expect .gitmodules
  '