git-submodule.shon commit Remove old generated files from .gitignore. (feb7f38)
   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] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
   8OPTIONS_SPEC=
   9. git-sh-setup
  10require_work_tree
  11
  12add=
  13branch=
  14init=
  15update=
  16status=
  17quiet=
  18cached=
  19
  20#
  21# print stuff on stdout unless -q was specified
  22#
  23say()
  24{
  25        if test -z "$quiet"
  26        then
  27                echo "$@"
  28        fi
  29}
  30
  31# NEEDSWORK: identical function exists in get_repo_base in clone.sh
  32get_repo_base() {
  33        (
  34                cd "`/bin/pwd`" &&
  35                cd "$1" || cd "$1.git" &&
  36                {
  37                        cd .git
  38                        pwd
  39                }
  40        ) 2>/dev/null
  41}
  42
  43# Resolve relative url by appending to parent's url
  44resolve_relative_url ()
  45{
  46        branch="$(git symbolic-ref HEAD 2>/dev/null)"
  47        remote="$(git config branch.${branch#refs/heads/}.remote)"
  48        remote="${remote:-origin}"
  49        remoteurl="$(git config remote.$remote.url)" ||
  50                die "remote ($remote) does not have a url in .git/config"
  51        url="$1"
  52        while test -n "$url"
  53        do
  54                case "$url" in
  55                ../*)
  56                        url="${url#../}"
  57                        remoteurl="${remoteurl%/*}"
  58                        ;;
  59                ./*)
  60                        url="${url#./}"
  61                        ;;
  62                *)
  63                        break;;
  64                esac
  65        done
  66        echo "$remoteurl/$url"
  67}
  68
  69#
  70# Map submodule path to submodule name
  71#
  72# $1 = path
  73#
  74module_name()
  75{
  76        # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
  77        re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
  78        name=$( GIT_CONFIG=.gitmodules \
  79                git config --get-regexp '^submodule\..*\.path$' |
  80                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
  81       test -z "$name" &&
  82       die "No submodule mapping found in .gitmodules for path '$path'"
  83       echo "$name"
  84}
  85
  86#
  87# Clone a submodule
  88#
  89# Prior to calling, modules_update checks that a possibly existing
  90# path is not a git repository.
  91# Likewise, module_add checks that path does not exist at all,
  92# since it is the location of a new submodule.
  93#
  94module_clone()
  95{
  96        path=$1
  97        url=$2
  98
  99        # If there already is a directory at the submodule path,
 100        # expect it to be empty (since that is the default checkout
 101        # action) and try to remove it.
 102        # Note: if $path is a symlink to a directory the test will
 103        # succeed but the rmdir will fail. We might want to fix this.
 104        if test -d "$path"
 105        then
 106                rmdir "$path" 2>/dev/null ||
 107                die "Directory '$path' exist, but is neither empty nor a git repository"
 108        fi
 109
 110        test -e "$path" &&
 111        die "A file already exist at path '$path'"
 112
 113        git-clone -n "$url" "$path" ||
 114        die "Clone of '$url' into submodule path '$path' failed"
 115}
 116
 117#
 118# Add a new submodule to the working tree, .gitmodules and the index
 119#
 120# $@ = repo [path]
 121#
 122# optional branch is stored in global branch variable
 123#
 124module_add()
 125{
 126        repo=$1
 127        path=$2
 128
 129        if test -z "$repo"; then
 130                usage
 131        fi
 132
 133        case "$repo" in
 134        ./*|../*)
 135                # dereference source url relative to parent's url
 136                realrepo="$(resolve_relative_url $repo)" ;;
 137        *)
 138                # Turn the source into an absolute path if
 139                # it is local
 140                if base=$(get_repo_base "$repo"); then
 141                        repo="$base"
 142                fi
 143                realrepo=$repo
 144                ;;
 145        esac
 146
 147        # Guess path from repo if not specified or strip trailing slashes
 148        if test -z "$path"; then
 149                path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
 150        else
 151                path=$(echo "$path" | sed -e 's|/*$||')
 152        fi
 153
 154        test -e "$path" &&
 155        die "'$path' already exists"
 156
 157        git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
 158        die "'$path' already exists in the index"
 159
 160        module_clone "$path" "$realrepo" || exit
 161        (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
 162        die "Unable to checkout submodule '$path'"
 163        git add "$path" ||
 164        die "Failed to add submodule '$path'"
 165
 166        GIT_CONFIG=.gitmodules git config submodule."$path".path "$path" &&
 167        GIT_CONFIG=.gitmodules git config submodule."$path".url "$repo" &&
 168        git add .gitmodules ||
 169        die "Failed to register submodule '$path'"
 170}
 171
 172#
 173# Register submodules in .git/config
 174#
 175# $@ = requested paths (default to all)
 176#
 177modules_init()
 178{
 179        git ls-files --stage -- "$@" | grep -e '^160000 ' |
 180        while read mode sha1 stage path
 181        do
 182                # Skip already registered paths
 183                name=$(module_name "$path") || exit
 184                url=$(git config submodule."$name".url)
 185                test -z "$url" || continue
 186
 187                url=$(GIT_CONFIG=.gitmodules git config submodule."$name".url)
 188                test -z "$url" &&
 189                die "No url found for submodule path '$path' in .gitmodules"
 190
 191                # Possibly a url relative to parent
 192                case "$url" in
 193                ./*|../*)
 194                        url="$(resolve_relative_url "$url")"
 195                        ;;
 196                esac
 197
 198                git config submodule."$name".url "$url" ||
 199                die "Failed to register url for submodule path '$path'"
 200
 201                say "Submodule '$name' ($url) registered for path '$path'"
 202        done
 203}
 204
 205#
 206# Update each submodule path to correct revision, using clone and checkout as needed
 207#
 208# $@ = requested paths (default to all)
 209#
 210modules_update()
 211{
 212        git ls-files --stage -- "$@" | grep -e '^160000 ' |
 213        while read mode sha1 stage path
 214        do
 215                name=$(module_name "$path") || exit
 216                url=$(git config submodule."$name".url)
 217                if test -z "$url"
 218                then
 219                        # Only mention uninitialized submodules when its
 220                        # path have been specified
 221                        test "$#" != "0" &&
 222                        say "Submodule path '$path' not initialized"
 223                        continue
 224                fi
 225
 226                if ! test -d "$path"/.git
 227                then
 228                        module_clone "$path" "$url" || exit
 229                        subsha1=
 230                else
 231                        subsha1=$(unset GIT_DIR; cd "$path" &&
 232                                git rev-parse --verify HEAD) ||
 233                        die "Unable to find current revision in submodule path '$path'"
 234                fi
 235
 236                if test "$subsha1" != "$sha1"
 237                then
 238                        (unset GIT_DIR; cd "$path" && git-fetch &&
 239                                git-checkout -q "$sha1") ||
 240                        die "Unable to checkout '$sha1' in submodule path '$path'"
 241
 242                        say "Submodule path '$path': checked out '$sha1'"
 243                fi
 244        done
 245}
 246
 247set_name_rev () {
 248        revname=$( (
 249                unset GIT_DIR
 250                cd "$1" && {
 251                        git describe "$2" 2>/dev/null ||
 252                        git describe --tags "$2" 2>/dev/null ||
 253                        git describe --contains --tags "$2"
 254                }
 255        ) )
 256        test -z "$revname" || revname=" ($revname)"
 257}
 258
 259#
 260# List all submodules, prefixed with:
 261#  - submodule not initialized
 262#  + different revision checked out
 263#
 264# If --cached was specified the revision in the index will be printed
 265# instead of the currently checked out revision.
 266#
 267# $@ = requested paths (default to all)
 268#
 269modules_list()
 270{
 271        git ls-files --stage -- "$@" | grep -e '^160000 ' |
 272        while read mode sha1 stage path
 273        do
 274                name=$(module_name "$path") || exit
 275                url=$(git config submodule."$name".url)
 276                if test -z "url" || ! test -d "$path"/.git
 277                then
 278                        say "-$sha1 $path"
 279                        continue;
 280                fi
 281                set_name_rev "$path" "$sha1"
 282                if git diff-files --quiet -- "$path"
 283                then
 284                        say " $sha1 $path$revname"
 285                else
 286                        if test -z "$cached"
 287                        then
 288                                sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
 289                                set_name_rev "$path" "$sha1"
 290                        fi
 291                        say "+$sha1 $path$revname"
 292                fi
 293        done
 294}
 295
 296while test $# != 0
 297do
 298        case "$1" in
 299        add)
 300                add=1
 301                ;;
 302        init)
 303                init=1
 304                ;;
 305        update)
 306                update=1
 307                ;;
 308        status)
 309                status=1
 310                ;;
 311        -q|--quiet)
 312                quiet=1
 313                ;;
 314        -b|--branch)
 315                case "$2" in
 316                '')
 317                        usage
 318                        ;;
 319                esac
 320                branch="$2"; shift
 321                ;;
 322        --cached)
 323                cached=1
 324                ;;
 325        --)
 326                break
 327                ;;
 328        -*)
 329                usage
 330                ;;
 331        *)
 332                break
 333                ;;
 334        esac
 335        shift
 336done
 337
 338case "$add,$branch" in
 3391,*)
 340        ;;
 341,)
 342        ;;
 343,*)
 344        usage
 345        ;;
 346esac
 347
 348case "$add,$init,$update,$status,$cached" in
 3491,,,,)
 350        module_add "$@"
 351        ;;
 352,1,,,)
 353        modules_init "$@"
 354        ;;
 355,,1,,)
 356        modules_update "$@"
 357        ;;
 358,,,*,*)
 359        modules_list "$@"
 360        ;;
 361*)
 362        usage
 363        ;;
 364esac