parse-options: add PARSE_OPT_LITERAL_ARGHELP for complicated argh's
[git] / git-submodule.sh
1 #!/bin/sh
2 #
3 # git-submodules.sh: add, init, update or list git submodules
4 #
5 # Copyright (c) 2007 Lars Hjemli
6
7 USAGE="[--quiet] [--cached] \
8 [add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit <n>] [<commit>]] \
9 [--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
10 OPTIONS_SPEC=
11 . git-sh-setup
12 . git-parse-remote
13 require_work_tree
14
15 command=
16 branch=
17 quiet=
18 cached=
19 nofetch=
20
21 #
22 # print stuff on stdout unless -q was specified
23 #
24 say()
25 {
26         if test -z "$quiet"
27         then
28                 echo "$@"
29         fi
30 }
31
32 # Resolve relative url by appending to parent's url
33 resolve_relative_url ()
34 {
35         remote=$(get_default_remote)
36         remoteurl=$(git config "remote.$remote.url") ||
37                 die "remote ($remote) does not have a url defined in .git/config"
38         url="$1"
39         remoteurl=${remoteurl%/}
40         while test -n "$url"
41         do
42                 case "$url" in
43                 ../*)
44                         url="${url#../}"
45                         remoteurl="${remoteurl%/*}"
46                         ;;
47                 ./*)
48                         url="${url#./}"
49                         ;;
50                 *)
51                         break;;
52                 esac
53         done
54         echo "$remoteurl/${url%/}"
55 }
56
57 #
58 # Get submodule info for registered submodules
59 # $@ = path to limit submodule list
60 #
61 module_list()
62 {
63         git ls-files --error-unmatch --stage -- "$@" | grep '^160000 '
64 }
65
66 #
67 # Map submodule path to submodule name
68 #
69 # $1 = path
70 #
71 module_name()
72 {
73         # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
74         re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
75         name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
76                 sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
77        test -z "$name" &&
78        die "No submodule mapping found in .gitmodules for path '$path'"
79        echo "$name"
80 }
81
82 #
83 # Clone a submodule
84 #
85 # Prior to calling, cmd_update checks that a possibly existing
86 # path is not a git repository.
87 # Likewise, cmd_add checks that path does not exist at all,
88 # since it is the location of a new submodule.
89 #
90 module_clone()
91 {
92         path=$1
93         url=$2
94
95         # If there already is a directory at the submodule path,
96         # expect it to be empty (since that is the default checkout
97         # action) and try to remove it.
98         # Note: if $path is a symlink to a directory the test will
99         # succeed but the rmdir will fail. We might want to fix this.
100         if test -d "$path"
101         then
102                 rmdir "$path" 2>/dev/null ||
103                 die "Directory '$path' exist, but is neither empty nor a git repository"
104         fi
105
106         test -e "$path" &&
107         die "A file already exist at path '$path'"
108
109         git-clone -n "$url" "$path" ||
110         die "Clone of '$url' into submodule path '$path' failed"
111 }
112
113 #
114 # Add a new submodule to the working tree, .gitmodules and the index
115 #
116 # $@ = repo path
117 #
118 # optional branch is stored in global branch variable
119 #
120 cmd_add()
121 {
122         # parse $args after "submodule ... add".
123         while test $# -ne 0
124         do
125                 case "$1" in
126                 -b | --branch)
127                         case "$2" in '') usage ;; esac
128                         branch=$2
129                         shift
130                         ;;
131                 -q|--quiet)
132                         quiet=1
133                         ;;
134                 --)
135                         shift
136                         break
137                         ;;
138                 -*)
139                         usage
140                         ;;
141                 *)
142                         break
143                         ;;
144                 esac
145                 shift
146         done
147
148         repo=$1
149         path=$2
150
151         if test -z "$repo" -o -z "$path"; then
152                 usage
153         fi
154
155         # assure repo is absolute or relative to parent
156         case "$repo" in
157         ./*|../*)
158                 # dereference source url relative to parent's url
159                 realrepo=$(resolve_relative_url "$repo") || exit
160                 ;;
161         *:*|/*)
162                 # absolute url
163                 realrepo=$repo
164                 ;;
165         *)
166                 die "repo URL: '$repo' must be absolute or begin with ./|../"
167         ;;
168         esac
169
170         # normalize path:
171         # multiple //; leading ./; /./; /../; trailing /
172         path=$(printf '%s/\n' "$path" |
173                 sed -e '
174                         s|//*|/|g
175                         s|^\(\./\)*||
176                         s|/\./|/|g
177                         :start
178                         s|\([^/]*\)/\.\./||
179                         tstart
180                         s|/*$||
181                 ')
182         git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
183         die "'$path' already exists in the index"
184
185         # perhaps the path exists and is already a git repo, else clone it
186         if test -e "$path"
187         then
188                 if test -d "$path"/.git -o -f "$path"/.git
189                 then
190                         echo "Adding existing repo at '$path' to the index"
191                 else
192                         die "'$path' already exists and is not a valid git repo"
193                 fi
194
195                 case "$repo" in
196                 ./*|../*)
197                         url=$(resolve_relative_url "$repo") || exit
198                     ;;
199                 *)
200                         url="$repo"
201                         ;;
202                 esac
203                 git config submodule."$path".url "$url"
204         else
205
206                 module_clone "$path" "$realrepo" || exit
207                 (
208                         unset GIT_DIR
209                         cd "$path" &&
210                         # ash fails to wordsplit ${branch:+-b "$branch"...}
211                         case "$branch" in
212                         '') git checkout -f -q ;;
213                         ?*) git checkout -f -q -b "$branch" "origin/$branch" ;;
214                         esac
215                 ) || die "Unable to checkout submodule '$path'"
216         fi
217
218         git add "$path" ||
219         die "Failed to add submodule '$path'"
220
221         git config -f .gitmodules submodule."$path".path "$path" &&
222         git config -f .gitmodules submodule."$path".url "$repo" &&
223         git add .gitmodules ||
224         die "Failed to register submodule '$path'"
225 }
226
227 #
228 # Execute an arbitrary command sequence in each checked out
229 # submodule
230 #
231 # $@ = command to execute
232 #
233 cmd_foreach()
234 {
235         module_list |
236         while read mode sha1 stage path
237         do
238                 if test -e "$path"/.git
239                 then
240                         say "Entering '$path'"
241                         (cd "$path" && eval "$@") ||
242                         die "Stopping at '$path'; script returned non-zero status."
243                 fi
244         done
245 }
246
247 #
248 # Register submodules in .git/config
249 #
250 # $@ = requested paths (default to all)
251 #
252 cmd_init()
253 {
254         # parse $args after "submodule ... init".
255         while test $# -ne 0
256         do
257                 case "$1" in
258                 -q|--quiet)
259                         quiet=1
260                         ;;
261                 --)
262                         shift
263                         break
264                         ;;
265                 -*)
266                         usage
267                         ;;
268                 *)
269                         break
270                         ;;
271                 esac
272                 shift
273         done
274
275         module_list "$@" |
276         while read mode sha1 stage path
277         do
278                 # Skip already registered paths
279                 name=$(module_name "$path") || exit
280                 url=$(git config submodule."$name".url)
281                 test -z "$url" || continue
282
283                 url=$(git config -f .gitmodules submodule."$name".url)
284                 test -z "$url" &&
285                 die "No url found for submodule path '$path' in .gitmodules"
286
287                 # Possibly a url relative to parent
288                 case "$url" in
289                 ./*|../*)
290                         url=$(resolve_relative_url "$url") || exit
291                         ;;
292                 esac
293
294                 git config submodule."$name".url "$url" ||
295                 die "Failed to register url for submodule path '$path'"
296
297                 say "Submodule '$name' ($url) registered for path '$path'"
298         done
299 }
300
301 #
302 # Update each submodule path to correct revision, using clone and checkout as needed
303 #
304 # $@ = requested paths (default to all)
305 #
306 cmd_update()
307 {
308         # parse $args after "submodule ... update".
309         while test $# -ne 0
310         do
311                 case "$1" in
312                 -q|--quiet)
313                         shift
314                         quiet=1
315                         ;;
316                 -i|--init)
317                         shift
318                         cmd_init "$@" || return
319                         ;;
320                 -N|--no-fetch)
321                         shift
322                         nofetch=1
323                         ;;
324                 --)
325                         shift
326                         break
327                         ;;
328                 -*)
329                         usage
330                         ;;
331                 *)
332                         break
333                         ;;
334                 esac
335         done
336
337         module_list "$@" |
338         while read mode sha1 stage path
339         do
340                 name=$(module_name "$path") || exit
341                 url=$(git config submodule."$name".url)
342                 if test -z "$url"
343                 then
344                         # Only mention uninitialized submodules when its
345                         # path have been specified
346                         test "$#" != "0" &&
347                         say "Submodule path '$path' not initialized" &&
348                         say "Maybe you want to use 'update --init'?"
349                         continue
350                 fi
351
352                 if ! test -d "$path"/.git -o -f "$path"/.git
353                 then
354                         module_clone "$path" "$url" || exit
355                         subsha1=
356                 else
357                         subsha1=$(unset GIT_DIR; cd "$path" &&
358                                 git rev-parse --verify HEAD) ||
359                         die "Unable to find current revision in submodule path '$path'"
360                 fi
361
362                 if test "$subsha1" != "$sha1"
363                 then
364                         force=
365                         if test -z "$subsha1"
366                         then
367                                 force="-f"
368                         fi
369
370                         if test -z "$nofetch"
371                         then
372                                 (unset GIT_DIR; cd "$path" &&
373                                         git-fetch) ||
374                                 die "Unable to fetch in submodule path '$path'"
375                         fi
376
377                         (unset GIT_DIR; cd "$path" &&
378                                   git-checkout $force -q "$sha1") ||
379                         die "Unable to checkout '$sha1' in submodule path '$path'"
380
381                         say "Submodule path '$path': checked out '$sha1'"
382                 fi
383         done
384 }
385
386 set_name_rev () {
387         revname=$( (
388                 unset GIT_DIR
389                 cd "$1" && {
390                         git describe "$2" 2>/dev/null ||
391                         git describe --tags "$2" 2>/dev/null ||
392                         git describe --contains "$2" 2>/dev/null ||
393                         git describe --all --always "$2"
394                 }
395         ) )
396         test -z "$revname" || revname=" ($revname)"
397 }
398 #
399 # Show commit summary for submodules in index or working tree
400 #
401 # If '--cached' is given, show summary between index and given commit,
402 # or between working tree and given commit
403 #
404 # $@ = [commit (default 'HEAD'),] requested paths (default all)
405 #
406 cmd_summary() {
407         summary_limit=-1
408         for_status=
409
410         # parse $args after "submodule ... summary".
411         while test $# -ne 0
412         do
413                 case "$1" in
414                 --cached)
415                         cached="$1"
416                         ;;
417                 --for-status)
418                         for_status="$1"
419                         ;;
420                 -n|--summary-limit)
421                         if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
422                         then
423                                 :
424                         else
425                                 usage
426                         fi
427                         shift
428                         ;;
429                 --)
430                         shift
431                         break
432                         ;;
433                 -*)
434                         usage
435                         ;;
436                 *)
437                         break
438                         ;;
439                 esac
440                 shift
441         done
442
443         test $summary_limit = 0 && return
444
445         if rev=$(git rev-parse -q --verify "$1^0")
446         then
447                 head=$rev
448                 shift
449         else
450                 head=HEAD
451         fi
452
453         cd_to_toplevel
454         # Get modified modules cared by user
455         modules=$(git diff-index $cached --raw $head -- "$@" |
456                 egrep '^:([0-7]* )?160000' |
457                 while read mod_src mod_dst sha1_src sha1_dst status name
458                 do
459                         # Always show modules deleted or type-changed (blob<->module)
460                         test $status = D -o $status = T && echo "$name" && continue
461                         # Also show added or modified modules which are checked out
462                         GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
463                         echo "$name"
464                 done
465         )
466
467         test -z "$modules" && return
468
469         git diff-index $cached --raw $head -- $modules |
470         egrep '^:([0-7]* )?160000' |
471         cut -c2- |
472         while read mod_src mod_dst sha1_src sha1_dst status name
473         do
474                 if test -z "$cached" &&
475                         test $sha1_dst = 0000000000000000000000000000000000000000
476                 then
477                         case "$mod_dst" in
478                         160000)
479                                 sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
480                                 ;;
481                         100644 | 100755 | 120000)
482                                 sha1_dst=$(git hash-object $name)
483                                 ;;
484                         000000)
485                                 ;; # removed
486                         *)
487                                 # unexpected type
488                                 echo >&2 "unexpected mode $mod_dst"
489                                 continue ;;
490                         esac
491                 fi
492                 missing_src=
493                 missing_dst=
494
495                 test $mod_src = 160000 &&
496                 ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
497                 missing_src=t
498
499                 test $mod_dst = 160000 &&
500                 ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
501                 missing_dst=t
502
503                 total_commits=
504                 case "$missing_src,$missing_dst" in
505                 t,)
506                         errmsg="  Warn: $name doesn't contain commit $sha1_src"
507                         ;;
508                 ,t)
509                         errmsg="  Warn: $name doesn't contain commit $sha1_dst"
510                         ;;
511                 t,t)
512                         errmsg="  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
513                         ;;
514                 *)
515                         errmsg=
516                         total_commits=$(
517                         if test $mod_src = 160000 -a $mod_dst = 160000
518                         then
519                                 range="$sha1_src...$sha1_dst"
520                         elif test $mod_src = 160000
521                         then
522                                 range=$sha1_src
523                         else
524                                 range=$sha1_dst
525                         fi
526                         GIT_DIR="$name/.git" \
527                         git log --pretty=oneline --first-parent $range | wc -l
528                         )
529                         total_commits=" ($(($total_commits + 0)))"
530                         ;;
531                 esac
532
533                 sha1_abbr_src=$(echo $sha1_src | cut -c1-7)
534                 sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
535                 if test $status = T
536                 then
537                         if test $mod_dst = 160000
538                         then
539                                 echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:"
540                         else
541                                 echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:"
542                         fi
543                 else
544                         echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
545                 fi
546                 if test -n "$errmsg"
547                 then
548                         # Don't give error msg for modification whose dst is not submodule
549                         # i.e. deleted or changed to blob
550                         test $mod_dst = 160000 && echo "$errmsg"
551                 else
552                         if test $mod_src = 160000 -a $mod_dst = 160000
553                         then
554                                 limit=
555                                 test $summary_limit -gt 0 && limit="-$summary_limit"
556                                 GIT_DIR="$name/.git" \
557                                 git log $limit --pretty='format:  %m %s' \
558                                 --first-parent $sha1_src...$sha1_dst
559                         elif test $mod_dst = 160000
560                         then
561                                 GIT_DIR="$name/.git" \
562                                 git log --pretty='format:  > %s' -1 $sha1_dst
563                         else
564                                 GIT_DIR="$name/.git" \
565                                 git log --pretty='format:  < %s' -1 $sha1_src
566                         fi
567                         echo
568                 fi
569                 echo
570         done |
571         if test -n "$for_status"; then
572                 echo "# Modified submodules:"
573                 echo "#"
574                 sed -e 's|^|# |' -e 's|^# $|#|'
575         else
576                 cat
577         fi
578 }
579 #
580 # List all submodules, prefixed with:
581 #  - submodule not initialized
582 #  + different revision checked out
583 #
584 # If --cached was specified the revision in the index will be printed
585 # instead of the currently checked out revision.
586 #
587 # $@ = requested paths (default to all)
588 #
589 cmd_status()
590 {
591         # parse $args after "submodule ... status".
592         while test $# -ne 0
593         do
594                 case "$1" in
595                 -q|--quiet)
596                         quiet=1
597                         ;;
598                 --cached)
599                         cached=1
600                         ;;
601                 --)
602                         shift
603                         break
604                         ;;
605                 -*)
606                         usage
607                         ;;
608                 *)
609                         break
610                         ;;
611                 esac
612                 shift
613         done
614
615         module_list "$@" |
616         while read mode sha1 stage path
617         do
618                 name=$(module_name "$path") || exit
619                 url=$(git config submodule."$name".url)
620                 if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
621                 then
622                         say "-$sha1 $path"
623                         continue;
624                 fi
625                 set_name_rev "$path" "$sha1"
626                 if git diff-files --quiet -- "$path"
627                 then
628                         say " $sha1 $path$revname"
629                 else
630                         if test -z "$cached"
631                         then
632                                 sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
633                                 set_name_rev "$path" "$sha1"
634                         fi
635                         say "+$sha1 $path$revname"
636                 fi
637         done
638 }
639 #
640 # Sync remote urls for submodules
641 # This makes the value for remote.$remote.url match the value
642 # specified in .gitmodules.
643 #
644 cmd_sync()
645 {
646         while test $# -ne 0
647         do
648                 case "$1" in
649                 -q|--quiet)
650                         quiet=1
651                         shift
652                         ;;
653                 --)
654                         shift
655                         break
656                         ;;
657                 -*)
658                         usage
659                         ;;
660                 *)
661                         break
662                         ;;
663                 esac
664         done
665         cd_to_toplevel
666         module_list "$@" |
667         while read mode sha1 stage path
668         do
669                 name=$(module_name "$path")
670                 url=$(git config -f .gitmodules --get submodule."$name".url)
671
672                 # Possibly a url relative to parent
673                 case "$url" in
674                 ./*|../*)
675                         url=$(resolve_relative_url "$url") || exit
676                         ;;
677                 esac
678
679                 if test -e "$path"/.git
680                 then
681                 (
682                         unset GIT_DIR
683                         cd "$path"
684                         remote=$(get_default_remote)
685                         say "Synchronizing submodule url for '$name'"
686                         git config remote."$remote".url "$url"
687                 )
688                 fi
689         done
690 }
691
692 # This loop parses the command line arguments to find the
693 # subcommand name to dispatch.  Parsing of the subcommand specific
694 # options are primarily done by the subcommand implementations.
695 # Subcommand specific options such as --branch and --cached are
696 # parsed here as well, for backward compatibility.
697
698 while test $# != 0 && test -z "$command"
699 do
700         case "$1" in
701         add | foreach | init | update | status | summary | sync)
702                 command=$1
703                 ;;
704         -q|--quiet)
705                 quiet=1
706                 ;;
707         -b|--branch)
708                 case "$2" in
709                 '')
710                         usage
711                         ;;
712                 esac
713                 branch="$2"; shift
714                 ;;
715         --cached)
716                 cached="$1"
717                 ;;
718         --)
719                 break
720                 ;;
721         -*)
722                 usage
723                 ;;
724         *)
725                 break
726                 ;;
727         esac
728         shift
729 done
730
731 # No command word defaults to "status"
732 test -n "$command" || command=status
733
734 # "-b branch" is accepted only by "add"
735 if test -n "$branch" && test "$command" != add
736 then
737         usage
738 fi
739
740 # "--cached" is accepted only by "status" and "summary"
741 if test -n "$cached" && test "$command" != status -a "$command" != summary
742 then
743         usage
744 fi
745
746 "cmd_$command" "$@"