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