Currently, in the event that a submodule's upstream URL changes, users
have to manually alter the URL in the .gitmodules file then run
`git submodule sync`. Let's make that process easier.

Teach submodule the set-url subcommand which will automatically change
the `submodule.$name.url` property in the .gitmodules file and then run
`git submodule sync` to complete the process.

Signed-off-by: Denton Liu <liu.den...@gmail.com>
---
 Documentation/git-submodule.txt        |  6 +++
 contrib/completion/git-completion.bash |  2 +-
 git-submodule.sh                       | 52 +++++++++++++++++++++++++-
 t/t7420-submodule-set-url.sh           | 51 +++++++++++++++++++++++++
 4 files changed, 109 insertions(+), 2 deletions(-)
 create mode 100755 t/t7420-submodule-set-url.sh

diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 0ed5c24dc1..8eac3c350c 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)
 'git submodule' [--quiet] update [<options>] [--] [<path>...]
 'git submodule' [--quiet] set-branch [<options>] [--] <path>
+'git submodule' [--quiet] set-url [<options>] [--] <path> <newurl>
 'git submodule' [--quiet] summary [<options>] [--] [<path>...]
 'git submodule' [--quiet] foreach [--recursive] <command>
 'git submodule' [--quiet] sync [--recursive] [--] [<path>...]
@@ -179,6 +180,11 @@ set-branch ((-d|--default)|(-b|--branch <branch>)) [--] 
<path>::
        `--default` option removes the submodule.<name>.branch configuration
        key, which causes the tracking branch to default to 'master'.
 
+set-url [--] <path> <newurl>::
+       Sets the URL of the specified submodule to <newurl>. Then, it will
+       automatically synchronize the submodule's new remote URL
+       configuration.
+
 summary [--cached|--files] [(-n|--summary-limit) <n>] [commit] [--] 
[<path>...]::
        Show commit summary between the given commit (defaults to HEAD) and
        working tree/index. For a submodule in question, a series of commits
diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index e087c4bf00..bec31a7fc3 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2666,7 +2666,7 @@ _git_submodule ()
 {
        __git_has_doubledash && return
 
-       local subcommands="add status init deinit update set-branch summary 
foreach sync absorbgitdirs"
+       local subcommands="add status init deinit update set-branch set-url 
summary foreach sync absorbgitdirs"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                case "$cur" in
diff --git a/git-submodule.sh b/git-submodule.sh
index c7f58c5756..f7374ddbd6 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -12,6 +12,7 @@ USAGE="[--quiet] [--cached]
    or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
    or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] 
[-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] 
[--reference <repository>] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] set-branch (--default|--branch <branch>) [--] <path>
+   or: $dashless [--quiet] set-url [--] <path> <newurl>
    or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] 
[commit] [--] [<path>...]
    or: $dashless [--quiet] foreach [--recursive] <command>
    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]
@@ -760,6 +761,55 @@ cmd_set_branch() {
        fi
 }
 
+#
+# Configures a submodule's remote url
+#
+# $@ = requested path, requested url
+#
+cmd_set_url() {
+       while test $# -ne 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       GIT_QUIET=1
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
+       if test $# -ne 2
+       then
+               usage
+       fi
+
+       # we can't use `git submodule--helper name` here because internally, it
+       # hashes the path so a trailing slash could lead to an unintentional no 
match
+       name="$(git submodule--helper list "$1" | cut -f2)"
+       if test -z "$name"
+       then
+               exit 1
+       fi
+
+       url="$2"
+       if test -z "$url"
+       then
+               exit 1
+       fi
+
+       git submodule--helper config submodule."$name".url "$url"
+       git submodule--helper sync ${GIT_QUIET:+--quiet} "$name"
+}
+
 #
 # Show commit summary for submodules in index or working tree
 #
@@ -1059,7 +1109,7 @@ cmd_absorbgitdirs()
 while test $# != 0 && test -z "$command"
 do
        case "$1" in
-       add | foreach | init | deinit | update | set-branch | status | summary 
| sync | absorbgitdirs)
+       add | foreach | init | deinit | update | set-branch | set-url | status 
| summary | sync | absorbgitdirs)
                command=$1
                ;;
        -q|--quiet)
diff --git a/t/t7420-submodule-set-url.sh b/t/t7420-submodule-set-url.sh
new file mode 100755
index 0000000000..60adc5db28
--- /dev/null
+++ b/t/t7420-submodule-set-url.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 Denton Liu
+#
+
+test_description='Test submodules set-url subcommand
+
+This test verifies that the set-url subcommand of git-submodule is working
+as expected.
+'
+
+TEST_NO_CREATE_REPO=1
+. ./test-lib.sh
+
+test_expect_success 'submodule config cache setup' '
+       mkdir submodule &&
+       (cd submodule &&
+               git init &&
+               echo a >file &&
+               git add file &&
+               git commit -ma
+       ) &&
+       mkdir super &&
+       (cd super &&
+               git init &&
+               git submodule add ../submodule &&
+               git commit -m "add submodule"
+       )
+'
+
+test_expect_success 'test submodule set-url' '
+       # add a commit and move the submodule (change the url)
+       (cd submodule &&
+               echo b >>file &&
+               git add file &&
+               git commit -mb
+       ) &&
+       mv submodule newsubmodule &&
+
+       git -C newsubmodule show >expect &&
+       (cd super &&
+               test_must_fail git submodule update --remote &&
+               git submodule set-url submodule ../newsubmodule &&
+               grep "url = \.\./newsubmodule" .gitmodules &&
+               git submodule update --remote &&
+               git -C submodule show >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_done
-- 
2.23.0

Reply via email to