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