afbaec7a742b7bc93d7ca889f472401619f33ec7
   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                revname=$(unset GIT_DIR && cd "$path" && git describe --tags $sha1)
 240                set_name_rev "$path" "$sha1"
 241                if git diff-files --quiet -- "$path"
 242                then
 243                        say " $sha1 $path$revname"
 244                else
 245                        if test -z "$cached"
 246                        then
 247                                sha1=$(unset GIT_DIR && cd "$path" && git rev-parse --verify HEAD)
 248                                set_name_rev "$path" "$sha1"
 249                        fi
 250                        say "+$sha1 $path$revname"
 251                fi
 252        done
 253}
 254
 255while case "$#" in 0) break ;; esac
 256do
 257        case "$1" in
 258        add)
 259                add=1
 260                ;;
 261        init)
 262                init=1
 263                ;;
 264        update)
 265                update=1
 266                ;;
 267        status)
 268                status=1
 269                ;;
 270        -q|--quiet)
 271                quiet=1
 272                ;;
 273        -b|--branch)
 274                case "$2" in
 275                '')
 276                        usage
 277                        ;;
 278                esac
 279                branch="$2"; shift
 280                ;;
 281        --cached)
 282                cached=1
 283                ;;
 284        --)
 285                break
 286                ;;
 287        -*)
 288                usage
 289                ;;
 290        *)
 291                break
 292                ;;
 293        esac
 294        shift
 295done
 296
 297case "$add,$branch" in
 2981,*)
 299        ;;
 300,)
 301        ;;
 302,*)
 303        usage
 304        ;;
 305esac
 306
 307case "$add,$init,$update,$status,$cached" in
 3081,,,,)
 309        module_add "$@"
 310        ;;
 311,1,,,)
 312        modules_init "$@"
 313        ;;
 314,,1,,)
 315        modules_update "$@"
 316        ;;
 317,,,1,*)
 318        modules_list "$@"
 319        ;;
 320*)
 321        usage
 322        ;;
 323esac