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