git-submodule summary: code framework
[gitweb.git] / git-submodule.sh
index 5af28ecd58de8c46f194280887ef9c27c784e606..dbb866644a24d587b67b85a3efa8c23e7bef0178 100755 (executable)
@@ -4,15 +4,13 @@
 #
 # Copyright (c) 2007 Lars Hjemli
 
-USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
+USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update|summary [<commit>]] [--] [<path>...]'
+OPTIONS_SPEC=
 . git-sh-setup
 require_work_tree
 
-add=
+command=
 branch=
-init=
-update=
-status=
 quiet=
 cached=
 
@@ -85,9 +83,9 @@ module_name()
 #
 # Clone a submodule
 #
-# Prior to calling, modules_update checks that a possibly existing
+# Prior to calling, cmd_update checks that a possibly existing
 # path is not a git repository.
-# Likewise, module_add checks that path does not exist at all,
+# Likewise, cmd_add checks that path does not exist at all,
 # since it is the location of a new submodule.
 #
 module_clone()
@@ -120,8 +118,34 @@ module_clone()
 #
 # optional branch is stored in global branch variable
 #
-module_add()
+cmd_add()
 {
+       # parse $args after "submodule ... add".
+       while test $# -ne 0
+       do
+               case "$1" in
+               -b | --branch)
+                       case "$2" in '') usage ;; esac
+                       branch=$2
+                       shift
+                       ;;
+               -q|--quiet)
+                       quiet=1
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
        repo=$1
        path=$2
 
@@ -157,7 +181,7 @@ module_add()
        die "'$path' already exists in the index"
 
        module_clone "$path" "$realrepo" || exit
-       (unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
+       (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
        die "Unable to checkout submodule '$path'"
        git add "$path" ||
        die "Failed to add submodule '$path'"
@@ -173,8 +197,29 @@ module_add()
 #
 # $@ = requested paths (default to all)
 #
-modules_init()
+cmd_init()
 {
+       # parse $args after "submodule ... init".
+       while test $# -ne 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       quiet=1
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
        git ls-files --stage -- "$@" | grep -e '^160000 ' |
        while read mode sha1 stage path
        do
@@ -206,8 +251,29 @@ modules_init()
 #
 # $@ = requested paths (default to all)
 #
-modules_update()
+cmd_update()
 {
+       # parse $args after "submodule ... update".
+       while test $# -ne 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       quiet=1
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
        git ls-files --stage -- "$@" | grep -e '^160000 ' |
        while read mode sha1 stage path
        do
@@ -227,14 +293,14 @@ modules_update()
                        module_clone "$path" "$url" || exit
                        subsha1=
                else
-                       subsha1=$(unset GIT_DIR && cd "$path" &&
+                       subsha1=$(unset GIT_DIR; cd "$path" &&
                                git rev-parse --verify HEAD) ||
                        die "Unable to find current revision in submodule path '$path'"
                fi
 
                if test "$subsha1" != "$sha1"
                then
-                       (unset GIT_DIR && cd "$path" && git-fetch &&
+                       (unset GIT_DIR; cd "$path" && git-fetch &&
                                git-checkout -q "$sha1") ||
                        die "Unable to checkout '$sha1' in submodule path '$path'"
 
@@ -245,7 +311,7 @@ modules_update()
 
 set_name_rev () {
        revname=$( (
-               unset GIT_DIR &&
+               unset GIT_DIR
                cd "$1" && {
                        git describe "$2" 2>/dev/null ||
                        git describe --tags "$2" 2>/dev/null ||
@@ -254,7 +320,58 @@ set_name_rev () {
        ) )
        test -z "$revname" || revname=" ($revname)"
 }
+#
+# Show commit summary for submodules in index or working tree
+#
+# If '--cached' is given, show summary between index and given commit,
+# or between working tree and given commit
+#
+# $@ = [commit (default 'HEAD'),] requested paths (default all)
+#
+cmd_summary() {
+       # parse $args after "submodule ... summary".
+       while test $# -ne 0
+       do
+               case "$1" in
+               --cached)
+                       cached="$1"
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
+       if rev=$(git rev-parse --verify "$1^0" 2>/dev/null)
+       then
+               head=$rev
+               shift
+       else
+               head=HEAD
+       fi
 
+       cd_to_toplevel
+       # Get modified modules cared by user
+       modules=$(git diff-index $cached --raw $head -- "$@" |
+               grep -e '^:160000' -e '^:[0-7]* 160000' |
+               while read mod_src mod_dst sha1_src sha1_dst status name
+               do
+                       # Always show modules deleted or type-changed (blob<->module)
+                       test $status = D -o $status = T && echo "$name" && continue
+                       # Also show added or modified modules which are checked out
+                       GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
+                       echo "$name"
+               done
+       )
+}
 #
 # List all submodules, prefixed with:
 #  - submodule not initialized
@@ -265,14 +382,38 @@ set_name_rev () {
 #
 # $@ = requested paths (default to all)
 #
-modules_list()
+cmd_status()
 {
+       # parse $args after "submodule ... status".
+       while test $# -ne 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       quiet=1
+                       ;;
+               --cached)
+                       cached=1
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
        git ls-files --stage -- "$@" | grep -e '^160000 ' |
        while read mode sha1 stage path
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
-               if test -z "url" || ! test -d "$path"/.git
+               if test -z "$url" || ! test -d "$path"/.git
                then
                        say "-$sha1 $path"
                        continue;
@@ -284,7 +425,7 @@ modules_list()
                else
                        if test -z "$cached"
                        then
-                               sha1=$(unset GIT_DIR && cd "$path" && git rev-parse --verify HEAD)
+                               sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
                                set_name_rev "$path" "$sha1"
                        fi
                        say "+$sha1 $path$revname"
@@ -292,20 +433,17 @@ modules_list()
        done
 }
 
-while test $# != 0
+# This loop parses the command line arguments to find the
+# subcommand name to dispatch.  Parsing of the subcommand specific
+# options are primarily done by the subcommand implementations.
+# Subcommand specific options such as --branch and --cached are
+# parsed here as well, for backward compatibility.
+
+while test $# != 0 && test -z "$command"
 do
        case "$1" in
-       add)
-               add=1
-               ;;
-       init)
-               init=1
-               ;;
-       update)
-               update=1
-               ;;
-       status)
-               status=1
+       add | init | update | status | summary)
+               command=$1
                ;;
        -q|--quiet)
                quiet=1
@@ -319,7 +457,7 @@ do
                branch="$2"; shift
                ;;
        --cached)
-               cached=1
+               cached="$1"
                ;;
        --)
                break
@@ -334,30 +472,19 @@ do
        shift
 done
 
-case "$add,$branch" in
-1,*)
-       ;;
-,)
-       ;;
-,*)
+# No command word defaults to "status"
+test -n "$command" || command=status
+
+# "-b branch" is accepted only by "add"
+if test -n "$branch" && test "$command" != add
+then
        usage
-       ;;
-esac
-
-case "$add,$init,$update,$status,$cached" in
-1,,,,)
-       module_add "$@"
-       ;;
-,1,,,)
-       modules_init "$@"
-       ;;
-,,1,,)
-       modules_update "$@"
-       ;;
-,,,*,*)
-       modules_list "$@"
-       ;;
-*)
+fi
+
+# "--cached" is accepted only by "status" and "summary"
+if test -n "$cached" && test "$command" != status -a "$command" != summary
+then
        usage
-       ;;
-esac
+fi
+
+"cmd_$command" "$@"