git-submodule.sh 6.2 KB
Newer Older
L
Lars Hjemli 已提交
1 2
#!/bin/sh
#
3
# git-submodules.sh: add, init, update or list git submodules
L
Lars Hjemli 已提交
4 5 6
#
# Copyright (c) 2007 Lars Hjemli

7
USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
L
Lars Hjemli 已提交
8 9 10
. git-sh-setup
require_work_tree

11 12
add=
branch=
L
Lars Hjemli 已提交
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
init=
update=
status=
quiet=
cached=

#
# print stuff on stdout unless -q was specified
#
say()
{
	if test -z "$quiet"
	then
		echo "$@"
	fi
}

30 31 32 33 34 35 36 37 38 39 40 41
# NEEDSWORK: identical function exists in get_repo_base in clone.sh
get_repo_base() {
	(
		cd "`/bin/pwd`" &&
		cd "$1" || cd "$1.git" &&
		{
			cd .git
			pwd
		}
	) 2>/dev/null
}

42 43 44 45 46 47 48 49 50 51 52 53 54
#
# Map submodule path to submodule name
#
# $1 = path
#
module_name()
{
       name=$(GIT_CONFIG=.gitmodules git-config --get-regexp '^submodule\..*\.path$' "$1" |
       sed -nre 's/^submodule\.(.+)\.path .+$/\1/p')
       test -z "$name" &&
       die "No submodule mapping found in .gitmodules for path '$path'"
       echo "$name"
}
55 56 57 58

#
# Clone a submodule
#
59 60 61 62 63
# Prior to calling, modules_update checks that a possibly existing
# path is not a git repository.
# Likewise, module_add checks that path does not exist at all,
# since it is the location of a new submodule.
#
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
module_clone()
{
	path=$1
	url=$2

	# If there already is a directory at the submodule path,
	# expect it to be empty (since that is the default checkout
	# action) and try to remove it.
	# Note: if $path is a symlink to a directory the test will
	# succeed but the rmdir will fail. We might want to fix this.
	if test -d "$path"
	then
		rmdir "$path" 2>/dev/null ||
		die "Directory '$path' exist, but is neither empty nor a git repository"
	fi

	test -e "$path" &&
	die "A file already exist at path '$path'"

	git-clone -n "$url" "$path" ||
84
	die "Clone of '$url' into submodule path '$path' failed"
85 86
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
#
# Add a new submodule to the working tree, .gitmodules and the index
#
# $@ = repo [path]
#
# optional branch is stored in global branch variable
#
module_add()
{
	repo=$1
	path=$2

	if test -z "$repo"; then
		usage
	fi

	# Turn the source into an absolute path if
	# it is local
	if base=$(get_repo_base "$repo"); then
		repo="$base"
	fi

	# Guess path from repo if not specified or strip trailing slashes
	if test -z "$path"; then
		path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
	else
		path=$(echo "$path" | sed -e 's|/*$||')
	fi

	test -e "$path" &&
	die "'$path' already exists"

	git-ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
	die "'$path' already exists in the index"

	module_clone "$path" "$repo" || exit
	(unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
	die "Unable to checkout submodule '$path'"
	git add "$path" ||
	die "Failed to add submodule '$path'"

	GIT_CONFIG=.gitmodules git config submodule."$path".path "$path" &&
	GIT_CONFIG=.gitmodules git config submodule."$path".url "$repo" &&
	git add .gitmodules ||
	die "Failed to register submodule '$path'"
}

L
Lars Hjemli 已提交
134
#
135
# Register submodules in .git/config
L
Lars Hjemli 已提交
136 137 138 139 140 141 142 143
#
# $@ = requested paths (default to all)
#
modules_init()
{
	git ls-files --stage -- "$@" | grep -e '^160000 ' |
	while read mode sha1 stage path
	do
144
		# Skip already registered paths
145 146
		name=$(module_name "$path") || exit
		url=$(git-config submodule."$name".url)
147
		test -z "$url" || continue
L
Lars Hjemli 已提交
148

149
		url=$(GIT_CONFIG=.gitmodules git-config submodule."$name".url)
L
Lars Hjemli 已提交
150
		test -z "$url" &&
151
		die "No url found for submodule path '$path' in .gitmodules"
L
Lars Hjemli 已提交
152

153 154
		git-config submodule."$name".url "$url" ||
		die "Failed to register url for submodule path '$path'"
L
Lars Hjemli 已提交
155

156
		say "Submodule '$name' ($url) registered for path '$path'"
L
Lars Hjemli 已提交
157 158 159 160
	done
}

#
161
# Update each submodule path to correct revision, using clone and checkout as needed
L
Lars Hjemli 已提交
162 163 164 165 166 167 168 169
#
# $@ = requested paths (default to all)
#
modules_update()
{
	git ls-files --stage -- "$@" | grep -e '^160000 ' |
	while read mode sha1 stage path
	do
170 171
		name=$(module_name "$path") || exit
		url=$(git-config submodule."$name".url)
172
		if test -z "$url"
L
Lars Hjemli 已提交
173 174 175 176
		then
			# Only mention uninitialized submodules when its
			# path have been specified
			test "$#" != "0" &&
177
			say "Submodule path '$path' not initialized"
178 179 180 181 182 183
			continue
		fi

		if ! test -d "$path"/.git
		then
			module_clone "$path" "$url" || exit
184 185 186 187
			subsha1=
		else
			subsha1=$(unset GIT_DIR && cd "$path" &&
				git-rev-parse --verify HEAD) ||
188
			die "Unable to find current revision in submodule path '$path'"
L
Lars Hjemli 已提交
189
		fi
190

L
Lars Hjemli 已提交
191 192 193 194
		if test "$subsha1" != "$sha1"
		then
			(unset GIT_DIR && cd "$path" && git-fetch &&
				git-checkout -q "$sha1") ||
195
			die "Unable to checkout '$sha1' in submodule path '$path'"
L
Lars Hjemli 已提交
196

197
			say "Submodule path '$path': checked out '$sha1'"
L
Lars Hjemli 已提交
198 199 200 201
		fi
	done
}

202 203 204 205 206 207 208 209 210 211 212 213
set_name_rev () {
	revname=$( (
		unset GIT_DIR &&
		cd "$1" && {
			git-describe "$2" 2>/dev/null ||
			git-describe --tags "$2" 2>/dev/null ||
			git-describe --contains --tags "$2"
		}
	) )
	test -z "$revname" || revname=" ($revname)"
}

L
Lars Hjemli 已提交
214
#
215
# List all submodules, prefixed with:
L
Lars Hjemli 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228
#  - submodule not initialized
#  + different revision checked out
#
# If --cached was specified the revision in the index will be printed
# instead of the currently checked out revision.
#
# $@ = requested paths (default to all)
#
modules_list()
{
	git ls-files --stage -- "$@" | grep -e '^160000 ' |
	while read mode sha1 stage path
	do
229 230 231
		name=$(module_name "$path") || exit
		url=$(git-config submodule."$name".url)
		if test -z "url" || ! test -d "$path"/.git
L
Lars Hjemli 已提交
232 233 234 235
		then
			say "-$sha1 $path"
			continue;
		fi
236 237
		revname=$(unset GIT_DIR && cd "$path" && git-describe --tags $sha1)
		set_name_rev "$path" $"sha1"
L
Lars Hjemli 已提交
238 239
		if git diff-files --quiet -- "$path"
		then
240
			say " $sha1 $path$revname"
L
Lars Hjemli 已提交
241 242 243 244
		else
			if test -z "$cached"
			then
				sha1=$(unset GIT_DIR && cd "$path" && git-rev-parse --verify HEAD)
245
				set_name_rev "$path" $"sha1"
L
Lars Hjemli 已提交
246
			fi
247
			say "+$sha1 $path$revname"
L
Lars Hjemli 已提交
248 249 250 251 252 253 254
		fi
	done
}

while case "$#" in 0) break ;; esac
do
	case "$1" in
255 256 257
	add)
		add=1
		;;
L
Lars Hjemli 已提交
258 259 260 261 262 263 264 265 266 267 268 269
	init)
		init=1
		;;
	update)
		update=1
		;;
	status)
		status=1
		;;
	-q|--quiet)
		quiet=1
		;;
270 271 272 273 274 275 276 277
	-b|--branch)
		case "$2" in
		'')
			usage
			;;
		esac
		branch="$2"; shift
		;;
L
Lars Hjemli 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
	--cached)
		cached=1
		;;
	--)
		break
		;;
	-*)
		usage
		;;
	*)
		break
		;;
	esac
	shift
done

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
case "$add,$branch" in
1,*)
	;;
,)
	;;
,*)
	usage
	;;
esac

case "$add,$init,$update,$status,$cached" in
1,,,,)
	module_add "$@"
	;;
,1,,,)
L
Lars Hjemli 已提交
309 310
	modules_init "$@"
	;;
311
,,1,,)
L
Lars Hjemli 已提交
312 313
	modules_update "$@"
	;;
314
,,,1,*)
L
Lars Hjemli 已提交
315 316 317 318 319 320
	modules_list "$@"
	;;
*)
	usage
	;;
esac