git-submodule.shon commit Merge branch 'jc/test-portability' (8f98074)
   1#!/bin/sh
   2#
   3# git-submodule.sh: add, init, update or list git submodules
   4#
   5# Copyright (c) 2007 Lars Hjemli
   6
   7dashless=$(basename "$0" | sed -e 's/-/ /')
   8USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
   9   or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
  10   or: $dashless [--quiet] init [--] [<path>...]
  11   or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
  12   or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
  13   or: $dashless [--quiet] foreach [--recursive] <command>
  14   or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
  15OPTIONS_SPEC=
  16. git-sh-setup
  17. git-sh-i18n
  18. git-parse-remote
  19require_work_tree
  20
  21command=
  22branch=
  23force=
  24reference=
  25cached=
  26recursive=
  27init=
  28files=
  29nofetch=
  30update=
  31prefix=
  32custom_name=
  33
  34# The function takes at most 2 arguments. The first argument is the
  35# URL that navigates to the submodule origin repo. When relative, this URL
  36# is relative to the superproject origin URL repo. The second up_path
  37# argument, if specified, is the relative path that navigates
  38# from the submodule working tree to the superproject working tree.
  39#
  40# The output of the function is the origin URL of the submodule.
  41#
  42# The output will either be an absolute URL or filesystem path (if the
  43# superproject origin URL is an absolute URL or filesystem path,
  44# respectively) or a relative file system path (if the superproject
  45# origin URL is a relative file system path).
  46#
  47# When the output is a relative file system path, the path is either
  48# relative to the submodule working tree, if up_path is specified, or to
  49# the superproject working tree otherwise.
  50resolve_relative_url ()
  51{
  52        remote=$(get_default_remote)
  53        remoteurl=$(git config "remote.$remote.url") ||
  54                remoteurl=$(pwd) # the repository is its own authoritative upstream
  55        url="$1"
  56        remoteurl=${remoteurl%/}
  57        sep=/
  58        up_path="$2"
  59
  60        case "$remoteurl" in
  61        *:*|/*)
  62                is_relative=
  63                ;;
  64        ./*|../*)
  65                is_relative=t
  66                ;;
  67        *)
  68                is_relative=t
  69                remoteurl="./$remoteurl"
  70                ;;
  71        esac
  72
  73        while test -n "$url"
  74        do
  75                case "$url" in
  76                ../*)
  77                        url="${url#../}"
  78                        case "$remoteurl" in
  79                        */*)
  80                                remoteurl="${remoteurl%/*}"
  81                                ;;
  82                        *:*)
  83                                remoteurl="${remoteurl%:*}"
  84                                sep=:
  85                                ;;
  86                        *)
  87                                if test -z "$is_relative" || test "." = "$remoteurl"
  88                                then
  89                                        die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
  90                                else
  91                                        remoteurl=.
  92                                fi
  93                                ;;
  94                        esac
  95                        ;;
  96                ./*)
  97                        url="${url#./}"
  98                        ;;
  99                *)
 100                        break;;
 101                esac
 102        done
 103        remoteurl="$remoteurl$sep${url%/}"
 104        echo "${is_relative:+${up_path}}${remoteurl#./}"
 105}
 106
 107#
 108# Get submodule info for registered submodules
 109# $@ = path to limit submodule list
 110#
 111module_list()
 112{
 113        (
 114                git ls-files --error-unmatch --stage -- "$@" ||
 115                echo "unmatched pathspec exists"
 116        ) |
 117        perl -e '
 118        my %unmerged = ();
 119        my ($null_sha1) = ("0" x 40);
 120        my @out = ();
 121        my $unmatched = 0;
 122        while (<STDIN>) {
 123                if (/^unmatched pathspec/) {
 124                        $unmatched = 1;
 125                        next;
 126                }
 127                chomp;
 128                my ($mode, $sha1, $stage, $path) =
 129                        /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
 130                next unless $mode eq "160000";
 131                if ($stage ne "0") {
 132                        if (!$unmerged{$path}++) {
 133                                push @out, "$mode $null_sha1 U\t$path\n";
 134                        }
 135                        next;
 136                }
 137                push @out, "$_\n";
 138        }
 139        if ($unmatched) {
 140                print "#unmatched\n";
 141        } else {
 142                print for (@out);
 143        }
 144        '
 145}
 146
 147die_if_unmatched ()
 148{
 149        if test "$1" = "#unmatched"
 150        then
 151                exit 1
 152        fi
 153}
 154
 155#
 156# Map submodule path to submodule name
 157#
 158# $1 = path
 159#
 160module_name()
 161{
 162        # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
 163        sm_path="$1"
 164        re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
 165        name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
 166                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
 167        test -z "$name" &&
 168        die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")"
 169        echo "$name"
 170}
 171
 172#
 173# Clone a submodule
 174#
 175# Prior to calling, cmd_update checks that a possibly existing
 176# path is not a git repository.
 177# Likewise, cmd_add checks that path does not exist at all,
 178# since it is the location of a new submodule.
 179#
 180module_clone()
 181{
 182        sm_path=$1
 183        name=$2
 184        url=$3
 185        reference="$4"
 186        quiet=
 187        if test -n "$GIT_QUIET"
 188        then
 189                quiet=-q
 190        fi
 191
 192        gitdir=
 193        gitdir_base=
 194        base_name=$(dirname "$name")
 195
 196        gitdir=$(git rev-parse --git-dir)
 197        gitdir_base="$gitdir/modules/$base_name"
 198        gitdir="$gitdir/modules/$name"
 199
 200        if test -d "$gitdir"
 201        then
 202                mkdir -p "$sm_path"
 203                rm -f "$gitdir/index"
 204        else
 205                mkdir -p "$gitdir_base"
 206                (
 207                        clear_local_git_env
 208                        git clone $quiet -n ${reference:+"$reference"} \
 209                                --separate-git-dir "$gitdir" "$url" "$sm_path"
 210                ) ||
 211                die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
 212        fi
 213
 214        # We already are at the root of the work tree but cd_to_toplevel will
 215        # resolve any symlinks that might be present in $PWD
 216        a=$(cd_to_toplevel && cd "$gitdir" && pwd)/
 217        b=$(cd_to_toplevel && cd "$sm_path" && pwd)/
 218        # normalize Windows-style absolute paths to POSIX-style absolute paths
 219        case $a in [a-zA-Z]:/*) a=/${a%%:*}${a#*:} ;; esac
 220        case $b in [a-zA-Z]:/*) b=/${b%%:*}${b#*:} ;; esac
 221        # Remove all common leading directories after a sanity check
 222        if test "${a#$b}" != "$a" || test "${b#$a}" != "$b"; then
 223                die "$(eval_gettext "Gitdir '\$a' is part of the submodule path '\$b' or vice versa")"
 224        fi
 225        while test "${a%%/*}" = "${b%%/*}"
 226        do
 227                a=${a#*/}
 228                b=${b#*/}
 229        done
 230        # Now chop off the trailing '/'s that were added in the beginning
 231        a=${a%/}
 232        b=${b%/}
 233
 234        # Turn each leading "*/" component into "../"
 235        rel=$(echo $b | sed -e 's|[^/][^/]*|..|g')
 236        echo "gitdir: $rel/$a" >"$sm_path/.git"
 237
 238        rel=$(echo $a | sed -e 's|[^/][^/]*|..|g')
 239        (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
 240}
 241
 242#
 243# Add a new submodule to the working tree, .gitmodules and the index
 244#
 245# $@ = repo path
 246#
 247# optional branch is stored in global branch variable
 248#
 249cmd_add()
 250{
 251        # parse $args after "submodule ... add".
 252        while test $# -ne 0
 253        do
 254                case "$1" in
 255                -b | --branch)
 256                        case "$2" in '') usage ;; esac
 257                        branch=$2
 258                        shift
 259                        ;;
 260                -f | --force)
 261                        force=$1
 262                        ;;
 263                -q|--quiet)
 264                        GIT_QUIET=1
 265                        ;;
 266                --reference)
 267                        case "$2" in '') usage ;; esac
 268                        reference="--reference=$2"
 269                        shift
 270                        ;;
 271                --reference=*)
 272                        reference="$1"
 273                        ;;
 274                --name)
 275                        case "$2" in '') usage ;; esac
 276                        custom_name=$2
 277                        shift
 278                        ;;
 279                --)
 280                        shift
 281                        break
 282                        ;;
 283                -*)
 284                        usage
 285                        ;;
 286                *)
 287                        break
 288                        ;;
 289                esac
 290                shift
 291        done
 292
 293        repo=$1
 294        sm_path=$2
 295
 296        if test -z "$sm_path"; then
 297                sm_path=$(echo "$repo" |
 298                        sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
 299        fi
 300
 301        if test -z "$repo" -o -z "$sm_path"; then
 302                usage
 303        fi
 304
 305        # assure repo is absolute or relative to parent
 306        case "$repo" in
 307        ./*|../*)
 308                # dereference source url relative to parent's url
 309                realrepo=$(resolve_relative_url "$repo") || exit
 310                ;;
 311        *:*|/*)
 312                # absolute url
 313                realrepo=$repo
 314                ;;
 315        *)
 316                die "$(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
 317        ;;
 318        esac
 319
 320        # normalize path:
 321        # multiple //; leading ./; /./; /../; trailing /
 322        sm_path=$(printf '%s/\n' "$sm_path" |
 323                sed -e '
 324                        s|//*|/|g
 325                        s|^\(\./\)*||
 326                        s|/\./|/|g
 327                        :start
 328                        s|\([^/]*\)/\.\./||
 329                        tstart
 330                        s|/*$||
 331                ')
 332        git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
 333        die "$(eval_gettext "'\$sm_path' already exists in the index")"
 334
 335        if test -z "$force" && ! git add --dry-run --ignore-missing "$sm_path" > /dev/null 2>&1
 336        then
 337                eval_gettextln "The following path is ignored by one of your .gitignore files:
 338\$sm_path
 339Use -f if you really want to add it." >&2
 340                exit 1
 341        fi
 342
 343        if test -n "$custom_name"
 344        then
 345                sm_name="$custom_name"
 346        else
 347                sm_name="$sm_path"
 348        fi
 349
 350        # perhaps the path exists and is already a git repo, else clone it
 351        if test -e "$sm_path"
 352        then
 353                if test -d "$sm_path"/.git -o -f "$sm_path"/.git
 354                then
 355                        eval_gettextln "Adding existing repo at '\$sm_path' to the index"
 356                else
 357                        die "$(eval_gettext "'\$sm_path' already exists and is not a valid git repo")"
 358                fi
 359
 360        else
 361                if test -d ".git/modules/$sm_name"
 362                then
 363                        if test -z "$force"
 364                        then
 365                                echo >&2 "$(eval_gettext "A git directory for '\$sm_name' is found locally with remote(s):")"
 366                                GIT_DIR=".git/modules/$sm_name" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,"  ", -e s,' (fetch)',, >&2
 367                                echo >&2 "$(eval_gettext "If you want to reuse this local git directory instead of cloning again from")"
 368                                echo >&2 "  $realrepo"
 369                                echo >&2 "$(eval_gettext "use the '--force' option. If the local git directory is not the correct repo")"
 370                                die "$(eval_gettext "or you are unsure what this means choose another name with the '--name' option.")"
 371                        else
 372                                echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
 373                        fi
 374                fi
 375                module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit
 376                (
 377                        clear_local_git_env
 378                        cd "$sm_path" &&
 379                        # ash fails to wordsplit ${branch:+-b "$branch"...}
 380                        case "$branch" in
 381                        '') git checkout -f -q ;;
 382                        ?*) git checkout -f -q -B "$branch" "origin/$branch" ;;
 383                        esac
 384                ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
 385        fi
 386        git config submodule."$sm_name".url "$realrepo"
 387
 388        git add $force "$sm_path" ||
 389        die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
 390
 391        git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
 392        git config -f .gitmodules submodule."$sm_name".url "$repo" &&
 393        git add --force .gitmodules ||
 394        die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
 395}
 396
 397#
 398# Execute an arbitrary command sequence in each checked out
 399# submodule
 400#
 401# $@ = command to execute
 402#
 403cmd_foreach()
 404{
 405        # parse $args after "submodule ... foreach".
 406        while test $# -ne 0
 407        do
 408                case "$1" in
 409                -q|--quiet)
 410                        GIT_QUIET=1
 411                        ;;
 412                --recursive)
 413                        recursive=1
 414                        ;;
 415                -*)
 416                        usage
 417                        ;;
 418                *)
 419                        break
 420                        ;;
 421                esac
 422                shift
 423        done
 424
 425        toplevel=$(pwd)
 426
 427        # dup stdin so that it can be restored when running the external
 428        # command in the subshell (and a recursive call to this function)
 429        exec 3<&0
 430
 431        module_list |
 432        while read mode sha1 stage sm_path
 433        do
 434                die_if_unmatched "$mode"
 435                if test -e "$sm_path"/.git
 436                then
 437                        say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
 438                        name=$(module_name "$sm_path")
 439                        (
 440                                prefix="$prefix$sm_path/"
 441                                clear_local_git_env
 442                                # we make $path available to scripts ...
 443                                path=$sm_path
 444                                cd "$sm_path" &&
 445                                eval "$@" &&
 446                                if test -n "$recursive"
 447                                then
 448                                        cmd_foreach "--recursive" "$@"
 449                                fi
 450                        ) <&3 3<&- ||
 451                        die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")"
 452                fi
 453        done
 454}
 455
 456#
 457# Register submodules in .git/config
 458#
 459# $@ = requested paths (default to all)
 460#
 461cmd_init()
 462{
 463        # parse $args after "submodule ... init".
 464        while test $# -ne 0
 465        do
 466                case "$1" in
 467                -q|--quiet)
 468                        GIT_QUIET=1
 469                        ;;
 470                --)
 471                        shift
 472                        break
 473                        ;;
 474                -*)
 475                        usage
 476                        ;;
 477                *)
 478                        break
 479                        ;;
 480                esac
 481                shift
 482        done
 483
 484        module_list "$@" |
 485        while read mode sha1 stage sm_path
 486        do
 487                die_if_unmatched "$mode"
 488                name=$(module_name "$sm_path") || exit
 489
 490                # Copy url setting when it is not set yet
 491                if test -z "$(git config "submodule.$name.url")"
 492                then
 493                        url=$(git config -f .gitmodules submodule."$name".url)
 494                        test -z "$url" &&
 495                        die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
 496
 497                        # Possibly a url relative to parent
 498                        case "$url" in
 499                        ./*|../*)
 500                                url=$(resolve_relative_url "$url") || exit
 501                                ;;
 502                        esac
 503                        git config submodule."$name".url "$url" ||
 504                        die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
 505
 506                        say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
 507                fi
 508
 509                # Copy "update" setting when it is not set yet
 510                upd="$(git config -f .gitmodules submodule."$name".update)"
 511                test -z "$upd" ||
 512                test -n "$(git config submodule."$name".update)" ||
 513                git config submodule."$name".update "$upd" ||
 514                die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
 515        done
 516}
 517
 518#
 519# Update each submodule path to correct revision, using clone and checkout as needed
 520#
 521# $@ = requested paths (default to all)
 522#
 523cmd_update()
 524{
 525        # parse $args after "submodule ... update".
 526        orig_flags=
 527        while test $# -ne 0
 528        do
 529                case "$1" in
 530                -q|--quiet)
 531                        GIT_QUIET=1
 532                        ;;
 533                -i|--init)
 534                        init=1
 535                        ;;
 536                -N|--no-fetch)
 537                        nofetch=1
 538                        ;;
 539                -f|--force)
 540                        force=$1
 541                        ;;
 542                -r|--rebase)
 543                        update="rebase"
 544                        ;;
 545                --reference)
 546                        case "$2" in '') usage ;; esac
 547                        reference="--reference=$2"
 548                        orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
 549                        shift
 550                        ;;
 551                --reference=*)
 552                        reference="$1"
 553                        ;;
 554                -m|--merge)
 555                        update="merge"
 556                        ;;
 557                --recursive)
 558                        recursive=1
 559                        ;;
 560                --checkout)
 561                        update="checkout"
 562                        ;;
 563                --)
 564                        shift
 565                        break
 566                        ;;
 567                -*)
 568                        usage
 569                        ;;
 570                *)
 571                        break
 572                        ;;
 573                esac
 574                orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
 575                shift
 576        done
 577
 578        if test -n "$init"
 579        then
 580                cmd_init "--" "$@" || return
 581        fi
 582
 583        cloned_modules=
 584        module_list "$@" | {
 585        err=
 586        while read mode sha1 stage sm_path
 587        do
 588                die_if_unmatched "$mode"
 589                if test "$stage" = U
 590                then
 591                        echo >&2 "Skipping unmerged submodule $sm_path"
 592                        continue
 593                fi
 594                name=$(module_name "$sm_path") || exit
 595                url=$(git config submodule."$name".url)
 596                if ! test -z "$update"
 597                then
 598                        update_module=$update
 599                else
 600                        update_module=$(git config submodule."$name".update)
 601                fi
 602
 603                if test "$update_module" = "none"
 604                then
 605                        echo "Skipping submodule '$sm_path'"
 606                        continue
 607                fi
 608
 609                if test -z "$url"
 610                then
 611                        # Only mention uninitialized submodules when its
 612                        # path have been specified
 613                        test "$#" != "0" &&
 614                        say "$(eval_gettext "Submodule path '\$sm_path' not initialized
 615Maybe you want to use 'update --init'?")"
 616                        continue
 617                fi
 618
 619                if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
 620                then
 621                        module_clone "$sm_path" "$name" "$url" "$reference" || exit
 622                        cloned_modules="$cloned_modules;$name"
 623                        subsha1=
 624                else
 625                        subsha1=$(clear_local_git_env; cd "$sm_path" &&
 626                                git rev-parse --verify HEAD) ||
 627                        die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")"
 628                fi
 629
 630                if test "$subsha1" != "$sha1" -o -n "$force"
 631                then
 632                        subforce=$force
 633                        # If we don't already have a -f flag and the submodule has never been checked out
 634                        if test -z "$subsha1" -a -z "$force"
 635                        then
 636                                subforce="-f"
 637                        fi
 638
 639                        if test -z "$nofetch"
 640                        then
 641                                # Run fetch only if $sha1 isn't present or it
 642                                # is not reachable from a ref.
 643                                (clear_local_git_env; cd "$sm_path" &&
 644                                        ( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
 645                                         test -z "$rev") || git-fetch)) ||
 646                                die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
 647                        fi
 648
 649                        # Is this something we just cloned?
 650                        case ";$cloned_modules;" in
 651                        *";$name;"*)
 652                                # then there is no local change to integrate
 653                                update_module= ;;
 654                        esac
 655
 656                        must_die_on_failure=
 657                        case "$update_module" in
 658                        rebase)
 659                                command="git rebase"
 660                                die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$sm_path'")"
 661                                say_msg="$(eval_gettext "Submodule path '\$sm_path': rebased into '\$sha1'")"
 662                                must_die_on_failure=yes
 663                                ;;
 664                        merge)
 665                                command="git merge"
 666                                die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$sm_path'")"
 667                                say_msg="$(eval_gettext "Submodule path '\$sm_path': merged in '\$sha1'")"
 668                                must_die_on_failure=yes
 669                                ;;
 670                        *)
 671                                command="git checkout $subforce -q"
 672                                die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$sm_path'")"
 673                                say_msg="$(eval_gettext "Submodule path '\$sm_path': checked out '\$sha1'")"
 674                                ;;
 675                        esac
 676
 677                        if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
 678                        then
 679                                say "$say_msg"
 680                        elif test -n "$must_die_on_failure"
 681                        then
 682                                die_with_status 2 "$die_msg"
 683                        else
 684                                err="${err};$die_msg"
 685                                continue
 686                        fi
 687                fi
 688
 689                if test -n "$recursive"
 690                then
 691                        (clear_local_git_env; cd "$sm_path" && eval cmd_update "$orig_flags")
 692                        res=$?
 693                        if test $res -gt 0
 694                        then
 695                                die_msg="$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
 696                                if test $res -eq 1
 697                                then
 698                                        err="${err};$die_msg"
 699                                        continue
 700                                else
 701                                        die_with_status $res "$die_msg"
 702                                fi
 703                        fi
 704                fi
 705        done
 706
 707        if test -n "$err"
 708        then
 709                OIFS=$IFS
 710                IFS=';'
 711                for e in $err
 712                do
 713                        if test -n "$e"
 714                        then
 715                                echo >&2 "$e"
 716                        fi
 717                done
 718                IFS=$OIFS
 719                exit 1
 720        fi
 721        }
 722}
 723
 724set_name_rev () {
 725        revname=$( (
 726                clear_local_git_env
 727                cd "$1" && {
 728                        git describe "$2" 2>/dev/null ||
 729                        git describe --tags "$2" 2>/dev/null ||
 730                        git describe --contains "$2" 2>/dev/null ||
 731                        git describe --all --always "$2"
 732                }
 733        ) )
 734        test -z "$revname" || revname=" ($revname)"
 735}
 736#
 737# Show commit summary for submodules in index or working tree
 738#
 739# If '--cached' is given, show summary between index and given commit,
 740# or between working tree and given commit
 741#
 742# $@ = [commit (default 'HEAD'),] requested paths (default all)
 743#
 744cmd_summary() {
 745        summary_limit=-1
 746        for_status=
 747        diff_cmd=diff-index
 748
 749        # parse $args after "submodule ... summary".
 750        while test $# -ne 0
 751        do
 752                case "$1" in
 753                --cached)
 754                        cached="$1"
 755                        ;;
 756                --files)
 757                        files="$1"
 758                        ;;
 759                --for-status)
 760                        for_status="$1"
 761                        ;;
 762                -n|--summary-limit)
 763                        if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
 764                        then
 765                                :
 766                        else
 767                                usage
 768                        fi
 769                        shift
 770                        ;;
 771                --)
 772                        shift
 773                        break
 774                        ;;
 775                -*)
 776                        usage
 777                        ;;
 778                *)
 779                        break
 780                        ;;
 781                esac
 782                shift
 783        done
 784
 785        test $summary_limit = 0 && return
 786
 787        if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
 788        then
 789                head=$rev
 790                test $# = 0 || shift
 791        elif test -z "$1" -o "$1" = "HEAD"
 792        then
 793                # before the first commit: compare with an empty tree
 794                head=$(git hash-object -w -t tree --stdin </dev/null)
 795                test -z "$1" || shift
 796        else
 797                head="HEAD"
 798        fi
 799
 800        if [ -n "$files" ]
 801        then
 802                test -n "$cached" &&
 803                die "$(gettext "The --cached option cannot be used with the --files option")"
 804                diff_cmd=diff-files
 805                head=
 806        fi
 807
 808        cd_to_toplevel
 809        # Get modified modules cared by user
 810        modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
 811                sane_egrep '^:([0-7]* )?160000' |
 812                while read mod_src mod_dst sha1_src sha1_dst status name
 813                do
 814                        # Always show modules deleted or type-changed (blob<->module)
 815                        test $status = D -o $status = T && echo "$name" && continue
 816                        # Also show added or modified modules which are checked out
 817                        GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
 818                        echo "$name"
 819                done
 820        )
 821
 822        test -z "$modules" && return
 823
 824        git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
 825        sane_egrep '^:([0-7]* )?160000' |
 826        cut -c2- |
 827        while read mod_src mod_dst sha1_src sha1_dst status name
 828        do
 829                if test -z "$cached" &&
 830                        test $sha1_dst = 0000000000000000000000000000000000000000
 831                then
 832                        case "$mod_dst" in
 833                        160000)
 834                                sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
 835                                ;;
 836                        100644 | 100755 | 120000)
 837                                sha1_dst=$(git hash-object $name)
 838                                ;;
 839                        000000)
 840                                ;; # removed
 841                        *)
 842                                # unexpected type
 843                                eval_gettextln "unexpected mode \$mod_dst" >&2
 844                                continue ;;
 845                        esac
 846                fi
 847                missing_src=
 848                missing_dst=
 849
 850                test $mod_src = 160000 &&
 851                ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
 852                missing_src=t
 853
 854                test $mod_dst = 160000 &&
 855                ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
 856                missing_dst=t
 857
 858                total_commits=
 859                case "$missing_src,$missing_dst" in
 860                t,)
 861                        errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_src")"
 862                        ;;
 863                ,t)
 864                        errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_dst")"
 865                        ;;
 866                t,t)
 867                        errmsg="$(eval_gettext "  Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
 868                        ;;
 869                *)
 870                        errmsg=
 871                        total_commits=$(
 872                        if test $mod_src = 160000 -a $mod_dst = 160000
 873                        then
 874                                range="$sha1_src...$sha1_dst"
 875                        elif test $mod_src = 160000
 876                        then
 877                                range=$sha1_src
 878                        else
 879                                range=$sha1_dst
 880                        fi
 881                        GIT_DIR="$name/.git" \
 882                        git rev-list --first-parent $range -- | wc -l
 883                        )
 884                        total_commits=" ($(($total_commits + 0)))"
 885                        ;;
 886                esac
 887
 888                sha1_abbr_src=$(echo $sha1_src | cut -c1-7)
 889                sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
 890                if test $status = T
 891                then
 892                        blob="$(gettext "blob")"
 893                        submodule="$(gettext "submodule")"
 894                        if test $mod_dst = 160000
 895                        then
 896                                echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
 897                        else
 898                                echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
 899                        fi
 900                else
 901                        echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
 902                fi
 903                if test -n "$errmsg"
 904                then
 905                        # Don't give error msg for modification whose dst is not submodule
 906                        # i.e. deleted or changed to blob
 907                        test $mod_dst = 160000 && echo "$errmsg"
 908                else
 909                        if test $mod_src = 160000 -a $mod_dst = 160000
 910                        then
 911                                limit=
 912                                test $summary_limit -gt 0 && limit="-$summary_limit"
 913                                GIT_DIR="$name/.git" \
 914                                git log $limit --pretty='format:  %m %s' \
 915                                --first-parent $sha1_src...$sha1_dst
 916                        elif test $mod_dst = 160000
 917                        then
 918                                GIT_DIR="$name/.git" \
 919                                git log --pretty='format:  > %s' -1 $sha1_dst
 920                        else
 921                                GIT_DIR="$name/.git" \
 922                                git log --pretty='format:  < %s' -1 $sha1_src
 923                        fi
 924                        echo
 925                fi
 926                echo
 927        done |
 928        if test -n "$for_status"; then
 929                if [ -n "$files" ]; then
 930                        gettextln "# Submodules changed but not updated:"
 931                else
 932                        gettextln "# Submodule changes to be committed:"
 933                fi
 934                echo "#"
 935                sed -e 's|^|# |' -e 's|^# $|#|'
 936        else
 937                cat
 938        fi
 939}
 940#
 941# List all submodules, prefixed with:
 942#  - submodule not initialized
 943#  + different revision checked out
 944#
 945# If --cached was specified the revision in the index will be printed
 946# instead of the currently checked out revision.
 947#
 948# $@ = requested paths (default to all)
 949#
 950cmd_status()
 951{
 952        # parse $args after "submodule ... status".
 953        while test $# -ne 0
 954        do
 955                case "$1" in
 956                -q|--quiet)
 957                        GIT_QUIET=1
 958                        ;;
 959                --cached)
 960                        cached=1
 961                        ;;
 962                --recursive)
 963                        recursive=1
 964                        ;;
 965                --)
 966                        shift
 967                        break
 968                        ;;
 969                -*)
 970                        usage
 971                        ;;
 972                *)
 973                        break
 974                        ;;
 975                esac
 976                shift
 977        done
 978
 979        module_list "$@" |
 980        while read mode sha1 stage sm_path
 981        do
 982                die_if_unmatched "$mode"
 983                name=$(module_name "$sm_path") || exit
 984                url=$(git config submodule."$name".url)
 985                displaypath="$prefix$sm_path"
 986                if test "$stage" = U
 987                then
 988                        say "U$sha1 $displaypath"
 989                        continue
 990                fi
 991                if test -z "$url" || ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
 992                then
 993                        say "-$sha1 $displaypath"
 994                        continue;
 995                fi
 996                set_name_rev "$sm_path" "$sha1"
 997                if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
 998                then
 999                        say " $sha1 $displaypath$revname"
1000                else
1001                        if test -z "$cached"
1002                        then
1003                                sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD)
1004                                set_name_rev "$sm_path" "$sha1"
1005                        fi
1006                        say "+$sha1 $displaypath$revname"
1007                fi
1008
1009                if test -n "$recursive"
1010                then
1011                        (
1012                                prefix="$displaypath/"
1013                                clear_local_git_env
1014                                cd "$sm_path" &&
1015                                eval cmd_status
1016                        ) ||
1017                        die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
1018                fi
1019        done
1020}
1021#
1022# Sync remote urls for submodules
1023# This makes the value for remote.$remote.url match the value
1024# specified in .gitmodules.
1025#
1026cmd_sync()
1027{
1028        while test $# -ne 0
1029        do
1030                case "$1" in
1031                -q|--quiet)
1032                        GIT_QUIET=1
1033                        shift
1034                        ;;
1035                --recursive)
1036                        recursive=1
1037                        shift
1038                        ;;
1039                --)
1040                        shift
1041                        break
1042                        ;;
1043                -*)
1044                        usage
1045                        ;;
1046                *)
1047                        break
1048                        ;;
1049                esac
1050        done
1051        cd_to_toplevel
1052        module_list "$@" |
1053        while read mode sha1 stage sm_path
1054        do
1055                die_if_unmatched "$mode"
1056                name=$(module_name "$sm_path")
1057                url=$(git config -f .gitmodules --get submodule."$name".url)
1058
1059                # Possibly a url relative to parent
1060                case "$url" in
1061                ./*|../*)
1062                        # rewrite foo/bar as ../.. to find path from
1063                        # submodule work tree to superproject work tree
1064                        up_path="$(echo "$sm_path" | sed "s/[^/][^/]*/../g")" &&
1065                        # guarantee a trailing /
1066                        up_path=${up_path%/}/ &&
1067                        # path from submodule work tree to submodule origin repo
1068                        sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
1069                        # path from superproject work tree to submodule origin repo
1070                        super_config_url=$(resolve_relative_url "$url") || exit
1071                        ;;
1072                *)
1073                        sub_origin_url="$url"
1074                        super_config_url="$url"
1075                        ;;
1076                esac
1077
1078                if git config "submodule.$name.url" >/dev/null 2>/dev/null
1079                then
1080                        say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")"
1081                        git config submodule."$name".url "$super_config_url"
1082
1083                        if test -e "$sm_path"/.git
1084                        then
1085                        (
1086                                clear_local_git_env
1087                                cd "$sm_path"
1088                                remote=$(get_default_remote)
1089                                git config remote."$remote".url "$sub_origin_url"
1090
1091                                if test -n "$recursive"
1092                                then
1093                                        prefix="$prefix$sm_path/"
1094                                        eval cmd_sync
1095                                fi
1096                        )
1097                        fi
1098                fi
1099        done
1100}
1101
1102# This loop parses the command line arguments to find the
1103# subcommand name to dispatch.  Parsing of the subcommand specific
1104# options are primarily done by the subcommand implementations.
1105# Subcommand specific options such as --branch and --cached are
1106# parsed here as well, for backward compatibility.
1107
1108while test $# != 0 && test -z "$command"
1109do
1110        case "$1" in
1111        add | foreach | init | update | status | summary | sync)
1112                command=$1
1113                ;;
1114        -q|--quiet)
1115                GIT_QUIET=1
1116                ;;
1117        -b|--branch)
1118                case "$2" in
1119                '')
1120                        usage
1121                        ;;
1122                esac
1123                branch="$2"; shift
1124                ;;
1125        --cached)
1126                cached="$1"
1127                ;;
1128        --)
1129                break
1130                ;;
1131        -*)
1132                usage
1133                ;;
1134        *)
1135                break
1136                ;;
1137        esac
1138        shift
1139done
1140
1141# No command word defaults to "status"
1142if test -z "$command"
1143then
1144    if test $# = 0
1145    then
1146        command=status
1147    else
1148        usage
1149    fi
1150fi
1151
1152# "-b branch" is accepted only by "add"
1153if test -n "$branch" && test "$command" != add
1154then
1155        usage
1156fi
1157
1158# "--cached" is accepted only by "status" and "summary"
1159if test -n "$cached" && test "$command" != status -a "$command" != summary
1160then
1161        usage
1162fi
1163
1164"cmd_$command" "$@"