1#!/bin/sh
2#
3# git-submodules.sh: init, update or list git submodules
4#
5# Copyright (c) 2007 Lars Hjemli
6
7USAGE='[--quiet] [--cached] [status|init|update] [--] [<path>...]'
8. git-sh-setup
9require_work_tree
10
11init=
12update=
13status=
14quiet=
15cached=
16
17#
18# print stuff on stdout unless -q was specified
19#
20say()
21{
22 if test -z "$quiet"
23 then
24 echo "$@"
25 fi
26}
27
28#
29# Map submodule path to submodule name
30#
31# $1 = path
32#
33module_name()
34{
35 name=$(GIT_CONFIG=.gitmodules git-config --get-regexp '^submodule\..*\.path$' "$1" |
36 sed -nre 's/^submodule\.(.+)\.path .+$/\1/p')
37 test -z "$name" &&
38 die "No submodule mapping found in .gitmodules for path '$path'"
39 echo "$name"
40}
41
42#
43# Clone a submodule
44#
45module_clone()
46{
47 path=$1
48 url=$2
49
50 # If there already is a directory at the submodule path,
51 # expect it to be empty (since that is the default checkout
52 # action) and try to remove it.
53 # Note: if $path is a symlink to a directory the test will
54 # succeed but the rmdir will fail. We might want to fix this.
55 if test -d "$path"
56 then
57 rmdir "$path" 2>/dev/null ||
58 die "Directory '$path' exist, but is neither empty nor a git repository"
59 fi
60
61 test -e "$path" &&
62 die "A file already exist at path '$path'"
63
64 git-clone -n "$url" "$path" ||
65 die "Clone of '$url' into submodule path '$path' failed"
66}
67
68#
69# Register submodules in .git/config
70#
71# $@ = requested paths (default to all)
72#
73modules_init()
74{
75 git ls-files --stage -- "$@" | grep -e '^160000 ' |
76 while read mode sha1 stage path
77 do
78 # Skip already registered paths
79 name=$(module_name "$path") || exit
80 url=$(git-config submodule."$name".url)
81 test -z "$url" || continue
82
83 url=$(GIT_CONFIG=.gitmodules git-config submodule."$name".url)
84 test -z "$url" &&
85 die "No url found for submodule path '$path' in .gitmodules"
86
87 git-config submodule."$name".url "$url" ||
88 die "Failed to register url for submodule path '$path'"
89
90 say "Submodule '$name' ($url) registered for path '$path'"
91 done
92}
93
94#
95# Update each submodule path to correct revision, using clone and checkout as needed
96#
97# $@ = requested paths (default to all)
98#
99modules_update()
100{
101 git ls-files --stage -- "$@" | grep -e '^160000 ' |
102 while read mode sha1 stage path
103 do
104 name=$(module_name "$path") || exit
105 url=$(git-config submodule."$name".url)
106 if test -z "$url"
107 then
108 # Only mention uninitialized submodules when its
109 # path have been specified
110 test "$#" != "0" &&
111 say "Submodule path '$path' not initialized"
112 continue
113 fi
114
115 if ! test -d "$path"/.git
116 then
117 module_clone "$path" "$url" || exit
118 subsha1=
119 else
120 subsha1=$(unset GIT_DIR && cd "$path" &&
121 git-rev-parse --verify HEAD) ||
122 die "Unable to find current revision in submodule path '$path'"
123 fi
124
125 if test "$subsha1" != "$sha1"
126 then
127 (unset GIT_DIR && cd "$path" && git-fetch &&
128 git-checkout -q "$sha1") ||
129 die "Unable to checkout '$sha1' in submodule path '$path'"
130
131 say "Submodule path '$path': checked out '$sha1'"
132 fi
133 done
134}
135
136#
137# List all submodules, prefixed with:
138# - submodule not initialized
139# + different revision checked out
140#
141# If --cached was specified the revision in the index will be printed
142# instead of the currently checked out revision.
143#
144# $@ = requested paths (default to all)
145#
146modules_list()
147{
148 git ls-files --stage -- "$@" | grep -e '^160000 ' |
149 while read mode sha1 stage path
150 do
151 name=$(module_name "$path") || exit
152 url=$(git-config submodule."$name".url)
153 if test -z "url" || ! test -d "$path"/.git
154 then
155 say "-$sha1 $path"
156 continue;
157 fi
158 revname=$(unset GIT_DIR && cd "$path" && git-describe $sha1)
159 if git diff-files --quiet -- "$path"
160 then
161 say " $sha1 $path ($revname)"
162 else
163 if test -z "$cached"
164 then
165 sha1=$(unset GIT_DIR && cd "$path" && git-rev-parse --verify HEAD)
166 revname=$(unset GIT_DIR && cd "$path" && git-describe $sha1)
167 fi
168 say "+$sha1 $path ($revname)"
169 fi
170 done
171}
172
173while case "$#" in 0) break ;; esac
174do
175 case "$1" in
176 init)
177 init=1
178 ;;
179 update)
180 update=1
181 ;;
182 status)
183 status=1
184 ;;
185 -q|--quiet)
186 quiet=1
187 ;;
188 --cached)
189 cached=1
190 ;;
191 --)
192 break
193 ;;
194 -*)
195 usage
196 ;;
197 *)
198 break
199 ;;
200 esac
201 shift
202done
203
204case "$init,$update,$status,$cached" in
2051,,,)
206 modules_init "$@"
207 ;;
208,1,,)
209 modules_update "$@"
210 ;;
211,,*,*)
212 modules_list "$@"
213 ;;
214*)
215 usage
216 ;;
217esac