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