Merge branch 'jh/submodule-foreach'
authorJunio C Hamano <gitster@pobox.com>
Thu, 27 Aug 2009 23:59:25 +0000 (16:59 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 27 Aug 2009 23:59:25 +0000 (16:59 -0700)
* jh/submodule-foreach:
git clone: Add --recursive to automatically checkout (nested) submodules
t7407: Use 'rev-parse --short' rather than bash's substring expansion notation
git submodule status: Add --recursive to recurse into nested submodules
git submodule update: Introduce --recursive to update nested submodules
git submodule foreach: Add --recursive to recurse into nested submodules
git submodule foreach: test access to submodule name as '$name'
Add selftest for 'git submodule foreach'
git submodule: Cleanup usage string and add option parsing to cmd_foreach()
git submodule foreach: Provide access to submodule name, as '$name'

Conflicts:
Documentation/git-submodule.txt
git-submodule.sh

1  2 
Documentation/git-clone.txt
Documentation/git-submodule.txt
git-submodule.sh
index 2c63a0fbaee0e246538607c8e2f983eb6eafa8e5,5a685ceec94b75faa5a0a90cd46e76e45a42555a..88ea272ee5a1994edfbc93347d5841228de46f17
@@@ -12,7 -12,7 +12,7 @@@ SYNOPSI
  'git clone' [--template=<template_directory>]
          [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
          [-o <name>] [-u <upload-pack>] [--reference <repository>]
-         [--depth <depth>] [--] <repository> [<directory>]
+         [--depth <depth>] [--recursive] [--] <repository> [<directory>]
  
  DESCRIPTION
  -----------
@@@ -72,16 -72,8 +72,16 @@@ These objects may be removed by normal 
  which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
  If these objects are removed and were referenced by the cloned repository,
  then the cloned repository will become corrupt.
 -
 -
 ++
 +Note that running `git repack` without the `-l` option in a repository
 +cloned with `-s` will copy objects from the source repository into a pack
 +in the cloned repository, removing the disk space savings of `clone -s`.
 +It is safe, however, to run `git gc`, which uses the `-l` option by
 +default.
 ++
 +If you want to break the dependency of a repository cloned with `-s` on
 +its source repository, you can simply run `git repack -a` to copy all
 +objects from the source repository into a pack in the cloned repository.
  
  --reference <repository>::
        If the reference repository is on the local machine
        with a long history, and would want to send in fixes
        as patches.
  
+ --recursive::
+       After the clone is created, initialize all submodules within,
+       using their default settings. This is equivalent to running
+       'git submodule update --init --recursive' immediately after
+       the clone is finished. This option is ignored if the cloned
+       repository does not have a worktree/checkout (i.e. if any of
+       `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
  <repository>::
        The (possibly remote) repository to clone from.  See the
        <<URLS,URLS>> section below for more information on specifying
index bb7d159179a4f97c8641a50ba4425271c62566d8,b81c830c28c22d82beada45286f14676f7bccae8..5ccdd18c89381e81fc616facb22bd7fee6faf964
@@@ -11,12 -11,12 +11,12 @@@ SYNOPSI
  [verse]
  'git submodule' [--quiet] add [-b branch]
              [--reference <repository>] [--] <repository> <path>
- 'git submodule' [--quiet] status [--cached] [--] [<path>...]
+ 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
  'git submodule' [--quiet] init [--] [<path>...]
  'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
-             [--reference <repository>] [--merge] [--] [<path>...]
+             [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
 -'git submodule' [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
 +'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
- 'git submodule' [--quiet] foreach <command>
+ 'git submodule' [--quiet] foreach [--recursive] <command>
  'git submodule' [--quiet] sync [--] [<path>...]
  
  
@@@ -100,6 -100,9 +100,9 @@@ status:
        initialized and `+` if the currently checked out submodule commit
        does not match the SHA-1 found in the index of the containing
        repository. This command is the default command for 'git-submodule'.
+ +
+ If '--recursive' is specified, this command will recurse into nested
+ submodules, and show their status as well.
  
  init::
        Initialize the submodules, i.e. register each submodule name
@@@ -122,25 -125,27 +125,31 @@@ update:
  If the submodule is not yet initialized, and you just want to use the
  setting as stored in .gitmodules, you can automatically initialize the
  submodule with the --init option.
+ +
+ If '--recursive' is specified, this command will recurse into the
+ registered submodules, and update any nested submodules within.
  
  summary::
        Show commit summary between the given commit (defaults to HEAD) and
        working tree/index. For a submodule in question, a series of commits
        in the submodule between the given super project commit and the
 -      index or working tree (switched by --cached) are shown.
 +      index or working tree (switched by --cached) are shown. If the option
 +      --files is given, show the series of commits in the submodule between
 +      the index of the super project and the working tree of the submodule
 +      (this option doesn't allow to use the --cached option or to provide an
 +      explicit commit).
  
  foreach::
        Evaluates an arbitrary shell command in each checked out submodule.
-       The command has access to the variables $path and $sha1:
+       The command has access to the variables $name, $path and $sha1:
+       $name is the name of the relevant submodule section in .gitmodules,
        $path is the name of the submodule directory relative to the
        superproject, and $sha1 is the commit as recorded in the superproject.
        Any submodules defined in the superproject but not checked out are
        ignored by this command. Unless given --quiet, foreach prints the name
        of each submodule before evaluating the command.
+       If --recursive is given, submodules are traversed recursively (i.e.
+       the given shell command is evaluated in nested submodules as well).
        A non-zero return from the command in any submodule causes
        the processing to terminate. This can be overridden by adding '|| :'
        to the end of the command.
@@@ -173,11 -178,6 +182,11 @@@ OPTION
        commands typically use the commit found in the submodule HEAD, but
        with this option, the commit stored in the index is used instead.
  
 +--files::
 +      This option is only valid for the summary command. This command
 +      compares the commit in the index with that in the submodule HEAD
 +      when this option is used.
 +
  -n::
  --summary-limit::
        This option is only valid for the summary command.
  *NOTE*: Do *not* use this option unless you have read the note
  for linkgit:git-clone[1]'s --reference and --shared options carefully.
  
+ --recursive::
+       This option is only valid for foreach, update and status commands.
+       Traverse submodules recursively. The operation is performed not
+       only in the submodules of the current repo, but also
+       in any nested submodules inside those submodules (and so on).
  <path>...::
        Paths to submodule(s). When specified this will restrict the command
        to only operate on the submodules found at the specified paths.
diff --combined git-submodule.sh
index 9bdd6ea3d0f988f781b902c1bf5be29836fdc07a,446bbc0a1056b4b9de802eebb7fe911e9d4d11a3..bfbd36b6f45097feaec92f107690224fc81c09c6
@@@ -4,9 -4,14 +4,14 @@@
  #
  # Copyright (c) 2007 Lars Hjemli
  
- USAGE="[--quiet] [--cached|--files] \
- [add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch] [--rebase|--merge]|summary [-n|--summary-limit <n>] [<commit>]] \
- [--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
+ dashless=$(basename "$0" | sed -e 's/-/ /')
+ USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path>
+    or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
+    or: $dashless [--quiet] init [--] [<path>...]
+    or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
 -   or: $dashless [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
++   or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+    or: $dashless [--quiet] foreach [--recursive] <command>
+    or: $dashless [--quiet] sync [--] [<path>...]"
  OPTIONS_SPEC=
  . git-sh-setup
  . git-parse-remote
@@@ -16,9 -21,9 +21,10 @@@ command
  branch=
  reference=
  cached=
 +files=
  nofetch=
  update=
+ prefix=
  
  # Resolve relative url by appending to parent's url
  resolve_relative_url ()
@@@ -238,13 -243,43 +244,43 @@@ cmd_add(
  #
  cmd_foreach()
  {
+       # parse $args after "submodule ... foreach".
+       while test $# -ne 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       GIT_QUIET=1
+                       ;;
+               --recursive)
+                       recursive=1
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
        module_list |
        while read mode sha1 stage path
        do
                if test -e "$path"/.git
                then
-                       say "Entering '$path'"
-                       (cd "$path" && eval "$@") ||
+                       say "Entering '$prefix$path'"
+                       name=$(module_name "$path")
+                       (
+                               prefix="$prefix$path/"
+                               unset GIT_DIR
+                               cd "$path" &&
+                               eval "$@" &&
+                               if test -n "$recursive"
+                               then
+                                       cmd_foreach "--recursive" "$@"
+                               fi
+                       ) ||
                        die "Stopping at '$path'; script returned non-zero status."
                fi
        done
@@@ -317,6 -352,7 +353,7 @@@ cmd_init(
  cmd_update()
  {
        # parse $args after "submodule ... update".
+       orig_args="$@"
        while test $# -ne 0
        do
                case "$1" in
                        shift
                        update="merge"
                        ;;
+               --recursive)
+                       shift
+                       recursive=1
+                       ;;
                --)
                        shift
                        break
                        die "Unable to $action '$sha1' in submodule path '$path'"
                        say "Submodule path '$path': $msg '$sha1'"
                fi
+               if test -n "$recursive"
+               then
+                       (unset GIT_DIR; cd "$path" && cmd_update $orig_args) ||
+                       die "Failed to recurse into submodule path '$path'"
+               fi
        done
  }
  
@@@ -461,7 -507,6 +508,7 @@@ set_name_rev () 
  cmd_summary() {
        summary_limit=-1
        for_status=
 +      diff_cmd=diff-index
  
        # parse $args after "submodule ... summary".
        while test $# -ne 0
                --cached)
                        cached="$1"
                        ;;
 +              --files)
 +                      files="$1"
 +                      ;;
                --for-status)
                        for_status="$1"
                        ;;
                head=HEAD
        fi
  
 +      if [ -n "$files" ]
 +      then
 +              test -n "$cached" &&
 +              die "--cached cannot be used with --files"
 +              diff_cmd=diff-files
 +              head=
 +      fi
 +
        cd_to_toplevel
        # Get modified modules cared by user
 -      modules=$(git diff-index $cached --raw $head -- "$@" |
 +      modules=$(git $diff_cmd $cached --raw $head -- "$@" |
                egrep '^:([0-7]* )?160000' |
                while read mod_src mod_dst sha1_src sha1_dst status name
                do
  
        test -z "$modules" && return
  
 -      git diff-index $cached --raw $head -- $modules |
 +      git $diff_cmd $cached --raw $head -- $modules |
        egrep '^:([0-7]* )?160000' |
        cut -c2- |
        while read mod_src mod_dst sha1_src sha1_dst status name
  cmd_status()
  {
        # parse $args after "submodule ... status".
+       orig_args="$@"
        while test $# -ne 0
        do
                case "$1" in
                --cached)
                        cached=1
                        ;;
+               --recursive)
+                       recursive=1
+                       ;;
                --)
                        shift
                        break
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
+               displaypath="$prefix$path"
                if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
                then
-                       say "-$sha1 $path"
+                       say "-$sha1 $displaypath"
                        continue;
                fi
                set_name_rev "$path" "$sha1"
                if git diff-files --quiet -- "$path"
                then
-                       say " $sha1 $path$revname"
+                       say " $sha1 $displaypath$revname"
                else
                        if test -z "$cached"
                        then
                                sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
                                set_name_rev "$path" "$sha1"
                        fi
-                       say "+$sha1 $path$revname"
+                       say "+$sha1 $displaypath$revname"
+               fi
+               if test -n "$recursive"
+               then
+                       (
+                               prefix="$displaypath/"
+                               unset GIT_DIR
+                               cd "$path" &&
+                               cmd_status $orig_args
+                       ) ||
+                       die "Failed to recurse into submodule path '$path'"
                fi
        done
  }