git-submodule: add support for --rebase.
authorPeter Hutterer <peter.hutterer@who-t.net>
Thu, 23 Apr 2009 23:06:38 +0000 (09:06 +1000)
committerJunio C Hamano <gitster@pobox.com>
Fri, 24 Apr 2009 08:20:25 +0000 (01:20 -0700)
'git submodule update --rebase' rebases your local branch on top of what
would have been checked out to a detached HEAD otherwise.

In some cases, detaching the HEAD when updating a submodule complicates
the workflow to commit to this submodule (checkout master, rebase, then
commit).  For submodules that require frequent updates but infrequent
(if any) commits, a rebase can be executed directly by the git-submodule
command, ensuring that the submodules stay on their respective branches.

git-config key: submodule.$name.rebase (bool)

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-submodule.txt
Documentation/gitmodules.txt
git-submodule.sh
t/t7406-submodule-update.sh [new file with mode: 0755]

index 3b8df4467377d73d613f76875c725cbf8544ee77..0286409744937bac5b4bed5af775f9b9834ea126 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 'git submodule' [--quiet] add [-b branch] [--] <repository> <path>
 'git submodule' [--quiet] status [--cached] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
-'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--] [<path>...]
+'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--] [<path>...]
 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
 'git submodule' [--quiet] foreach <command>
 'git submodule' [--quiet] sync [--] [<path>...]
@@ -113,7 +113,8 @@ init::
 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.
+       This will make the submodules HEAD be detached unless '--rebase' is
+       specified or the key `submodule.$name.rebase` is set to `true`.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
@@ -177,6 +178,15 @@ OPTIONS
        This option is only valid for the update command.
        Don't fetch new objects from the remote site.
 
+--rebase::
+       This option is only valid for the update command.
+       Rebase the current branch onto the commit recorded in the
+       superproject. If this option is given, the submodule's HEAD will not
+       be detached. If a a merge failure prevents this process, you will have
+       to resolve these failures with linkgit:git-rebase[1].
+       If the key `submodule.$name.rebase` is set to `true`, this option is
+       implicit.
+
 <path>...::
        Paths to submodule(s). When specified this will restrict the command
        to only operate on the submodules found at the specified paths.
index d1a17e2625890245341a2099cc2b058e63564da2..7c22c409497cbfa403d108e6009ca000acaf771f 100644 (file)
@@ -30,6 +30,9 @@ submodule.<name>.path::
 submodule.<name>.url::
        Defines an url from where the submodule repository can be cloned.
 
+submodule.<name>.rebase::
+       Defines that the submodule should be rebased by default.
+
 
 EXAMPLES
 --------
index 8e234a4028d22e11baedba11f871d33f56945716..3176226ac7ecca9dc875a20bd5c2df607cdfb504 100755 (executable)
@@ -17,6 +17,7 @@ branch=
 quiet=
 cached=
 nofetch=
+rebase=
 
 #
 # print stuff on stdout unless -q was specified
@@ -294,6 +295,11 @@ cmd_init()
                git config submodule."$name".url "$url" ||
                die "Failed to register url for submodule path '$path'"
 
+               test true != "$(git config -f .gitmodules --bool \
+                       submodule."$name".rebase)" ||
+               git config submodule."$name".rebase true ||
+               die "Failed to register submodule path '$path' as rebasing"
+
                say "Submodule '$name' ($url) registered for path '$path'"
        done
 }
@@ -321,6 +327,10 @@ cmd_update()
                        shift
                        nofetch=1
                        ;;
+               -r|--rebase)
+                       shift
+                       rebase=true
+                       ;;
                --)
                        shift
                        break
@@ -339,6 +349,7 @@ cmd_update()
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
+               rebase_module=$(git config --bool submodule."$name".rebase)
                if test -z "$url"
                then
                        # Only mention uninitialized submodules when its
@@ -359,6 +370,11 @@ cmd_update()
                        die "Unable to find current revision in submodule path '$path'"
                fi
 
+               if test true = "$rebase"
+               then
+                       rebase_module=true
+               fi
+
                if test "$subsha1" != "$sha1"
                then
                        force=
@@ -374,11 +390,20 @@ cmd_update()
                                die "Unable to fetch in submodule path '$path'"
                        fi
 
-                       (unset GIT_DIR; cd "$path" &&
-                                 git-checkout $force -q "$sha1") ||
-                       die "Unable to checkout '$sha1' in submodule path '$path'"
+                       if test true = "$rebase_module"
+                       then
+                               command="git-rebase"
+                               action="rebase"
+                               msg="rebased onto"
+                       else
+                               command="git-checkout $force -q"
+                               action="checkout"
+                               msg="checked out"
+                       fi
 
-                       say "Submodule path '$path': checked out '$sha1'"
+                       (unset GIT_DIR; cd "$path" && $command "$sha1") ||
+                       die "Unable to $action '$sha1' in submodule path '$path'"
+                       say "Submodule path '$path': $msg '$sha1'"
                fi
        done
 }
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
new file mode 100755 (executable)
index 0000000..3442c05
--- /dev/null
@@ -0,0 +1,140 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Red Hat, Inc.
+#
+
+test_description='Test updating submodules
+
+This test verifies that "git submodule update" detaches the HEAD of the
+submodule and "git submodule update --rebase" does not detach the HEAD.
+'
+
+. ./test-lib.sh
+
+
+compare_head()
+{
+    sha_master=`git-rev-list --max-count=1 master`
+    sha_head=`git-rev-list --max-count=1 HEAD`
+
+    test "$sha_master" = "$sha_head"
+}
+
+
+test_expect_success 'setup a submodule tree' '
+       echo file > file &&
+       git add file &&
+       test_tick &&
+       git commit -m upstream
+       git clone . super &&
+       git clone super submodule &&
+       (cd super &&
+        git submodule add ../submodule submodule &&
+        test_tick &&
+        git commit -m "submodule" &&
+        git submodule init submodule
+       ) &&
+       (cd submodule &&
+       echo "line2" > file &&
+       git add file &&
+       git commit -m "Commit 2"
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         git pull --rebase origin
+        ) &&
+        git add submodule &&
+        git commit -m "submodule update"
+       )
+'
+
+test_expect_success 'submodule update detaching the HEAD ' '
+       (cd super/submodule &&
+        git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        ! compare_head
+       )
+'
+
+test_expect_success 'submodule update --rebase staying on master' '
+       (cd super/submodule &&
+         git checkout master
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update --rebase submodule &&
+        cd submodule &&
+        compare_head
+       )
+'
+
+test_expect_success 'submodule update - rebase true in .git/config' '
+       (cd super &&
+        git config submodule.submodule.rebase true
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        compare_head
+       )
+'
+
+test_expect_success 'submodule update - rebase false in .git/config but --rebase given' '
+       (cd super &&
+        git config submodule.submodule.rebase false
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update --rebase submodule &&
+        cd submodule &&
+        compare_head
+       )
+'
+
+test_expect_success 'submodule update - rebase false in .git/config' '
+       (cd super &&
+        git config submodule.submodule.rebase false
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD^
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        ! compare_head
+       )
+'
+
+test_expect_success 'submodule init picks up rebase' '
+       (cd super &&
+        git config submodule.rebasing.url git://non-existing/git &&
+        git config submodule.rebasing.path does-not-matter &&
+        git config submodule.rebasing.rebase true &&
+        git submodule init rebasing &&
+        test true = $(git config --bool submodule.rebasing.rebase)
+       )
+'
+
+test_done