Relocate the python file under appropriate hierarchy
[git-modules-bs.git] / git-submodule
blob28b1a2193a2a38c033e791995a49e960fba7ce5a
1 #!/bin/sh
3 # git-submodules.sh: add, init, update or list git submodules
4 # or recurse any git command over the submodules recursively.
6 # Copyright (c) 2007 Lars Hjemli
8 # Synopsis of this commands are as follows
9 # git-submodule [-q|--quiet] add [-b|--branch branch] <repository> [<path>]
10 # git-submodule [-q|--quiet] [status] [-c|--cached] [--] [<path>...]
11 # git-submodule [-q|--quiet] init [--] [<path>...]
12 # git-submodule [-q|--quiet] update [--] [<path>...]
13 # git-submodule [-q|--quiet] recurse [-i|--initialize] [-e|--exit-after-error] [-d|--depth <recursion depth>] [-df|--depth-first] [-ca|--customized-argument] [-p|--pre-command] <command> [<arguments> ...]
14 USAGE='[-q|--quiet] [[[add [-b|--branch branch] <repo>]|[[[status [-c|--cached]]|init|update] [--]]] [<path>...]]|[recurse [-i|--initialize] [-e|--exit-after-error] [-d|--depth <recursion depth>] [-df|--depth-first] [-ca|--customized-argument] [-p|--pre-command] <command> [<arguments> ...]]'
15 OPTIONS_SPEC=
16 . git-sh-setup
17 require_work_tree
19 MODULES_LIST='modules_list'
21 add=
22 branch=
23 quiet=
24 cached=
25 command=
26 depth=0
27 current_depth=0
28 auto_initialize=
29 depth_first=
30 use_custom_args=
31 custom_args=
32 pre_cmd=
33 on_error=
36 # print stuff on stdout unless -q was specified
38 say()
40 if test -z "$quiet"
41 then
42 echo "$@"
46 # NEEDSWORK: identical function exists in get_repo_base in clone.sh
47 get_repo_base() {
49 cd "`/bin/pwd`" &&
50 cd "$1" || cd "$1.git" &&
52 cd .git
53 pwd
55 ) 2>/dev/null
58 # Resolve relative url by appending to parent's url
59 resolve_relative_url ()
61 branch="$(git symbolic-ref HEAD 2>/dev/null)"
62 remote="$(git config branch.${branch#refs/heads/}.remote)"
63 remote="${remote:-origin}"
64 remoteurl="$(git config remote.$remote.url)" ||
65 die "remote ($remote) does not have a url in .git/config"
66 url="$1"
67 while test -n "$url"
69 case "$url" in
70 ../*)
71 url="${url#../}"
72 remoteurl="${remoteurl%/*}"
74 ./*)
75 url="${url#./}"
78 break;;
79 esac
80 done
81 echo "$remoteurl/$url"
85 # Map submodule path to submodule name
87 # $1 = path
89 module_name()
91 # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
92 re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
93 name=$( GIT_CONFIG=.gitmodules \
94 git config --get-regexp '^submodule\..*\.path$' |
95 sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
96 test -z "$name" &&
97 die "No submodule mapping found in .gitmodules for path '$path'"
98 echo "$name"
102 # Clone a submodule
104 # Prior to calling, modules_update checks that a possibly existing
105 # path is not a git repository.
106 # Likewise, module_add checks that path does not exist at all,
107 # since it is the location of a new submodule.
109 module_clone()
111 path=$1
112 url=$2
114 # If there already is a directory at the submodule path,
115 # expect it to be empty (since that is the default checkout
116 # action) and try to remove it.
117 # Note: if $path is a symlink to a directory the test will
118 # succeed but the rmdir will fail. We might want to fix this.
119 if test -d "$path"
120 then
121 rmdir "$path" 2>/dev/null ||
122 die "Directory '$path' exist, but is neither empty nor a git repository"
125 test -e "$path" &&
126 die "A file already exist at path '$path'"
128 git-clone -n "$url" "$path" ||
129 die "Clone of '$url' into submodule path '$path' failed"
132 # Parses the branch name and exits if not present
133 parse_branch_name()
135 branch="$1";
136 if test -z "$branch"
137 then
138 echo Branch name must me specified
139 usage
144 # Add a new submodule to the working tree, .gitmodules and the index
146 # $@ = repo [path]
148 # optional branch is stored in global branch variable
150 module_add()
152 case "$1" in
153 -b|--branch)
154 shift
155 parse_branch_name "$@" &&
156 shift
159 usage
161 esac
162 repo=$1
163 path=$2
165 if test -z "$repo"; then
166 usage
169 case "$repo" in
170 ./*|../*)
171 # dereference source url relative to parent's url
172 realrepo="$(resolve_relative_url $repo)" ;;
174 # Turn the source into an absolute path if
175 # it is local
176 if base=$(get_repo_base "$repo"); then
177 repo="$base"
179 realrepo=$repo
181 esac
183 # Guess path from repo if not specified or strip trailing slashes
184 if test -z "$path"; then
185 path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
186 else
187 path=$(echo "$path" | sed -e 's|/*$||')
190 test -e "$path" &&
191 die "'$path' already exists"
193 git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
194 die "'$path' already exists in the index"
196 module_clone "$path" "$realrepo" || exit
197 (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
198 die "Unable to checkout submodule '$path'"
199 git add "$path" ||
200 die "Failed to add submodule '$path'"
202 GIT_CONFIG=.gitmodules git config submodule."$path".path "$path" &&
203 GIT_CONFIG=.gitmodules git config submodule."$path".url "$repo" &&
204 git add .gitmodules ||
205 die "Failed to register submodule '$path'"
209 # Register submodules in .git/config
211 # $@ = requested paths (default to all)
213 modules_init()
215 # Added here to ensure that no argument is passed to be treated as
216 # parameter to the sub command. This will be used to parse any
217 # to the subcommand
218 case "$1" in
220 usage
222 esac
223 git ls-files --stage -- "$@" | grep -e '^160000 ' |
224 while read mode sha1 stage path
226 # Skip already registered paths
227 name=$(module_name "$path") || exit
228 url=$(git config submodule."$name".url)
229 test -z "$url" || continue
231 url=$(GIT_CONFIG=.gitmodules git config submodule."$name".url)
232 test -z "$url" &&
233 die "No url found for submodule path '$path' in .gitmodules"
235 # Possibly a url relative to parent
236 case "$url" in
237 ./*|../*)
238 url="$(resolve_relative_url "$url")"
240 esac
242 git config submodule."$name".url "$url" ||
243 die "Failed to register url for submodule path '$path'"
245 say "Submodule '$name' ($url) registered for path '$path'"
246 done
250 # Update each submodule path to correct revision, using clone and checkout as needed
252 # $@ = requested paths (default to all)
254 modules_update()
256 # Added here to ensure that no argument is passed to be treated as
257 # parameter to the sub command. This will be used to parse any
258 # to the subcommand
259 case "$1" in
261 usage
263 esac
264 git ls-files --stage -- "$@" | grep -e '^160000 ' |
265 while read mode sha1 stage path
267 name=$(module_name "$path") || exit
268 url=$(git config submodule."$name".url)
269 if test -z "$url"
270 then
271 # Only mention uninitialized submodules when its
272 # path have been specified
273 test "$#" != "0" &&
274 say "Submodule path '$path' not initialized"
275 continue
278 if ! test -d "$path"/.git
279 then
280 module_clone "$path" "$url" || exit
281 subsha1=
282 else
283 subsha1=$(unset GIT_DIR; cd "$path" &&
284 git rev-parse --verify HEAD) ||
285 die "Unable to find current revision in submodule path '$path'"
288 if test "$subsha1" != "$sha1"
289 then
290 (unset GIT_DIR; cd "$path" && git-fetch &&
291 git-checkout -q "$sha1") ||
292 die "Unable to checkout '$sha1' in submodule path '$path'"
294 say "Submodule path '$path': checked out '$sha1'"
296 done
299 set_name_rev () {
300 revname=$( (
301 unset GIT_DIR
302 cd "$1" && {
303 git describe "$2" 2>/dev/null ||
304 git describe --tags "$2" 2>/dev/null ||
305 git describe --contains --tags "$2"
308 test -z "$revname" || revname=" ($revname)"
312 # List all submodules, prefixed with:
313 # - submodule not initialized
314 # + different revision checked out
316 # If --cached was specified the revision in the index will be printed
317 # instead of the currently checked out revision.
319 # $@ = requested paths (default to all)
321 modules_list()
323 git ls-files --stage -- "$@" | grep -e '^160000 ' |
324 while read mode sha1 stage path
326 name=$(module_name "$path") || exit
327 url=$(git config submodule."$name".url)
328 if test -z "url" || ! test -d "$path"/.git
329 then
330 say "-$sha1 $path"
331 continue;
333 set_name_rev "$path" "$sha1"
334 if git diff-files --quiet -- "$path"
335 then
336 say " $sha1 $path$revname"
337 else
338 if test -z "$cached"
339 then
340 sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
341 set_name_rev "$path" "$sha1"
343 say "+$sha1 $path$revname"
345 done
348 # Delgates to modules_list after parsing its arguments
349 modules_status()
351 case "$1" in
352 -c|--cached)
353 shift
354 cached=1
357 usage
359 esac
360 "$MODULES_LIST" "$@"
363 # If there is '--' as the first argument simply ignores it and thus shifts
364 check_for_terminator()
366 if test -n "$1" && test "$1" = "--"
367 then
368 shift
372 # Initializes the submodule if already not initialized
373 # and auto initialize is enabled
374 initialize_sub_module()
376 if test ! -d "$1"/.git &&
377 test -n "$auto_initialize"
378 then
379 say "Initializing and updating $1"
380 git-submodule init "$1" &&
381 git-submodule update "$1" &&
382 return 0
383 # Returns true if module is already initialized
384 elif test -d "$1"/.git
385 then
386 return 0
388 say "Module $1 is not initialized and skipped"
389 return 1
392 # Take command from user and execute it until user wants to discontinue
393 do_pre_command()
395 say "Starting pre-comamnd execution!"
396 while :
399 read -p "Please provide a git command: " pre_command
400 test -z "$pre_command" || git "$pre_command"
402 read -p "Press y to continue with another git command... " keypress
403 if test "$keypress" != "y" &&
404 test "$keypress" != "Y"
405 then
406 break
408 done
411 # Take arguments from user to pass as custom arguments
412 get_custom_args()
414 read -p "Please provide arguments for this module: " custom_args
417 traverse_submodule()
419 # If current depth is the range specified than it will continue
420 # else return with success
421 if test "$depth" -gt 0 &&
422 test "$current_depth" -ge "$depth"
423 then
424 return 0;
426 # If submodules exists than it will traverse over them
427 if test -f .gitmodules
428 then
429 # Incrementing the depth for the next level of submodules
430 current_depth=$(($current_depth + 1))
431 for mod_path in `sed -n -e 's/path = //p' .gitmodules`; do
432 traverse_module "$mod_path" "$@"
433 done
434 # Decremented the depth to bring it back to the depth of
435 # the current module
436 current_depth=$(($current_depth - 1))
440 # This actually traverses the module; checks
441 # whether the module is initialized or not.
442 # if not initialized, then done so and then the
443 # intended command is evaluated. Then it
444 # recursively goes into it modules.
445 traverse_module()
447 # Will work in the module if and only if the module is initialized
448 initialize_sub_module "$1" &&
450 submod_path="$1"
451 shift
452 cd "$submod_path"
453 # If depth-first is specified in that case submodules are
454 # are traversed before executing the command on this module
455 test -n "$depth_first" && traverse_submodule "$@"
456 # pwd is mentioned in order to enable the ser to distinguish
457 # between same name modules, e.g. a/lib and b/lib.
458 say "Working in mod $submod_path" @ `pwd` "with $@ ($#)"
459 test -n "$pre_cmd" && do_pre_command
460 test -n "$use_custom_args" && get_custom_args
461 cmd_status=
462 git "$@" "$custom_args" || cmd_status=1
463 # if exit on error is specifed than script will exit if any
464 # command fails. As there is no transaction there will be
465 # no rollback either
466 if test -n "$cmd_status" && test -n "$on_error"
467 then
468 die "git $@ failed in module $submod_path @ $(pwd)"
470 # If depth-first is not specified in that case submodules are
471 # are traversed after executing the command on this module
472 test -z "$depth_first" && traverse_submodule "$@"
476 # Propagates or recurses over all the submodules at any
477 # depth with any git command, e.g. git-clone, git-status,
478 # git-commit etc., with the arguments supplied exactly as
479 # it would have been supplied to the command otherwise.
480 # This actually starts the recursive propagation
481 modules_recurse() {
482 while :
484 case "$1" in
485 -d|--depth)
486 shift
487 if test -z "$1"
488 then
489 echo "No <recursion depth> specified"
490 usage
491 # Arithmatic operation will give an error if depth is not number
492 # thus chose to check intergerness with regular expression
493 elif test "$(expr $1 : '[1-9][0-9]*')" -eq "$(expr $1 : '.*')"
494 then
495 depth="$1"
496 else
497 echo "<recursion depth> not an integer"
498 usage
501 -df|--depth-first)
502 depth_first=1
504 -e|--exit-after-error)
505 on_error=1
507 -i|--initialize)
508 auto_initialize=1
510 -p|--pre-command)
511 pre_cmd=1
513 -ca|--customized-argument)
514 use_custom_args=1
517 usage
520 break
522 esac
523 shift
524 done
525 test "$#" -le 0 && die "No git command specified"
526 project_home="$(pwd)"
527 say "Project Home: $project_home"
528 if test "$depth" -gt 0
529 then
530 say Command will recurse upto "$depth" depth
532 if test -d "$project_home"/.git/
533 then
534 say "Command to recurse: git $@"
535 traverse_module . "$@"
536 else
537 die "$project_home not a git repo thus exiting"
541 # Command synopsis clearly shows that all arguments after
542 # subcommand are arguments to the command itself. Thus
543 # there lies no command that has configuration argument
544 # after the mention of the subcommand. Thus once the
545 # subcommand is found and the separator ('--') is ignored
546 # rest can be safely sent the subcommand action
547 # It is to be noted that pre-subcommand arguments are parsed
548 # just to have backward compatibility.
549 while test $# != 0
551 case "$1" in
552 add)
553 add=1
554 command="module_$1"
555 shift
556 break
558 init|update|status)
559 command="modules_$1"
560 shift
561 check_for_terminator "$1"
562 break
564 -q|--quiet)
565 quiet=1
567 -b|--branch)
568 shift
569 parse_branch_name "$@"
571 -c|--cached)
572 cached=1
574 recurse)
575 command="modules_$1"
576 shift
577 break
580 # It is shifted so that it is not passed
581 # as an argument to the default subcommand
582 shift
583 break
586 usage
589 break
591 esac
592 shift
593 done
595 # Throws usage error if branch is not used with add command
596 if test -n "$branch" &&
597 test -z "$add"
598 then
599 echo Branch can not be specified without add subcommand
600 usage
603 # If no command is specified then default command
604 # is - git submodule status
605 test -z "$command" && command="modules_status"
607 # Throws usage if --cached is used by other than status, init or update
608 # that is used with add command
609 if test -n "$cached" &&
610 test "$command" != "modules_status"
611 then
612 echo Cached can only be used with the status subcommand
613 usage
616 "$command" "$@"