git-submodule.shon commit git submodule update: Introduce --recursive to update nested submodules (b13fd5c)
   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] [--reference <repository>] [--] <repository> <path>
   9   or: $dashless [--quiet] status [--cached] [--] [<path>...]
  10   or: $dashless [--quiet] init [--] [<path>...]
  11   or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
  12   or: $dashless [--quiet] summary [--cached] [--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-parse-remote
  18require_work_tree
  19
  20command=
  21branch=
  22reference=
  23cached=
  24nofetch=
  25update=
  26prefix=
  27
  28# Resolve relative url by appending to parent's url
  29resolve_relative_url ()
  30{
  31        remote=$(get_default_remote)
  32        remoteurl=$(git config "remote.$remote.url") ||
  33                die "remote ($remote) does not have a url defined in .git/config"
  34        url="$1"
  35        remoteurl=${remoteurl%/}
  36        while test -n "$url"
  37        do
  38                case "$url" in
  39                ../*)
  40                        url="${url#../}"
  41                        remoteurl="${remoteurl%/*}"
  42                        ;;
  43                ./*)
  44                        url="${url#./}"
  45                        ;;
  46                *)
  47                        break;;
  48                esac
  49        done
  50        echo "$remoteurl/${url%/}"
  51}
  52
  53#
  54# Get submodule info for registered submodules
  55# $@ = path to limit submodule list
  56#
  57module_list()
  58{
  59        git ls-files --error-unmatch --stage -- "$@" | grep '^160000 '
  60}
  61
  62#
  63# Map submodule path to submodule name
  64#
  65# $1 = path
  66#
  67module_name()
  68{
  69        # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
  70        re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
  71        name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
  72                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
  73       test -z "$name" &&
  74       die "No submodule mapping found in .gitmodules for path '$path'"
  75       echo "$name"
  76}
  77
  78#
  79# Clone a submodule
  80#
  81# Prior to calling, cmd_update checks that a possibly existing
  82# path is not a git repository.
  83# Likewise, cmd_add checks that path does not exist at all,
  84# since it is the location of a new submodule.
  85#
  86module_clone()
  87{
  88        path=$1
  89        url=$2
  90        reference="$3"
  91
  92        # If there already is a directory at the submodule path,
  93        # expect it to be empty (since that is the default checkout
  94        # action) and try to remove it.
  95        # Note: if $path is a symlink to a directory the test will
  96        # succeed but the rmdir will fail. We might want to fix this.
  97        if test -d "$path"
  98        then
  99                rmdir "$path" 2>/dev/null ||
 100                die "Directory '$path' exist, but is neither empty nor a git repository"
 101        fi
 102
 103        test -e "$path" &&
 104        die "A file already exist at path '$path'"
 105
 106        if test -n "$reference"
 107        then
 108                git-clone "$reference" -n "$url" "$path"
 109        else
 110                git-clone -n "$url" "$path"
 111        fi ||
 112        die "Clone of '$url' into submodule path '$path' failed"
 113}
 114
 115#
 116# Add a new submodule to the working tree, .gitmodules and the index
 117#
 118# $@ = repo path
 119#
 120# optional branch is stored in global branch variable
 121#
 122cmd_add()
 123{
 124        # parse $args after "submodule ... add".
 125        while test $# -ne 0
 126        do
 127                case "$1" in
 128                -b | --branch)
 129                        case "$2" in '') usage ;; esac
 130                        branch=$2
 131                        shift
 132                        ;;
 133                -q|--quiet)
 134                        GIT_QUIET=1
 135                        ;;
 136                --reference)
 137                        case "$2" in '') usage ;; esac
 138                        reference="--reference=$2"
 139                        shift
 140                        ;;
 141                --reference=*)
 142                        reference="$1"
 143                        shift
 144                        ;;
 145                --)
 146                        shift
 147                        break
 148                        ;;
 149                -*)
 150                        usage
 151                        ;;
 152                *)
 153                        break
 154                        ;;
 155                esac
 156                shift
 157        done
 158
 159        repo=$1
 160        path=$2
 161
 162        if test -z "$repo" -o -z "$path"; then
 163                usage
 164        fi
 165
 166        # assure repo is absolute or relative to parent
 167        case "$repo" in
 168        ./*|../*)
 169                # dereference source url relative to parent's url
 170                realrepo=$(resolve_relative_url "$repo") || exit
 171                ;;
 172        *:*|/*)
 173                # absolute url
 174                realrepo=$repo
 175                ;;
 176        *)
 177                die "repo URL: '$repo' must be absolute or begin with ./|../"
 178        ;;
 179        esac
 180
 181        # normalize path:
 182        # multiple //; leading ./; /./; /../; trailing /
 183        path=$(printf '%s/\n' "$path" |
 184                sed -e '
 185                        s|//*|/|g
 186                        s|^\(\./\)*||
 187                        s|/\./|/|g
 188                        :start
 189                        s|\([^/]*\)/\.\./||
 190                        tstart
 191                        s|/*$||
 192                ')
 193        git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
 194        die "'$path' already exists in the index"
 195
 196        # perhaps the path exists and is already a git repo, else clone it
 197        if test -e "$path"
 198        then
 199                if test -d "$path"/.git -o -f "$path"/.git
 200                then
 201                        echo "Adding existing repo at '$path' to the index"
 202                else
 203                        die "'$path' already exists and is not a valid git repo"
 204                fi
 205
 206                case "$repo" in
 207                ./*|../*)
 208                        url=$(resolve_relative_url "$repo") || exit
 209                    ;;
 210                *)
 211                        url="$repo"
 212                        ;;
 213                esac
 214                git config submodule."$path".url "$url"
 215        else
 216
 217                module_clone "$path" "$realrepo" "$reference" || exit
 218                (
 219                        unset GIT_DIR
 220                        cd "$path" &&
 221                        # ash fails to wordsplit ${branch:+-b "$branch"...}
 222                        case "$branch" in
 223                        '') git checkout -f -q ;;
 224                        ?*) git checkout -f -q -b "$branch" "origin/$branch" ;;
 225                        esac
 226                ) || die "Unable to checkout submodule '$path'"
 227        fi
 228
 229        git add "$path" ||
 230        die "Failed to add submodule '$path'"
 231
 232        git config -f .gitmodules submodule."$path".path "$path" &&
 233        git config -f .gitmodules submodule."$path".url "$repo" &&
 234        git add .gitmodules ||
 235        die "Failed to register submodule '$path'"
 236}
 237
 238#
 239# Execute an arbitrary command sequence in each checked out
 240# submodule
 241#
 242# $@ = command to execute
 243#
 244cmd_foreach()
 245{
 246        # parse $args after "submodule ... foreach".
 247        while test $# -ne 0
 248        do
 249                case "$1" in
 250                -q|--quiet)
 251                        GIT_QUIET=1
 252                        ;;
 253                --recursive)
 254                        recursive=1
 255                        ;;
 256                -*)
 257                        usage
 258                        ;;
 259                *)
 260                        break
 261                        ;;
 262                esac
 263                shift
 264        done
 265
 266        module_list |
 267        while read mode sha1 stage path
 268        do
 269                if test -e "$path"/.git
 270                then
 271                        say "Entering '$prefix$path'"
 272                        name=$(module_name "$path")
 273                        (
 274                                prefix="$prefix$path/"
 275                                unset GIT_DIR
 276                                cd "$path" &&
 277                                eval "$@" &&
 278                                if test -n "$recursive"
 279                                then
 280                                        cmd_foreach "--recursive" "$@"
 281                                fi
 282                        ) ||
 283                        die "Stopping at '$path'; script returned non-zero status."
 284                fi
 285        done
 286}
 287
 288#
 289# Register submodules in .git/config
 290#
 291# $@ = requested paths (default to all)
 292#
 293cmd_init()
 294{
 295        # parse $args after "submodule ... init".
 296        while test $# -ne 0
 297        do
 298                case "$1" in
 299                -q|--quiet)
 300                        GIT_QUIET=1
 301                        ;;
 302                --)
 303                        shift
 304                        break
 305                        ;;
 306                -*)
 307                        usage
 308                        ;;
 309                *)
 310                        break
 311                        ;;
 312                esac
 313                shift
 314        done
 315
 316        module_list "$@" |
 317        while read mode sha1 stage path
 318        do
 319                # Skip already registered paths
 320                name=$(module_name "$path") || exit
 321                url=$(git config submodule."$name".url)
 322                test -z "$url" || continue
 323
 324                url=$(git config -f .gitmodules submodule."$name".url)
 325                test -z "$url" &&
 326                die "No url found for submodule path '$path' in .gitmodules"
 327
 328                # Possibly a url relative to parent
 329                case "$url" in
 330                ./*|../*)
 331                        url=$(resolve_relative_url "$url") || exit
 332                        ;;
 333                esac
 334
 335                git config submodule."$name".url "$url" ||
 336                die "Failed to register url for submodule path '$path'"
 337
 338                upd="$(git config -f .gitmodules submodule."$name".update)"
 339                test -z "$upd" ||
 340                git config submodule."$name".update "$upd" ||
 341                die "Failed to register update mode for submodule path '$path'"
 342
 343                say "Submodule '$name' ($url) registered for path '$path'"
 344        done
 345}
 346
 347#
 348# Update each submodule path to correct revision, using clone and checkout as needed
 349#
 350# $@ = requested paths (default to all)
 351#
 352cmd_update()
 353{
 354        # parse $args after "submodule ... update".
 355        orig_args="$@"
 356        while test $# -ne 0
 357        do
 358                case "$1" in
 359                -q|--quiet)
 360                        shift
 361                        GIT_QUIET=1
 362                        ;;
 363                -i|--init)
 364                        init=1
 365                        shift
 366                        ;;
 367                -N|--no-fetch)
 368                        shift
 369                        nofetch=1
 370                        ;;
 371                -r|--rebase)
 372                        shift
 373                        update="rebase"
 374                        ;;
 375                --reference)
 376                        case "$2" in '') usage ;; esac
 377                        reference="--reference=$2"
 378                        shift 2
 379                        ;;
 380                --reference=*)
 381                        reference="$1"
 382                        shift
 383                        ;;
 384                -m|--merge)
 385                        shift
 386                        update="merge"
 387                        ;;
 388                --recursive)
 389                        shift
 390                        recursive=1
 391                        ;;
 392                --)
 393                        shift
 394                        break
 395                        ;;
 396                -*)
 397                        usage
 398                        ;;
 399                *)
 400                        break
 401                        ;;
 402                esac
 403        done
 404
 405        if test -n "$init"
 406        then
 407                cmd_init "--" "$@" || return
 408        fi
 409
 410        module_list "$@" |
 411        while read mode sha1 stage path
 412        do
 413                name=$(module_name "$path") || exit
 414                url=$(git config submodule."$name".url)
 415                update_module=$(git config submodule."$name".update)
 416                if test -z "$url"
 417                then
 418                        # Only mention uninitialized submodules when its
 419                        # path have been specified
 420                        test "$#" != "0" &&
 421                        say "Submodule path '$path' not initialized" &&
 422                        say "Maybe you want to use 'update --init'?"
 423                        continue
 424                fi
 425
 426                if ! test -d "$path"/.git -o -f "$path"/.git
 427                then
 428                        module_clone "$path" "$url" "$reference"|| exit
 429                        subsha1=
 430                else
 431                        subsha1=$(unset GIT_DIR; cd "$path" &&
 432                                git rev-parse --verify HEAD) ||
 433                        die "Unable to find current revision in submodule path '$path'"
 434                fi
 435
 436                if ! test -z "$update"
 437                then
 438                        update_module=$update
 439                fi
 440
 441                if test "$subsha1" != "$sha1"
 442                then
 443                        force=
 444                        if test -z "$subsha1"
 445                        then
 446                                force="-f"
 447                        fi
 448
 449                        if test -z "$nofetch"
 450                        then
 451                                (unset GIT_DIR; cd "$path" &&
 452                                        git-fetch) ||
 453                                die "Unable to fetch in submodule path '$path'"
 454                        fi
 455
 456                        case "$update_module" in
 457                        rebase)
 458                                command="git rebase"
 459                                action="rebase"
 460                                msg="rebased onto"
 461                                ;;
 462                        merge)
 463                                command="git merge"
 464                                action="merge"
 465                                msg="merged in"
 466                                ;;
 467                        *)
 468                                command="git checkout $force -q"
 469                                action="checkout"
 470                                msg="checked out"
 471                                ;;
 472                        esac
 473
 474                        (unset GIT_DIR; cd "$path" && $command "$sha1") ||
 475                        die "Unable to $action '$sha1' in submodule path '$path'"
 476                        say "Submodule path '$path': $msg '$sha1'"
 477                fi
 478
 479                if test -n "$recursive"
 480                then
 481                        (unset GIT_DIR; cd "$path" && cmd_update $orig_args) ||
 482                        die "Failed to recurse into submodule path '$path'"
 483                fi
 484        done
 485}
 486
 487set_name_rev () {
 488        revname=$( (
 489                unset GIT_DIR
 490                cd "$1" && {
 491                        git describe "$2" 2>/dev/null ||
 492                        git describe --tags "$2" 2>/dev/null ||
 493                        git describe --contains "$2" 2>/dev/null ||
 494                        git describe --all --always "$2"
 495                }
 496        ) )
 497        test -z "$revname" || revname=" ($revname)"
 498}
 499#
 500# Show commit summary for submodules in index or working tree
 501#
 502# If '--cached' is given, show summary between index and given commit,
 503# or between working tree and given commit
 504#
 505# $@ = [commit (default 'HEAD'),] requested paths (default all)
 506#
 507cmd_summary() {
 508        summary_limit=-1
 509        for_status=
 510
 511        # parse $args after "submodule ... summary".
 512        while test $# -ne 0
 513        do
 514                case "$1" in
 515                --cached)
 516                        cached="$1"
 517                        ;;
 518                --for-status)
 519                        for_status="$1"
 520                        ;;
 521                -n|--summary-limit)
 522                        if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
 523                        then
 524                                :
 525                        else
 526                                usage
 527                        fi
 528                        shift
 529                        ;;
 530                --)
 531                        shift
 532                        break
 533                        ;;
 534                -*)
 535                        usage
 536                        ;;
 537                *)
 538                        break
 539                        ;;
 540                esac
 541                shift
 542        done
 543
 544        test $summary_limit = 0 && return
 545
 546        if rev=$(git rev-parse -q --verify "$1^0")
 547        then
 548                head=$rev
 549                shift
 550        else
 551                head=HEAD
 552        fi
 553
 554        cd_to_toplevel
 555        # Get modified modules cared by user
 556        modules=$(git diff-index $cached --raw $head -- "$@" |
 557                egrep '^:([0-7]* )?160000' |
 558                while read mod_src mod_dst sha1_src sha1_dst status name
 559                do
 560                        # Always show modules deleted or type-changed (blob<->module)
 561                        test $status = D -o $status = T && echo "$name" && continue
 562                        # Also show added or modified modules which are checked out
 563                        GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
 564                        echo "$name"
 565                done
 566        )
 567
 568        test -z "$modules" && return
 569
 570        git diff-index $cached --raw $head -- $modules |
 571        egrep '^:([0-7]* )?160000' |
 572        cut -c2- |
 573        while read mod_src mod_dst sha1_src sha1_dst status name
 574        do
 575                if test -z "$cached" &&
 576                        test $sha1_dst = 0000000000000000000000000000000000000000
 577                then
 578                        case "$mod_dst" in
 579                        160000)
 580                                sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
 581                                ;;
 582                        100644 | 100755 | 120000)
 583                                sha1_dst=$(git hash-object $name)
 584                                ;;
 585                        000000)
 586                                ;; # removed
 587                        *)
 588                                # unexpected type
 589                                echo >&2 "unexpected mode $mod_dst"
 590                                continue ;;
 591                        esac
 592                fi
 593                missing_src=
 594                missing_dst=
 595
 596                test $mod_src = 160000 &&
 597                ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
 598                missing_src=t
 599
 600                test $mod_dst = 160000 &&
 601                ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
 602                missing_dst=t
 603
 604                total_commits=
 605                case "$missing_src,$missing_dst" in
 606                t,)
 607                        errmsg="  Warn: $name doesn't contain commit $sha1_src"
 608                        ;;
 609                ,t)
 610                        errmsg="  Warn: $name doesn't contain commit $sha1_dst"
 611                        ;;
 612                t,t)
 613                        errmsg="  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
 614                        ;;
 615                *)
 616                        errmsg=
 617                        total_commits=$(
 618                        if test $mod_src = 160000 -a $mod_dst = 160000
 619                        then
 620                                range="$sha1_src...$sha1_dst"
 621                        elif test $mod_src = 160000
 622                        then
 623                                range=$sha1_src
 624                        else
 625                                range=$sha1_dst
 626                        fi
 627                        GIT_DIR="$name/.git" \
 628                        git log --pretty=oneline --first-parent $range | wc -l
 629                        )
 630                        total_commits=" ($(($total_commits + 0)))"
 631                        ;;
 632                esac
 633
 634                sha1_abbr_src=$(echo $sha1_src | cut -c1-7)
 635                sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
 636                if test $status = T
 637                then
 638                        if test $mod_dst = 160000
 639                        then
 640                                echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:"
 641                        else
 642                                echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:"
 643                        fi
 644                else
 645                        echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
 646                fi
 647                if test -n "$errmsg"
 648                then
 649                        # Don't give error msg for modification whose dst is not submodule
 650                        # i.e. deleted or changed to blob
 651                        test $mod_dst = 160000 && echo "$errmsg"
 652                else
 653                        if test $mod_src = 160000 -a $mod_dst = 160000
 654                        then
 655                                limit=
 656                                test $summary_limit -gt 0 && limit="-$summary_limit"
 657                                GIT_DIR="$name/.git" \
 658                                git log $limit --pretty='format:  %m %s' \
 659                                --first-parent $sha1_src...$sha1_dst
 660                        elif test $mod_dst = 160000
 661                        then
 662                                GIT_DIR="$name/.git" \
 663                                git log --pretty='format:  > %s' -1 $sha1_dst
 664                        else
 665                                GIT_DIR="$name/.git" \
 666                                git log --pretty='format:  < %s' -1 $sha1_src
 667                        fi
 668                        echo
 669                fi
 670                echo
 671        done |
 672        if test -n "$for_status"; then
 673                echo "# Modified submodules:"
 674                echo "#"
 675                sed -e 's|^|# |' -e 's|^# $|#|'
 676        else
 677                cat
 678        fi
 679}
 680#
 681# List all submodules, prefixed with:
 682#  - submodule not initialized
 683#  + different revision checked out
 684#
 685# If --cached was specified the revision in the index will be printed
 686# instead of the currently checked out revision.
 687#
 688# $@ = requested paths (default to all)
 689#
 690cmd_status()
 691{
 692        # parse $args after "submodule ... status".
 693        while test $# -ne 0
 694        do
 695                case "$1" in
 696                -q|--quiet)
 697                        GIT_QUIET=1
 698                        ;;
 699                --cached)
 700                        cached=1
 701                        ;;
 702                --)
 703                        shift
 704                        break
 705                        ;;
 706                -*)
 707                        usage
 708                        ;;
 709                *)
 710                        break
 711                        ;;
 712                esac
 713                shift
 714        done
 715
 716        module_list "$@" |
 717        while read mode sha1 stage path
 718        do
 719                name=$(module_name "$path") || exit
 720                url=$(git config submodule."$name".url)
 721                if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
 722                then
 723                        say "-$sha1 $path"
 724                        continue;
 725                fi
 726                set_name_rev "$path" "$sha1"
 727                if git diff-files --quiet -- "$path"
 728                then
 729                        say " $sha1 $path$revname"
 730                else
 731                        if test -z "$cached"
 732                        then
 733                                sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
 734                                set_name_rev "$path" "$sha1"
 735                        fi
 736                        say "+$sha1 $path$revname"
 737                fi
 738        done
 739}
 740#
 741# Sync remote urls for submodules
 742# This makes the value for remote.$remote.url match the value
 743# specified in .gitmodules.
 744#
 745cmd_sync()
 746{
 747        while test $# -ne 0
 748        do
 749                case "$1" in
 750                -q|--quiet)
 751                        GIT_QUIET=1
 752                        shift
 753                        ;;
 754                --)
 755                        shift
 756                        break
 757                        ;;
 758                -*)
 759                        usage
 760                        ;;
 761                *)
 762                        break
 763                        ;;
 764                esac
 765        done
 766        cd_to_toplevel
 767        module_list "$@" |
 768        while read mode sha1 stage path
 769        do
 770                name=$(module_name "$path")
 771                url=$(git config -f .gitmodules --get submodule."$name".url)
 772
 773                # Possibly a url relative to parent
 774                case "$url" in
 775                ./*|../*)
 776                        url=$(resolve_relative_url "$url") || exit
 777                        ;;
 778                esac
 779
 780                if test -e "$path"/.git
 781                then
 782                (
 783                        unset GIT_DIR
 784                        cd "$path"
 785                        remote=$(get_default_remote)
 786                        say "Synchronizing submodule url for '$name'"
 787                        git config remote."$remote".url "$url"
 788                )
 789                fi
 790        done
 791}
 792
 793# This loop parses the command line arguments to find the
 794# subcommand name to dispatch.  Parsing of the subcommand specific
 795# options are primarily done by the subcommand implementations.
 796# Subcommand specific options such as --branch and --cached are
 797# parsed here as well, for backward compatibility.
 798
 799while test $# != 0 && test -z "$command"
 800do
 801        case "$1" in
 802        add | foreach | init | update | status | summary | sync)
 803                command=$1
 804                ;;
 805        -q|--quiet)
 806                GIT_QUIET=1
 807                ;;
 808        -b|--branch)
 809                case "$2" in
 810                '')
 811                        usage
 812                        ;;
 813                esac
 814                branch="$2"; shift
 815                ;;
 816        --cached)
 817                cached="$1"
 818                ;;
 819        --)
 820                break
 821                ;;
 822        -*)
 823                usage
 824                ;;
 825        *)
 826                break
 827                ;;
 828        esac
 829        shift
 830done
 831
 832# No command word defaults to "status"
 833test -n "$command" || command=status
 834
 835# "-b branch" is accepted only by "add"
 836if test -n "$branch" && test "$command" != add
 837then
 838        usage
 839fi
 840
 841# "--cached" is accepted only by "status" and "summary"
 842if test -n "$cached" && test "$command" != status -a "$command" != summary
 843then
 844        usage
 845fi
 846
 847"cmd_$command" "$@"