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