diff options
-rw-r--r-- | Documentation/git-submodule.txt | 46 | ||||
-rw-r--r-- | Documentation/gitmodules.txt | 4 | ||||
-rwxr-xr-x | git-submodule.sh | 89 | ||||
-rwxr-xr-x | t/t7406-submodule-update.sh | 38 |
4 files changed, 135 insertions, 42 deletions
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index bfef8a0c62..21cb59a6d6 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -15,8 +15,8 @@ SYNOPSIS 'git submodule' [--quiet] init [--] [<path>...] 'git submodule' [--quiet] deinit [-f|--force] [--] <path>... 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch] - [-f|--force] [--rebase] [--reference <repository>] [--depth <depth>] - [--merge] [--recursive] [--] [<path>...] + [-f|--force] [--rebase|--merge|--checkout] [--reference <repository>] + [--depth <depth>] [--recursive] [--] [<path>...] 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>] [commit] [--] [<path>...] 'git submodule' [--quiet] foreach [--recursive] <command> @@ -155,13 +155,31 @@ it contains local modifications. update:: Update the registered submodules, i.e. clone missing submodules and - checkout the commit specified in the index of the containing repository. - This will make the submodules HEAD be detached unless `--rebase` or - `--merge` is specified or the key `submodule.$name.update` is set to - `rebase`, `merge` or `none`. `none` can be overridden by specifying - `--checkout`. Setting the key `submodule.$name.update` to `!command` - will cause `command` to be run. `command` can be any arbitrary shell - command that takes a single argument, namely the sha1 to update to. + checkout the commit specified in the index of the containing + repository. The update mode defaults to `checkout`, but can be + configured with the `submodule.<name>.update` setting or the + `--rebase`, `--merge`, or `--checkout` options. ++ +For updates that clone missing submodules, checkout-mode updates will +create submodules with detached HEADs; all other modes will create +submodules with a local branch named after `submodule.<path>.branch`. ++ +For updates that do not clone missing submodules, the submodule's HEAD +is only touched when the remote reference does not match the +submodule's HEAD (for none-mode updates, the submodule is never +touched). The remote reference is usually the gitlinked commit from +the superproject's tree, but with `--remote` it is the upstream +subproject's `submodule.<name>.branch`. This remote reference is +integrated with the submodule's HEAD using the specified update mode. +For checkout-mode updates, that will result in a detached HEAD. For +rebase- and merge-mode updates, the commit referenced by the +submodule's HEAD may change, but the symbolic reference will remain +unchanged (i.e. checked-out branches will still be checked-out +branches, and detached HEADs will still be detached HEADs). If none +of the builtin modes fit your needs, set `submodule.<name>.update` to +`!command` to configure a custom integration command. `command` can +be any arbitrary shell command that takes a single argument, namely +the sha1 to update to. + If the submodule is not yet initialized, and you just want to use the setting as stored in .gitmodules, you can automatically initialize the @@ -281,6 +299,16 @@ In order to ensure a current tracking branch state, `update --remote` fetches the submodule's remote repository before calculating the SHA-1. If you don't want to fetch, you should use `submodule update --remote --no-fetch`. ++ +Use this option to integrate changes from the upstream subproject with +your submodule's current HEAD. Alternatively, you can run `git pull` +from the submodule, which is equivalent except for the remote branch +name: `update --remote` uses the default upstream repository and +`submodule.<name>.branch`, while `git pull` uses the submodule's +`branch.<name>.merge`. Prefer `submodule.<name>.branch` if you want +to distribute the default upstream branch with the superproject and +`branch.<name>.merge` if you want a more native feel while working in +the submodule itself. -N:: --no-fetch:: diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index 347a9f76ee..f539e3f66a 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -55,6 +55,10 @@ submodule.<name>.branch:: A remote branch name for tracking updates in the upstream submodule. If the option is not specified, it defaults to 'master'. See the `--remote` documentation in linkgit:git-submodule[1] for details. ++ +This branch name is also used for the local branch created by +non-checkout cloning updates. See the `update` documentation in +linkgit:git-submodule[1] for details. submodule.<name>.fetchRecurseSubmodules:: This option can be used to control recursive fetching of this diff --git a/git-submodule.sh b/git-submodule.sh index 4a30087768..a33f68d27c 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -241,6 +241,15 @@ module_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) +# $6 = (remote-tracking) starting point for the local branch (empty for HEAD) +# $7 = local branch to create (empty for a detached HEAD, unless $6 is +# also empty, in which case the local branch is left unchanged) +# # 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, @@ -253,6 +262,8 @@ module_clone() url=$3 reference="$4" depth="$5" + start_point="$6" + local_branch="$7" quiet= if test -n "$GIT_QUIET" then @@ -306,7 +317,16 @@ module_clone() 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") + ( + clear_local_git_env + cd "$sm_path" && + GIT_WORK_TREE=. git config core.worktree "$rel/$b" && + # ash fails to wordsplit ${local_branch:+-B "$local_branch"...} + case "$local_branch" in + '') git checkout -f -q ${start_point:+"$start_point"} ;; + ?*) git checkout -f -q -B "$local_branch" ${start_point:+"$start_point"} ;; + esac + ) || die "$(eval_gettext "Unable to setup cloned submodule '\$sm_path'")" } isnumber() @@ -469,16 +489,15 @@ 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 - ( - 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" ;; - esac - ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")" + if test -n "$branch" + then + start_point="origin/$branch" + local_branch="$branch" + else + start_point="" + local_branch="" + fi + module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" "$start_point" "$local_branch" || exit fi git config submodule."$sm_name".url "$realrepo" @@ -799,32 +818,35 @@ cmd_update() fi name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) - branch=$(get_submodule_config "$name" branch master) + config_branch=$(get_submodule_config "$name" branch) + branch="${config_branch:-master}" + local_branch="$branch" if ! test -z "$update" then update_module=$update else update_module=$(git config submodule."$name".update) - case "$update_module" in - '') - ;; # Unset update mode - checkout | rebase | merge | none) - ;; # Known update modes - !*) - ;; # Custom update command - *) - die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")" - ;; - esac + if test -z "$update_module" + then + update_module="checkout" + fi fi displaypath=$(relative_path "$prefix$sm_path") - if test "$update_module" = "none" - then + case "$update_module" in + none) echo "Skipping submodule '$displaypath'" continue - fi + ;; + checkout) + local_branch="" + ;; + rebase | merge | !*) + ;; + *) + die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")" + esac if test -z "$url" then @@ -838,7 +860,8 @@ Maybe you want to use 'update --init'?")" if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git then - module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit + start_point="origin/${branch}" + module_clone "$sm_path" "$name" "$url" "$reference" "$depth" "$start_point" "$local_branch" || exit cloned_modules="$cloned_modules;$name" subsha1= else @@ -884,11 +907,16 @@ Maybe you want to use 'update --init'?")" case ";$cloned_modules;" in *";$name;"*) # then there is no local change to integrate - update_module= ;; + update_module='!git reset --hard -q' 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'")" @@ -908,10 +936,7 @@ Maybe you want to use 'update --init'?")" 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") diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 0246e80b1a..28ca76384f 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -63,6 +63,9 @@ test_expect_success 'setup a submodule tree' ' git submodule add ../none none && test_tick && git commit -m "none" + ) && + (cd super && + git tag initial-setup ) ' @@ -703,7 +706,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re git clone super_update_r super_update_r2 && (cd super_update_r2 && git submodule update --init --recursive >actual && - test_i18ngrep "Submodule path .submodule/subsubmodule.: checked out" actual && + test_i18ngrep "Submodule path .submodule/subsubmodule.: .git reset --hard -q" actual && (cd submodule/subsubmodule && git log > ../../expected ) && @@ -775,4 +778,37 @@ test_expect_success 'submodule update --recursive drops module name before recur ) ' +test_expect_success 'submodule update --checkout clones detached HEAD' ' + git clone super super4 && + echo "detached HEAD" >expected && + (cd super4 && + git reset --hard initial-setup && + git submodule init submodule && + git submodule update >> /tmp/log 2>&1 && + (cd submodule && + git symbolic-ref HEAD > ../../actual || + echo "detached HEAD" > ../../actual + ) + ) && + test_cmp actual expected && + rm -rf super4 +' + +test_expect_success 'submodule update --merge clones attached HEAD' ' + git clone super super4 && + echo "refs/heads/master" >expected && + (cd super4 && + git reset --hard initial-setup && + git submodule init submodule && + git config submodule.submodule.update merge && + git submodule update --merge && + (cd submodule && + git symbolic-ref HEAD > ../../actual || + echo "detached HEAD" > ../../actual + ) + ) && + test_cmp actual expected && + rm -rf super4 +' + test_done |