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