From bcd9abb2b506e3edf21a456f1d61d2963534ef5f Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 28 Nov 2012 10:47:39 -0500 Subject: [PATCH] submodule update: add --remote for submodule's upstream changes MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The current `update` command incorporates the superproject's gitlinked SHA-1 ($sha1) into the submodule HEAD ($subsha1). Depending on the options you use, it may checkout $sha1, rebase the $subsha1 onto $sha1, or merge $sha1 into $subsha1. This helps you keep up with changes in the upstream superproject. However, it's also useful to stay up to date with changes in the upstream subproject. Previous workflows for incorporating such changes include the ungainly: $ git submodule foreach 'git checkout $(git config --file $toplevel/.gitmodules submodule.$name.branch) && git pull' With this patch, all of the useful functionality for incorporating superproject changes can be reused to incorporate upstream subproject updates. When you specify --remote, the target $sha1 is replaced with a $sha1 of the submodule's origin/master tracking branch. If you want to merge a different tracking branch, you can configure the `submodule..branch` option in `.gitmodules`. You can override the `.gitmodules` configuration setting for a particular superproject by configuring the option in that superproject's default configuration (using the usual configuration hierarchy, e.g. `.git/config`, `~/.gitconfig`, etc.). Previous use of submodule..branch ======================================= Because we're adding a new configuration option, it's a good idea to check if anyone else is already using the option. The foreach-pull example above was described by Ævar in commit f030c96d8643fa0a1a9b2bd9c2f36a77721fb61f Author: Ævar Arnfjörð Bjarmason Date: Fri May 21 16:10:10 2010 +0000 git-submodule foreach: Add $toplevel variable Gerrit uses the same interpretation for the setting, but because Gerrit has direct access to the subproject repositories, it updates the superproject repositories automatically when a subproject changes. Gerrit also accepts the special value '.', which it expands into the superproject's branch name. Although the --remote functionality is using `submodule..branch` slightly differently, the effect is the same. The foreach-pull example uses the option to record the name of the local branch to checkout before pulls. The tracking branch to be pulled is recorded in `.git/modules//config`, which was initialized by the module clone during `submodule add` or `submodule init`. Because the branch name stored in `submodule..branch` was likely the same as the branch name used during the initial `submodule add`, the same branch will be pulled in each workflow. Implementation details ====================== In order to ensure a current tracking branch state, `update --remote` fetches the submodule's remote repository before calculating the SHA-1. However, I didn't change the logic guarding the existing fetch: if test -z "$nofetch" then # Run fetch only if $sha1 isn't present or it # is not reachable from a ref. (clear_local_git_env; cd "$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'")" fi There will not be a double-fetch, because the new $sha1 determined after the `--remote` triggered fetch should always exist in the repository. If it doesn't, it's because some racy process removed it from the submodule's repository and we *should* be re-fetching. Signed-off-by: W. Trevor King --- Documentation/config.txt | 9 +++++---- Documentation/git-submodule.txt | 24 +++++++++++++++++++++++- Documentation/gitmodules.txt | 5 +++++ git-submodule.sh | 26 +++++++++++++++++++++++++- t/t7406-submodule-update.sh | 31 +++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 6 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 11f320b96..de39b1cfb 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1994,10 +1994,11 @@ status.submodulesummary:: submodule..path:: submodule..url:: submodule..update:: - The path within this project, URL, and the updating strategy - for a submodule. These variables are initially populated - by 'git submodule init'; edit them to override the - URL and other values found in the `.gitmodules` file. See +submodule..branch:: + The path within this project, URL, the updating strategy, and the + remote branch name for a submodule. These variables are initially + populated by 'git submodule init'; edit them to override the URL and + other values found in the `.gitmodules` file. See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details. submodule..fetchRecurseSubmodules:: diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index b4683bba1..49c06d1c3 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -13,7 +13,7 @@ SYNOPSIS [--reference ] [--] [] 'git submodule' [--quiet] status [--cached] [--recursive] [--] [...] 'git submodule' [--quiet] init [--] [...] -'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] +'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch] [--rebase] [--reference ] [--merge] [--recursive] [--] [...] 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) ] [commit] [--] [...] @@ -236,6 +236,28 @@ OPTIONS (the default). This limit only applies to modified submodules. The size is always limited to 1 for added/deleted/typechanged submodules. +--remote:: + This option is only valid for the update command. + Instead of using the superproject's recorded SHA-1 to update the + submodule, use the status of the submodule's remote tracking branch. + The remote tracking branch defaults to origin/master, but the branch + name may be overridden by setting the `submodule..branch` + option in either `.gitmodules` or `.git/config` (with `.git/config` + taking precedence). ++ +This works for any of the supported update procedures (`--checkout`, +`--rebase`, etc.). The only change is the source of the target SHA-1. +For example, `submodule update --remote --merge` will merge upstream +submodule changes into the submodules, while `submodule update +--merge` will merge superproject gitlink changes into the submodules. ++ +In order to ensure a current tracking branch state, `update --remote` +fetches the submodule's remote repository before calculating the +SHA-1. This makes `submodule update --remote --merge` similar to +running `git pull` in the submodule. If you don't want to fetch (for +something closer to `git merge`), you should use `submodule update +--remote --no-fetch --merge`. + -N:: --no-fetch:: This option is only valid for the update command. diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index 4effd7890..4004fa6a7 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -47,6 +47,11 @@ submodule..update:: This config option is overridden if 'git submodule update' is given the '--merge', '--rebase' or '--checkout' options. +submodule..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. + submodule..fetchRecurseSubmodules:: This option can be used to control recursive fetching of this submodule. If this option is also present in the submodules entry in diff --git a/git-submodule.sh b/git-submodule.sh index 97ce5e408..1feb38a3a 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -8,7 +8,8 @@ dashless=$(basename "$0" | sed -e 's/-/ /') USAGE="[--quiet] add [-b branch] [-f|--force] [--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] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference ] [--merge] [--recursive] [--] [...] +ges or: $dashless [--quiet] summary [--cached|--files] [--summary-limit ] [commit] [--] [...] or: $dashless [--quiet] foreach [--recursive] or: $dashless [--quiet] sync [--] [...]" @@ -26,6 +27,7 @@ cached= recursive= init= files= +remote= nofetch= update= prefix= @@ -536,6 +538,9 @@ cmd_update() -i|--init) init=1 ;; + --remote) + remote=1 + ;; -N|--no-fetch) nofetch=1 ;; @@ -596,6 +601,12 @@ cmd_update() fi name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) + branch=$(git config submodule."$name".branch) + if test -z "$branch" + then # fall back on .gitmodules + branch=$(git config -f .gitmodules submodule."$name".branch) + fi + branch="${branch:-master}" if ! test -z "$update" then update_module=$update @@ -630,6 +641,19 @@ Maybe you want to use 'update --init'?")" die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")" fi + if test -n "$remote" + then + if test -z "$nofetch" + then + # Fetch remote before determining tracking $sha1 + (clear_local_git_env; cd "$sm_path" && git-fetch) || + die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" + fi + sha1=$(clear_local_git_env; cd "$sm_path" && + git rev-parse --verify origin/"$branch") || + die "$(eval_gettext "Unable to find current origin/$branch revision in submodule path '\$sm_path'")" + fi + if test "$subsha1" != "$sha1" -o -n "$force" then subforce=$force diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 15426530e..a56783497 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -135,6 +135,37 @@ test_expect_success 'submodule update --force forcibly checks out submodules' ' ) ' +test_expect_success 'submodule update --remote should fetch upstream changes' ' + (cd submodule && + echo line4 >> file && + git add file && + test_tick && + git commit -m "upstream line4" + ) && + (cd super && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline)" + ) +' + +test_expect_success 'local config should override .gitmodules branch' ' + (cd submodule && + git checkout -b test-branch && + echo line5 >> file && + git add file && + test_tick && + git commit -m "upstream line5" && + git checkout master + ) && + (cd super && + git config submodule.submodule.branch test-branch && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline test-branch)" + ) +' + test_expect_success 'submodule update --rebase staying on master' ' (cd super/submodule && git checkout master -- 2.26.2