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