Merge branch 'cc/bisect' (early part)
[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 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                 --)
360                         shift
361                         break
362                         ;;
363                 -*)
364                         usage
365                         ;;
366                 *)
367                         break
368                         ;;
369                 esac
370         done
371
372         if test -n "$init"
373         then
374                 cmd_init "--" "$@" || return
375         fi
376
377         module_list "$@" |
378         while read mode sha1 stage path
379         do
380                 name=$(module_name "$path") || exit
381                 url=$(git config submodule."$name".url)
382                 update_module=$(git config submodule."$name".update)
383                 if test -z "$url"
384                 then
385                         # Only mention uninitialized submodules when its
386                         # path have been specified
387                         test "$#" != "0" &&
388                         say "Submodule path '$path' not initialized" &&
389                         say "Maybe you want to use 'update --init'?"
390                         continue
391                 fi
392
393                 if ! test -d "$path"/.git -o -f "$path"/.git
394                 then
395                         module_clone "$path" "$url" "$reference"|| exit
396                         subsha1=
397                 else
398                         subsha1=$(unset GIT_DIR; cd "$path" &&
399                                 git rev-parse --verify HEAD) ||
400                         die "Unable to find current revision in submodule path '$path'"
401                 fi
402
403                 if ! test -z "$update"
404                 then
405                         update_module=$update
406                 fi
407
408                 if test "$subsha1" != "$sha1"
409                 then
410                         force=
411                         if test -z "$subsha1"
412                         then
413                                 force="-f"
414                         fi
415
416                         if test -z "$nofetch"
417                         then
418                                 (unset GIT_DIR; cd "$path" &&
419                                         git-fetch) ||
420                                 die "Unable to fetch in submodule path '$path'"
421                         fi
422
423                         case "$update_module" in
424                         rebase)
425                                 command="git rebase"
426                                 action="rebase"
427                                 msg="rebased onto"
428                                 ;;
429                         *)
430                                 command="git checkout $force -q"
431                                 action="checkout"
432                                 msg="checked out"
433                                 ;;
434                         esac
435
436                         (unset GIT_DIR; cd "$path" && $command "$sha1") ||
437                         die "Unable to $action '$sha1' in submodule path '$path'"
438                         say "Submodule path '$path': $msg '$sha1'"
439                 fi
440         done
441 }
442
443 set_name_rev () {
444         revname=$( (
445                 unset GIT_DIR
446                 cd "$1" && {
447                         git describe "$2" 2>/dev/null ||
448                         git describe --tags "$2" 2>/dev/null ||
449                         git describe --contains "$2" 2>/dev/null ||
450                         git describe --all --always "$2"
451                 }
452         ) )
453         test -z "$revname" || revname=" ($revname)"
454 }
455 #
456 # Show commit summary for submodules in index or working tree
457 #
458 # If '--cached' is given, show summary between index and given commit,
459 # or between working tree and given commit
460 #
461 # $@ = [commit (default 'HEAD'),] requested paths (default all)
462 #
463 cmd_summary() {
464         summary_limit=-1
465         for_status=
466
467         # parse $args after "submodule ... summary".
468         while test $# -ne 0
469         do
470                 case "$1" in
471                 --cached)
472                         cached="$1"
473                         ;;
474                 --for-status)
475                         for_status="$1"
476                         ;;
477                 -n|--summary-limit)
478                         if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
479                         then
480                                 :
481                         else
482                                 usage
483                         fi
484                         shift
485                         ;;
486                 --)
487                         shift
488                         break
489                         ;;
490                 -*)
491                         usage
492                         ;;
493                 *)
494                         break
495                         ;;
496                 esac
497                 shift
498         done
499
500         test $summary_limit = 0 && return
501
502         if rev=$(git rev-parse -q --verify "$1^0")
503         then
504                 head=$rev
505                 shift
506         else
507                 head=HEAD
508         fi
509
510         cd_to_toplevel
511         # Get modified modules cared by user
512         modules=$(git diff-index $cached --raw $head -- "$@" |
513                 egrep '^:([0-7]* )?160000' |
514                 while read mod_src mod_dst sha1_src sha1_dst status name
515                 do
516                         # Always show modules deleted or type-changed (blob<->module)
517                         test $status = D -o $status = T && echo "$name" && continue
518                         # Also show added or modified modules which are checked out
519                         GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
520                         echo "$name"
521                 done
522         )
523
524         test -z "$modules" && return
525
526         git diff-index $cached --raw $head -- $modules |
527         egrep '^:([0-7]* )?160000' |
528         cut -c2- |
529         while read mod_src mod_dst sha1_src sha1_dst status name
530         do
531                 if test -z "$cached" &&
532                         test $sha1_dst = 0000000000000000000000000000000000000000
533                 then
534                         case "$mod_dst" in
535                         160000)
536                                 sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
537                                 ;;
538                         100644 | 100755 | 120000)
539                                 sha1_dst=$(git hash-object $name)
540                                 ;;
541                         000000)
542                                 ;; # removed
543                         *)
544                                 # unexpected type
545                                 echo >&2 "unexpected mode $mod_dst"
546                                 continue ;;
547                         esac
548                 fi
549                 missing_src=
550                 missing_dst=
551
552                 test $mod_src = 160000 &&
553                 ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
554                 missing_src=t
555
556                 test $mod_dst = 160000 &&
557                 ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
558                 missing_dst=t
559
560                 total_commits=
561                 case "$missing_src,$missing_dst" in
562                 t,)
563                         errmsg="  Warn: $name doesn't contain commit $sha1_src"
564                         ;;
565                 ,t)
566                         errmsg="  Warn: $name doesn't contain commit $sha1_dst"
567                         ;;
568                 t,t)
569                         errmsg="  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
570                         ;;
571                 *)
572                         errmsg=
573                         total_commits=$(
574                         if test $mod_src = 160000 -a $mod_dst = 160000
575                         then
576                                 range="$sha1_src...$sha1_dst"
577                         elif test $mod_src = 160000
578                         then
579                                 range=$sha1_src
580                         else
581                                 range=$sha1_dst
582                         fi
583                         GIT_DIR="$name/.git" \
584                         git log --pretty=oneline --first-parent $range | wc -l
585                         )
586                         total_commits=" ($(($total_commits + 0)))"
587                         ;;
588                 esac
589
590                 sha1_abbr_src=$(echo $sha1_src | cut -c1-7)
591                 sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
592                 if test $status = T
593                 then
594                         if test $mod_dst = 160000
595                         then
596                                 echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:"
597                         else
598                                 echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:"
599                         fi
600                 else
601                         echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
602                 fi
603                 if test -n "$errmsg"
604                 then
605                         # Don't give error msg for modification whose dst is not submodule
606                         # i.e. deleted or changed to blob
607                         test $mod_dst = 160000 && echo "$errmsg"
608                 else
609                         if test $mod_src = 160000 -a $mod_dst = 160000
610                         then
611                                 limit=
612                                 test $summary_limit -gt 0 && limit="-$summary_limit"
613                                 GIT_DIR="$name/.git" \
614                                 git log $limit --pretty='format:  %m %s' \
615                                 --first-parent $sha1_src...$sha1_dst
616                         elif test $mod_dst = 160000
617                         then
618                                 GIT_DIR="$name/.git" \
619                                 git log --pretty='format:  > %s' -1 $sha1_dst
620                         else
621                                 GIT_DIR="$name/.git" \
622                                 git log --pretty='format:  < %s' -1 $sha1_src
623                         fi
624                         echo
625                 fi
626                 echo
627         done |
628         if test -n "$for_status"; then
629                 echo "# Modified submodules:"
630                 echo "#"
631                 sed -e 's|^|# |' -e 's|^# $|#|'
632         else
633                 cat
634         fi
635 }
636 #
637 # List all submodules, prefixed with:
638 #  - submodule not initialized
639 #  + different revision checked out
640 #
641 # If --cached was specified the revision in the index will be printed
642 # instead of the currently checked out revision.
643 #
644 # $@ = requested paths (default to all)
645 #
646 cmd_status()
647 {
648         # parse $args after "submodule ... status".
649         while test $# -ne 0
650         do
651                 case "$1" in
652                 -q|--quiet)
653                         quiet=1
654                         ;;
655                 --cached)
656                         cached=1
657                         ;;
658                 --)
659                         shift
660                         break
661                         ;;
662                 -*)
663                         usage
664                         ;;
665                 *)
666                         break
667                         ;;
668                 esac
669                 shift
670         done
671
672         module_list "$@" |
673         while read mode sha1 stage path
674         do
675                 name=$(module_name "$path") || exit
676                 url=$(git config submodule."$name".url)
677                 if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
678                 then
679                         say "-$sha1 $path"
680                         continue;
681                 fi
682                 set_name_rev "$path" "$sha1"
683                 if git diff-files --quiet -- "$path"
684                 then
685                         say " $sha1 $path$revname"
686                 else
687                         if test -z "$cached"
688                         then
689                                 sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
690                                 set_name_rev "$path" "$sha1"
691                         fi
692                         say "+$sha1 $path$revname"
693                 fi
694         done
695 }
696 #
697 # Sync remote urls for submodules
698 # This makes the value for remote.$remote.url match the value
699 # specified in .gitmodules.
700 #
701 cmd_sync()
702 {
703         while test $# -ne 0
704         do
705                 case "$1" in
706                 -q|--quiet)
707                         quiet=1
708                         shift
709                         ;;
710                 --)
711                         shift
712                         break
713                         ;;
714                 -*)
715                         usage
716                         ;;
717                 *)
718                         break
719                         ;;
720                 esac
721         done
722         cd_to_toplevel
723         module_list "$@" |
724         while read mode sha1 stage path
725         do
726                 name=$(module_name "$path")
727                 url=$(git config -f .gitmodules --get submodule."$name".url)
728
729                 # Possibly a url relative to parent
730                 case "$url" in
731                 ./*|../*)
732                         url=$(resolve_relative_url "$url") || exit
733                         ;;
734                 esac
735
736                 if test -e "$path"/.git
737                 then
738                 (
739                         unset GIT_DIR
740                         cd "$path"
741                         remote=$(get_default_remote)
742                         say "Synchronizing submodule url for '$name'"
743                         git config remote."$remote".url "$url"
744                 )
745                 fi
746         done
747 }
748
749 # This loop parses the command line arguments to find the
750 # subcommand name to dispatch.  Parsing of the subcommand specific
751 # options are primarily done by the subcommand implementations.
752 # Subcommand specific options such as --branch and --cached are
753 # parsed here as well, for backward compatibility.
754
755 while test $# != 0 && test -z "$command"
756 do
757         case "$1" in
758         add | foreach | init | update | status | summary | sync)
759                 command=$1
760                 ;;
761         -q|--quiet)
762                 quiet=1
763                 ;;
764         -b|--branch)
765                 case "$2" in
766                 '')
767                         usage
768                         ;;
769                 esac
770                 branch="$2"; shift
771                 ;;
772         --cached)
773                 cached="$1"
774                 ;;
775         --)
776                 break
777                 ;;
778         -*)
779                 usage
780                 ;;
781         *)
782                 break
783                 ;;
784         esac
785         shift
786 done
787
788 # No command word defaults to "status"
789 test -n "$command" || command=status
790
791 # "-b branch" is accepted only by "add"
792 if test -n "$branch" && test "$command" != add
793 then
794         usage
795 fi
796
797 # "--cached" is accepted only by "status" and "summary"
798 if test -n "$cached" && test "$command" != status -a "$command" != summary
799 then
800         usage
801 fi
802
803 "cmd_$command" "$@"