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