Merge branch 'jl/submodule-add-by-name'
authorJeff King <peff@peff.net>
Mon, 29 Oct 2012 08:12:12 +0000 (04:12 -0400)
committerJeff King <peff@peff.net>
Mon, 29 Oct 2012 08:12:12 +0000 (04:12 -0400)
If you remove a submodule, in order to keep the repository so that
"git checkout" to an older commit in the superproject history can
resurrect the submodule, the real repository will stay in $GIT_DIR
of the superproject. A later "git submodule add $path" to add a
different submodule at the same path will fail. Diagnose this case
a bit better, and if the user really wants to add an unrelated
submodule at the same path, give the "--name" option to give it a
place in $GIT_DIR of the superproject that does not conflict with
the original submodule.

* jl/submodule-add-by-name:
submodule add: Fail when .git/modules/<name> already exists unless forced
Teach "git submodule add" the --name option

1  2 
Documentation/git-submodule.txt
git-submodule.sh
t/t7400-submodule-basic.sh
index b4683bba1bc0ea6561a2378c663cf431776a3f7a,22efca01b75c452f927735a1ea6b91314faec028..1d6527ab9f46fb0eb5d52ef335c1c4b4c377a72b
@@@ -9,7 -9,7 +9,7 @@@ git-submodule - Initialize, update or i
  SYNOPSIS
  --------
  [verse]
- 'git submodule' [--quiet] add [-b branch] [-f|--force]
+ 'git submodule' [--quiet] add [-b branch] [-f|--force] [--name <name>]
              [--reference <repository>] [--] <repository> [<path>]
  'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
  'git submodule' [--quiet] init [--] [<path>...]
@@@ -112,6 -112,7 +112,6 @@@ status:
        initialized, `+` if the currently checked out submodule commit
        does not match the SHA-1 found in the index of the containing
        repository and `U` if the submodule has merge conflicts.
 -      This command is the default command for 'git submodule'.
  +
  If `--recursive` is specified, this command will recurse into nested
  submodules, and show their status as well.
@@@ -265,6 -266,11 +265,11 @@@ OPTION
        Initialize all submodules for which "git submodule init" has not been
        called so far before updating.
  
+ --name::
+       This option is only valid for the add command. It sets the submodule's
+       name to the given string instead of defaulting to its path. The name
+       must be valid as a directory name and may not end with a '/'.
  --reference <repository>::
        This option is only valid for add and update commands.  These
        commands sometimes need to clone a remote repository. In this case,
diff --combined git-submodule.sh
index ab6b1107b6090494f192f361471ed5748ffa7dc1,e8112c8d1a79d3ad654041d1e61d168fb3992b06..9e1e1f44106ca659ee705b4f66dc5cd4997ec907
@@@ -5,7 -5,7 +5,7 @@@
  # Copyright (c) 2007 Lars Hjemli
  
  dashless=$(basename "$0" | sed -e 's/-/ /')
- USAGE="[--quiet] add [-b branch] [-f|--force] [--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] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
@@@ -29,6 -29,7 +29,7 @@@ files
  nofetch=
  update=
  prefix=
+ custom_name=
  
  # The function takes at most 2 arguments. The first argument is the
  # URL that navigates to the submodule origin repo. When relative, this URL
@@@ -179,8 -180,9 +180,9 @@@ module_name(
  module_clone()
  {
        sm_path=$1
-       url=$2
-       reference="$3"
+       name=$2
+       url=$3
+       reference="$4"
        quiet=
        if test -n "$GIT_QUIET"
        then
  
        gitdir=
        gitdir_base=
-       name=$(module_name "$sm_path" 2>/dev/null)
-       test -n "$name" || name="$sm_path"
        base_name=$(dirname "$name")
  
        gitdir=$(git rev-parse --git-dir)
@@@ -272,6 -272,11 +272,11 @@@ cmd_add(
                        reference="$1"
                        shift
                        ;;
+               --name)
+                       case "$2" in '') usage ;; esac
+                       custom_name=$2
+                       shift
+                       ;;
                --)
                        shift
                        break
@@@ -336,6 -341,13 +341,13 @@@ Use -f if you really want to add it." >
                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 "$sm_path"
        then
                fi
  
        else
-               module_clone "$sm_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 "$sm_path" &&
                        esac
                ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
        fi
-       git config submodule."$sm_path".url "$realrepo"
+       git config submodule."$sm_name".url "$realrepo"
  
        git add $force "$sm_path" ||
        die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
  
-       git config -f .gitmodules submodule."$sm_path".path "$sm_path" &&
-       git config -f .gitmodules submodule."$sm_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 '\$sm_path'")"
  }
@@@ -594,7 -619,7 +619,7 @@@ Maybe you want to use 'update --init'?"
  
                if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
-                       module_clone "$sm_path" "$url" "$reference"|| exit
+                       module_clone "$sm_path" "$name" "$url" "$reference" || exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
  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
index 53970374913eaa6f1b5921b14b5f25043974e3a4,f1a94f7ca128655de04df01a8b3479b0b95bc18a..de7d45352e001d3c1b2c7f24361dc38dd9e0ea0a
@@@ -438,8 -438,8 +438,8 @@@ test_expect_success 'moving to a commi
        git checkout second
  '
  
 -test_expect_success 'submodule <invalid-path> warns' '
 -      test_failure_with_unknown_submodule
 +test_expect_success 'submodule <invalid-subcommand> fails' '
 +      test_must_fail git submodule no-such-subcommand
  '
  
  test_expect_success 'add submodules without specifying an explicit path' '
@@@ -681,4 -681,79 +681,79 @@@ test_expect_success 'moving the superpr
        )
  '
  
+ test_expect_success 'submodule add --name allows to replace a submodule with another at the same path' '
+       (
+               cd addtest2 &&
+               (
+                       cd repo &&
+                       echo "$submodurl/repo" >expect &&
+                       git config remote.origin.url >actual &&
+                       test_cmp expect actual &&
+                       echo "gitdir: ../.git/modules/repo" >expect &&
+                       test_cmp expect .git
+               ) &&
+               rm -rf repo &&
+               git rm repo &&
+               git submodule add -q --name repo_new "$submodurl/bare.git" repo >actual &&
+               test ! -s actual &&
+               echo "gitdir: ../.git/modules/submod" >expect &&
+               test_cmp expect submod/.git &&
+               (
+                       cd repo &&
+                       echo "$submodurl/bare.git" >expect &&
+                       git config remote.origin.url >actual &&
+                       test_cmp expect actual &&
+                       echo "gitdir: ../.git/modules/repo_new" >expect &&
+                       test_cmp expect .git
+               ) &&
+               echo "repo" >expect &&
+               git config -f .gitmodules submodule.repo.path >actual &&
+               test_cmp expect actual &&
+               git config -f .gitmodules submodule.repo_new.path >actual &&
+               test_cmp expect actual&&
+               echo "$submodurl/repo" >expect &&
+               git config -f .gitmodules submodule.repo.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/bare.git" >expect &&
+               git config -f .gitmodules submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/repo" >expect &&
+               git config submodule.repo.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/bare.git" >expect &&
+               git config submodule.repo_new.url >actual &&
+               test_cmp expect actual
+       )
+ '
+ test_expect_success 'submodule add with an existing name fails unless forced' '
+       (
+               cd addtest2 &&
+               rm -rf repo &&
+               git rm repo &&
+               test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo &&
+               test ! -d repo &&
+               echo "repo" >expect &&
+               git config -f .gitmodules submodule.repo_new.path >actual &&
+               test_cmp expect actual&&
+               echo "$submodurl/bare.git" >expect &&
+               git config -f .gitmodules submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/bare.git" >expect &&
+               git config submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               git submodule add -f -q --name repo_new "$submodurl/repo.git" repo &&
+               test -d repo &&
+               echo "repo" >expect &&
+               git config -f .gitmodules submodule.repo_new.path >actual &&
+               test_cmp expect actual&&
+               echo "$submodurl/repo.git" >expect &&
+               git config -f .gitmodules submodule.repo_new.url >actual &&
+               test_cmp expect actual &&
+               echo "$submodurl/repo.git" >expect &&
+               git config submodule.repo_new.url >actual &&
+               test_cmp expect actual
+       )
+ '
  test_done