]> git.xonotic.org Git - xonotic/xonotic.git/blob - misc/tools/all/git.subr
b14a8b6f1acd8e0f67443f7a211fcdf3c80fa3af
[xonotic/xonotic.git] / misc / tools / all / git.subr
1 initrepo_()
2 {
3         if [ x"$3" != x"." ]; then
4                 return
5         fi
6         case "$1" in
7                 *$4)
8                         base=${1%$4}
9                         ;;
10         esac
11         case "$2" in
12                 *$4)
13                         pushbase=${2%$4}
14                         ;;
15         esac
16 }
17 initrepo()
18 {
19         base=
20         pushbase=
21         allrepos initrepo_ "`git config remote.origin.url`" "`git config remote.origin.pushurl`"
22         if [ -z "$base" ]; then
23                 msg "The main repo is not xonotic.git, what have you done?"
24                 exit 1
25         fi
26         msg "Found main repo = $base"
27         if [ -n "$pushbase" ]; then
28                 msg "Found push repo = $pushbase"
29         fi
30 }
31 matchrepoflag()
32 {
33         case ",$2," in
34                 *",$1,"*)
35                         return 0
36                         ;;
37                 *)
38                         return 1
39                         ;;
40         esac
41 }
42 testrepoflag_()
43 {
44         [ x"$1" = x"$3" ] || return
45         if matchrepoflag "$6" "$2"; then
46                 echo 0
47         fi
48 }
49 testrepoflag()
50 {
51         allrepos testrepoflag_ "$1" "$2" | grep ^0 >/dev/null
52 }
53
54 mirrorspeed()
55 {
56         # first result is to be ignored, but we use it to check status
57         git ls-remote "$1" refs/heads/master >/dev/null 2>&1 || return 1
58         # if we can't time, we only check availability
59         if ! $have_time; then
60                 echo 0
61                 return
62         fi
63         # now actually time it
64         (
65                 set +x
66                 export REPO=$1 # so that the sh -c subshell can use it
67                 { measure_time sh -c 'git ls-remote "$REPO" refs/heads/master >/dev/null 2>&1'; } 2>&1 >/dev/null | head -n 1 | cut -d ' ' -f 2 | tr -d . | sed 's,^0*,,' | grep . || echo 0
68                         # unit: clock ticks (depends on what "time" returns
69         )
70 }
71 bestmirror()
72 {
73         oldurl="$1"
74         newprotocol="$2"
75         newlocation="$3"
76         oldprotocol=
77         oldlocation=
78         testrepo=
79         bestmirror_firstrepo()
80         {
81                 if [ -z "$testrepo" ]; then
82                         testrepo=$2
83                 fi
84         }
85         allrepos bestmirror_firstrepo
86         bestmirror_findold()
87         {
88                 if [ x"$oldurl" = x"$3" ]; then
89                         oldprotocol=$1
90                         oldlocation=$2
91                 fi
92         }
93         allmirrors bestmirror_findold
94
95         if [ -z "$newprotocol" ]; then
96                 newprotocol=$oldprotocol
97         fi
98         if [ -z "$newlocation" ]; then
99                 newlocation=$oldlocation
100         fi
101
102         besturl=
103         bestlocation=
104         besttime=
105         bestcount=
106         bestmirror_benchmark()
107         {
108                 bmb_curloc=$1
109                 bmb_proto=$2
110                 bmb_loc=$3
111                 bmb_url=$4
112                 bmb_fudge=$5
113
114                 if [ -z "$bmb_loc" ]; then
115                         # empty location is not allowed
116                         return
117                 fi
118                 case " $newprotocol " in
119                         *"  "*)
120                                 # no protocol requested? all match
121                                 ;;
122                         *" $bmb_proto "*)
123                                 ;;
124                         *)
125                                 return
126                                 ;;
127                 esac
128
129                 # prefer location match
130                 case " $newlocation " in
131                         *" $bmb_loc "*)
132                                 # bmb_curloc is true in first run, false in second
133                                 # so first run gets all matching locations
134                                 # so second run gets all non-matching locations
135                                 if ! $bmb_curloc; then
136                                         return
137                                 fi
138                                 ;;
139                         *)
140                                 if $bmb_curloc; then
141                                         return
142                                 fi
143                                 case " $newlocation " in
144                                         *" $bestlocation "*)
145                                                 # worse
146                                                 return
147                                                 ;;
148                                 esac
149                                 ;;
150                 esac
151
152                 case " $newlocation " in
153                         *" $bmb_loc "*)
154                                 # see below
155                                 ;;
156                         *)
157                                 case " $newlocation " in
158                                         *" $bestlocation "*)
159                                                 # worse
160                                                 return
161                                                 ;;
162                                 esac
163                                 ;;
164                 esac
165                 msg "Testing speed of $bmb_url..."
166
167                 # only working mirrors
168                 if ! thistime=`mirrorspeed "$bmb_url$testrepo"`; then
169                         msg "-> FAILED"
170                         return
171                 fi
172                 thistime=$(($thistime $bmb_fudge))
173                 msg "-> $thistime"
174
175                 # anything is better than nothing
176                 if [ -z "$besttime" ]; then
177                         besturl=$bmb_url
178                         bestlocation=$bmb_loc
179                         besttime=$thistime
180                         bestcount=1
181                         return
182                 fi
183
184                 # prefer location match
185                 case " $newlocation " in
186                         *" $bmb_loc "*)
187                                 case " $newlocation " in
188                                         *" $bestlocation "*)
189                                                 # equality
190                                                 ;;
191                                         *)
192                                                 # better
193                                                 besturl=$bmb_url
194                                                 bestlocation=$bmb_loc
195                                                 besttime=$thistime
196                                                 bestcount=1
197                                                 return
198                                                 ;;
199                                 esac
200                                 ;;
201                         *)
202                                 # if newlocation matches bestlocation, then we already discarded it above
203                                 ;;
204                 esac
205
206                 # if we get here, we must compare mirror speed as we have more than one match
207                 if [ $thistime -gt $besttime ]; then
208                         return
209                 elif [ $thistime -lt $besttime ]; then
210                         besturl=$bmb_url
211                         bestlocation=$bmb_loc
212                         besttime=$thistime
213                         bestcount=1
214                         return
215                 fi
216                 # both location and time match. Random decision.
217                 bestcount=$(($bestcount + 1))
218                 if [ $((($RANDOM + 0) % $bestcount)) -eq 0 ]; then
219                         besturl=$bmb_url
220                         bestlocation=$bmb_loc
221                 fi
222         }
223         allmirrors bestmirror_benchmark true
224         allmirrors bestmirror_benchmark false
225         echo "$besturl"
226 }
227
228 testrepoflag_()
229 {
230         [ x"$1" = x"$3" ] || return
231         case ",$6," in
232                 *",$2,"*)
233                         echo 0
234                         ;;
235                 *)
236                         ;;
237         esac
238 }
239 testrepoflag()
240 {
241         allrepos testrepoflag_ "$1" "$2" | grep ^0 >/dev/null
242 }
243 listrepos_()
244 {
245         d=$1
246         f=$4
247         p="${d%dir}"
248         # if we have .no file, skip
249         if [ -f "$d.no" ]; then
250                 msg "Repository $d disabled by a .no file, delete $d.no to enable"
251                 return
252         fi
253         # if .yes file exists, always keep it
254         if [ -f "$d.yes" ]; then
255                 msg "Repository $d enabled by a .yes file"
256                 $ECHO "$d"
257                 return
258         fi
259         # remove broken clones so they don't mess up stuff
260         if [ x"$d" != x"." ] && [ -d "$d" ] && ! [ -d "$d/.git" ]; then
261                 msg "$d exists but has no .git subdir. Probably a broken clone. Deleting."
262                 verbose rm -rf "$d"
263                 return
264         fi
265         # if we have the dir, always keep it
266         if [ -d "$d" ]; then
267                 msg "Repository $d enabled because it already exists"
268                 $ECHO "$d"
269                 return
270         fi
271         # if we have matching pk3, skip
272         if [ x"$p" != x"$d" ] && [ -f "$p" ]; then
273                 msg "Repository $d disabled by matching .pk3 file, delete $p or create $d.yes to enable"
274                 return
275         fi
276         # if "no" flag is set, skip
277         if matchrepoflag "$f" no; then
278                 msg "Repository $d disabled by default, create $d.yes to enable"
279                 return
280         fi
281         # default: enable
282         msg "Repository $d enabled by default"
283         $ECHO "$d"
284 }
285
286 listrepos()
287 {
288         $ECHO `allrepos listrepos_`
289 }
290 initrepo
291 repos=`listrepos`
292
293 ifrepoenabled()
294 {
295         eval ire_test=\$$(($1 + 3))
296         shift
297         case " $repos " in
298                 *" $ire_test "*)
299                         "$@"
300                         ;;
301         esac
302 }
303 check_mergeconflict() # overrides the one in ./all
304 {
305         if git ls-files -u | grep ' 1   '; then
306                 $ECHO
307                 $ECHO "MERGE CONFLICT."
308                 $ECHO "change into the \"$1\" project directory, and then:"
309                 $ECHO "- edit the files mentioned above with your favorite editor,"
310                 $ECHO "  and fix the conflicts (marked with <<<<<<< blocks)"
311                 $ECHO "- for binary files, you can select the files using"
312                 $ECHO "  git checkout --ours or git checkout --theirs"
313                 $ECHO "- when done with a file, 'git add' the file"
314                 $ECHO "- when done, 'git commit'"
315                 $ECHO
316                 exit 1
317         fi
318 }
319
320 visible_repo_name()
321 {
322         case "$1" in
323                 .)
324                         $ECHO "the root directory"
325                         ;;
326                 *)
327                         $ECHO "\"$1\""
328                         ;;
329         esac
330 }
331
332 fix_upstream_rebase()
333 {
334         if [ -z "$r_me" ] || [ -z "$r_other" ]; then
335                 return
336         fi
337
338         # one of the two sides of the merge should be remote upstream, or all is fine
339         r_r=`git symbolic-ref HEAD`
340         r_r=${r_r#refs/heads/}
341         r_rem=`git config "branch.$r_rem.remote" || $ECHO origin`
342         r_bra=`git config "branch.$r_bra.merge" || $ECHO "$r_r"`
343         r_bra=${r_bra#refs/heads/}
344         if [ x"$r_me" != x"`git rev-parse "$r_rem/$r_bra"`" ]; then
345                 if [ x"$r_other" != x"`git rev-parse "$r_rem/$r_bra"`" ]; then
346                         return
347                 fi
348         fi
349
350         r_base=`git merge-base "$r_me" "$r_other"`
351
352         # no merge-base? upstream did filter-branch
353         if [ -n "$r_base" ]; then
354                 # otherwise, check if the two histories are "similar"
355                 r_l_me=`git log --pretty="format:%s" "$r_other".."$r_me" | grep -v "^Merge" | sort -u`
356                 r_l_other=`git log --pretty="format:%s" "$r_me".."$r_other" | grep -v "^Merge" | sort -u`
357
358                 # heuristics: upstream rebase/filter-branch if more than 50% of the commits of one of the sides are in the other too
359                 r_lc_me=`$ECHO "$r_l_me" | wc -l`
360                 r_lc_other=`$ECHO "$r_l_other" | wc -l`
361                 r_lc_together=`{ $ECHO "$r_l_me"; $ECHO "$r_l_other"; } | sort -u | wc -l`
362                 r_lc_same=$(($r_lc_me + $r_lc_other - $r_lc_together))
363
364                 if [ $(( $r_lc_same * 2 )) -gt $(( $r_lc_me )) ] || [ $(( $r_lc_same * 2 )) -gt $(( $r_lc_other )) ]; then
365                         if yesno "Probable upstream rebase detected, automatically fix?" 'git log --oneline --graph --date-order --left-right "$r_other"..."$r_me"'; then
366                                 git reset --hard "$r_me"
367                                 git pull --rebase
368                                 return 1
369                         fi
370                 fi
371         fi
372
373         return 0
374 }
375
376 fix_upstream_rebase_mergeok()
377 {
378         r_me=`git rev-parse --revs-only HEAD^1 2>/dev/null || true`
379         r_other=`git rev-parse --revs-only HEAD^2 2>/dev/null || true`
380         fix_upstream_rebase
381 }
382
383 fix_upstream_rebase_mergefail()
384 {
385         r_me=`git rev-parse --revs-only HEAD 2>/dev/null || true`
386         r_other=`git rev-parse --revs-only MERGE_HEAD 2>/dev/null || true`
387         fix_upstream_rebase
388 }
389
390 fix_git_config()
391 {
392         if ! [ -f ".git/config" ]; then
393                 $ECHO "Not a git repository. Bailing out to not cause damage."
394                 exit 1
395         fi
396         verbose git config remote.origin.url "$1"
397         if [ -n "$2" ]; then
398                 verbose git config remote.origin.pushurl "$2"
399         else
400                 verbose git config --unset remote.origin.pushurl || true
401         fi
402         verbose git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
403         if testrepoflag "$d" noautocrlf; then
404                 verbose git config --unset core.autocrlf || true
405         else
406                 verbose git config core.autocrlf input
407         fi
408         if [ -z "`git config push.default || true`" ]; then
409                 verbose git config push.default current # or is tracking better?
410         fi
411         verbose git config filter.mapclean.clean "tr -d '\r' | grep '^[^/]'"
412         verbose git config filter.mapclean.smudge "cat"
413 }
414
415 setrepovars()
416 {
417         while [ $# -gt 4 ]; do
418                 shift
419         done
420         d=$1
421         url="$base$2"
422         if [ -n "$pushbase" ]; then
423                 pushurl="$pushbase$2"
424         else
425                 pushurl=
426         fi
427         branch=$3
428         f=$4
429 }
430
431 handled=true
432 case "$cmd" in
433         fix_upstream_rebase)
434                 fix_upstream_rebase_()
435                 {
436                         setrepovars "$@"
437                         enter "$d0/$d" verbose
438                         verbose fix_upstream_rebase_mergefail && verbose fix_upstream_rebase_mergeok
439                 }
440                 allrepos ifrepoenabled 0 fix_upstream_rebase_
441                 ;;
442         fix_config)
443                 fix_config_()
444                 {
445                         setrepovars "$@"
446                         if [ -f "$d0/$d/.git/config" ]; then
447                                 verbose cd "$d0/$d"
448                                 fix_git_config "$url" "$pushurl"
449                                 cd "$d0"
450                         fi
451                 }
452                 allrepos ifrepoenabled 0 fix_config_
453                 ;;
454         keygen)
455                 if [ -f ~/.ssh/id_rsa.pub ]; then
456                         msg ""
457                         msg "A key already exists and no new one will be generated. If you"
458                         msg "already have done the procedure for getting your key approved, you"
459                         msg "can skip the following paragraph and already use the repository."
460                         msg ""
461                         msg "To get access, your key has to be approved first. For that, visit"
462                         msg "$gitsite_url, then log in, enter the"
463                         msg "\"xonotic\" project, create an \"Issue\" tagged \"Repository Access\""
464                         msg "to apply for access and paste the following output into the issue:"
465                         msg ""
466                         msg "After that, go to your profile settings, \"SSH Keys\", \"Add SSH Key\""
467                         msg "and paste the following output:"
468                         msg ""
469                         msg "`cat ~/.ssh/id_rsa.pub`"
470                 elif [ -f ~/.ssh/id_dsa.pub ]; then
471                         msg ""
472                         msg "A key already exists and no new one will be generated. If you"
473                         msg "already have done the procedure for getting your key approved, you"
474                         msg "can skip the following paragraph and already use the repository."
475                         msg ""
476                         msg "To get access, your key has to be approved first. For that, visit"
477                         msg "$gitsite_url, then log in, enter the"
478                         msg "\"xonotic\" project, create an \"Issue\" tagged \"Repository Access\""
479                         msg "to apply for access and paste the following output into the issue:"
480                         msg ""
481                         msg "After that, go to your profile settings, \"SSH Keys\", \"Add SSH Key\""
482                         msg "and paste the following output:"
483                         msg ""
484                         msg "`cat ~/.ssh/id_dsa.pub`"
485                 else
486                         msg ""
487                         msg "No key has been generated yet. One will be generated now."
488                         msg "If other people are using your computer, it is recommended"
489                         msg "to specify a passphrase. Otherwise you can simply hit ENTER"
490                         msg "when asked for a passphrase."
491                         msg ""
492                         ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa
493                         msg ""
494                         msg "To get access, your key has to be approved first. For that, visit"
495                         msg "$gitsite_url, then log in, enter the"
496                         msg "\"xonotic\" project, create an \"Issue\" tagged \"Repository Access\""
497                         msg "to apply for access and paste the following output into the issue:"
498                         msg ""
499                         msg "After that, go to your profile settings, \"SSH Keys\", \"Add SSH Key\""
500                         msg "and paste the following output:"
501                         msg ""
502                         msg "`cat ~/.ssh/id_rsa.pub`"
503                 fi
504                 msg ""
505                 msg "Note that you will only have write access to branches that start"
506                 msg "with your user name."
507                 msg
508                 msg "Once you have gotten access, run ./all update -p"
509                 ;;
510         update|pull)
511                 allow_pull=true
512                 need_bestmirror=false
513
514                 newprotocol=
515                 newpushprotocol=
516                 newlocation=
517
518                 case "`git config xonotic.all.mirrorselection 2>/dev/null || true`" in
519                         done)
520                                 ;;
521                         try_same)
522                                 need_bestmirror=true
523                                 ;;
524                         try_all)
525                                 newprotocol="git http"
526                                 newlocation="any"
527                                 need_bestmirror=true
528                                 ;;
529                         *)
530                                 newprotocol= # same protocol
531                                 newlocation="any"
532                                 need_bestmirror=true
533                                 ;;
534                 esac
535
536                 if $need_bestmirror; then
537                         found=false
538                         identifymirror_()
539                         {
540                                 if [ x"$base" = x"$3" ]; then
541                                         found=true
542                                 fi
543                         }
544                         allmirrors identifymirror_
545                         if ! $found; then
546                                 msg ""
547                                 msg "Current mirror not found = $base"
548                                 msg "but the last pull attempt failed."
549                                 msg ""
550                                 msg "Use ./all update -l any to switch to the best mirror."
551                                 msg ""
552                                 need_bestmirror=false
553                         fi
554                 fi
555
556                 while :; do
557                         if [ x"$1" = x"-N" ]; then
558                                 allow_pull=false
559                         elif [ x"$1" = x"-p" ]; then
560                                 newpushprotocol=ssh
561                                 need_bestmirror=true
562                         elif [ x"$1" = x"-s" ]; then
563                                 newprotocol=ssh
564                                 need_bestmirror=true
565                         elif [ x"$1" = x"-g" ]; then
566                                 newprotocol=git
567                                 need_bestmirror=true
568                         elif [ x"$1" = x"-h" ]; then
569                                 newprotocol=http
570                                 need_bestmirror=true
571                         elif [ x"$1" = x"-l" ]; then
572                                 newlocation=$2
573                                 need_bestmirror=true
574                                 shift
575                         else
576                                 break
577                         fi
578                         shift
579                 done
580                 
581                 if $need_bestmirror; then
582                         newbase=`bestmirror "$base" "$newprotocol" "$newlocation"`
583                         if [ -z "$newbase" ]; then
584                                 msg "Could not find any good mirror. Maybe try again later."
585                                 git config xonotic.all.mirrorselection try_all
586                                 exit 1
587                         fi
588                         if [ -n "$newpushprotocol" ]; then
589                                 if [ -n "$pushbase" ]; then
590                                         newpushbase=`bestmirror "$pushbase" "$newpushprotocol" "$newlocation"`
591                                 else
592                                         newpushbase=`bestmirror "$base" "$newpushprotocol" "$newlocation"`
593                                 fi
594                         else
595                                 newpushbase=$pushbase
596                         fi
597
598                         if [ x"$base" != x"$newbase" ] || [ x"$pushbase" != x"$newpushbase" ]; then
599                                 base=$newbase
600                                 pushbase=$newpushbase
601                                 seturl_()
602                                 {
603                                         setrepovars "$@"
604                                         if [ x"$d" = x"." ]; then
605                                                 fix_git_config "$url" "$pushurl"
606                                         fi
607                                 }
608                                 allrepos ifrepoenabled 0 seturl_
609                         fi
610                         git config xonotic.all.mirrorselection done
611                 fi
612
613                 "$SELF" fix_config
614
615                 pull_()
616                 {
617                         setrepovars "$@"
618                         if [ -f "$d0/$d/.git/config" ]; then
619                                 # if we have .no file, skip
620                                 if [ -f "$d0/$d.no" ]; then
621                                         msg "Repository $d disabled by a .no file, delete $d.no to enable; thus, not updated"
622                                         return
623                                 fi
624                                 if $allow_pull; then
625                                         enter "$d0/$d" verbose
626                                         r=`git symbolic-ref HEAD`
627                                         r=${r#refs/heads/}
628                                         if git config branch.$r.remote >/dev/null 2>&1; then
629                                                 o=`( cd "$d0" && git config xonotic.all.mirrorselection 2>/dev/null || true )`
630                                                 ( cd "$d0" && git config xonotic.all.mirrorselection try_same )
631                                                 if ! verbose git pull; then
632                                                         if fix_upstream_rebase_mergefail; then
633                                                                 check_mergeconflict "$d"
634                                                                 $ECHO "Pulling failed. Press ENTER to continue, or Ctrl-C to abort."
635                                                                 read -r DUMMY
636                                                         fi
637                                                 else
638                                                         ( cd "$d0" && git config xonotic.all.mirrorselection "$o" )
639                                                         fix_upstream_rebase_mergeok || true
640                                                 fi
641                                         fi
642
643                                         cd "$d00"
644                                         checkself "$cmd" "$@"
645                                         cd "$d0/$d"
646                                         verbose git remote prune origin
647                                         cd "$d0"
648                                 fi
649                         else
650                                 if [ -d "$d0/$d" ]; then
651                                         if yesno "$d0/$d is in the way, get rid of it and reclone?"; then
652                                                 verbose rm -rf "$d0/$d"
653                                         else
654                                                 echo "Note: $d0/$d will stay broken."
655                                                 return
656                                         fi
657                                 fi
658                                 o=`git config xonotic.all.mirrorselection 2>/dev/null || true`
659                                 git config xonotic.all.mirrorselection try_same
660                                 verbose git clone --branch "$branch" "$url" "$d0/$d"
661                                 git config xonotic.all.mirrorselection "$o"
662                                 enter "$d0/$d" verbose
663                                 fix_git_config "$url" "$pushurl"
664                                 cd "$d0"
665                         fi
666                 }
667                 allrepos ifrepoenabled 0 pull_
668                 ;;
669         checkout|switch)
670                 checkoutflags=
671                 if [ x"$1" = x"-f" ]; then
672                         checkoutflags=-f
673                         shift
674                 fi
675                 remote=$1
676                 branch=$2
677                 if [ -z "$branch" ]; then
678                         case "$remote" in
679                                 origin/*)
680                                         askbranch=${remote#origin/}
681                                         remote=origin
682                                         ;;
683                                 *)
684                                         askbranch=$remote
685                                         remote=origin
686                                         ;;
687                         esac
688                 fi
689                 if [ -n "$checkoutflags" ]; then
690                         set -- -f "$@" # to make checkself work again
691                 fi
692                 exists=false
693                 checkout_()
694                 {
695                         setrepovars "$@"
696                         enter "$d0/$d" verbose
697                         b=$askbranch
698                         if [ -n "$b" ] && git rev-parse "refs/heads/$b" >/dev/null 2>&1; then
699                                 exists=true
700                                 verbose git checkout $checkoutflags "$b"
701                         elif [ -n "$b" ] && git rev-parse "refs/remotes/$remote/$b" >/dev/null 2>&1; then
702                                 exists=true
703                                 verbose git checkout $checkoutflags --track -b "$b" "$remote/$b"
704                         else
705                                 b=$branch
706                                 if git rev-parse "refs/heads/$b" >/dev/null 2>&1; then
707                                         [ -n "$b" ] || exists=true
708                                         verbose git checkout $checkoutflags "$b"
709                                 elif git rev-parse "refs/remotes/$remote/$b" >/dev/null 2>&1; then
710                                         [ -n "$b" ] || exists=true
711                                         verbose git checkout $checkoutflags --track -b "$b" "$remote/$b"
712                                 else
713                                         $ECHO "WTF? Not even branch $b doesn't exist in $d"
714                                         exit 1
715                                 fi
716                         fi
717                         cd "$d00"
718                         checkself "$cmd" "$@"
719                         cd "$d0"
720                 }
721                 allrepos ifrepoenabled 0 checkout_
722                 if ! $exists; then
723                         $ECHO "The requested branch was not found in any repository."
724                 fi
725                 exec "$SELF" branch
726                 ;;
727         branch)
728                 remote=$1
729                 askbranch=$2
730                 srcbranch=$3
731                 if [ -z "$askbranch" ]; then
732                         askbranch=$remote
733                         remote=origin
734                 fi
735                 branch_show_()
736                 {
737                         setrepovars "$@"
738                         enter "$d0/$d"
739                         r=`git symbolic-ref HEAD`
740                         r=${r#refs/heads/}
741                         dv=`visible_repo_name "$d"`
742                         $ECHO "$dv is at $r"
743                         cd "$d0"
744                 }
745                 if [ -n "$askbranch" ]; then
746                         branch_()
747                         {
748                                 setrepovars "$@"
749                                 dv=`visible_repo_name "$d"`
750                                 enter "$d0/$d" verbose
751                                 if git rev-parse "refs/heads/$askbranch" >/dev/null 2>&1; then
752                                         $ECHO "Already having this branch in $dv."
753                                 else
754                                         if yesno "Branch in $dv?"; then
755                                                 if [ -n "$srcbranch" ]; then
756                                                         b=$srcbranch
757                                                 else
758                                                         b=$branch
759                                                         verbose git fetch origin || true
760                                                 fi
761                                                 verbose git checkout -b "$askbranch" "$b"
762                                                 verbose git config "branch.$askbranch.remote" "$remote"
763                                                 verbose git config "branch.$askbranch.merge" "refs/heads/$askbranch"
764                                         fi
765                                 fi
766                                 cd "$d0"
767                         }
768                         allrepos ifrepoenabled 0 branch_
769                 fi
770                 allrepos ifrepoenabled 0 branch_show_
771                 ;;
772         push|commit)
773                 submit=$1
774                 push_()
775                 {
776                         setrepovars "$@"
777                         dv=`visible_repo_name "$d"`
778                         enter "$d0/$d" verbose
779                         r=`git symbolic-ref HEAD`
780                         r=${r#refs/heads/}
781                         diffdata=`git diff --color HEAD`
782                         if [ -n "$diffdata" ]; then
783                                 # we have uncommitted changes
784                                 if yesno "Uncommitted changes in \"$r\" in $dv. Commit?" '$ECHO "$diffdata" | less -r'; then
785                                         verbose git commit -a
786                                 fi
787                         fi
788                         rem=`git config "branch.$r.remote" || $ECHO origin`
789                         bra=`git config "branch.$r.merge" || $ECHO "$r"`
790                         upstream="$rem/${bra#refs/heads/}"
791                         if ! git rev-parse "$upstream" >/dev/null 2>&1; then
792                                 upstream="origin/$branch"
793                         fi
794                         logdata=`git log --color "$upstream".."$r"`
795                         if [ -n "$logdata" ]; then
796                                 if yesno "Push \"$r\" in $dv?" '$ECHO "$logdata" | less -r'; then
797                                         verbose git push "$rem" HEAD
798                                 fi
799                         fi
800                         if [ x"$submit" = x"-s" ]; then
801                                 case "$r" in
802                                         */*)
803                                                 verbose git push "$rem" HEAD:"${bra%%/*}/finished/${bra#*/}"
804                                                 ;;
805                                 esac
806                         fi
807                         cd "$d0"
808                 }
809                 allrepos ifrepoenabled 0 push_
810                 ;;
811         each|foreach)
812                 keep_going=false
813                 if [ x"$1" = x"-k" ]; then
814                         keep_going=true
815                         shift
816                 fi
817                 for d in $repos; do
818                         if verbose cd "$d0/$d"; then
819                                 if $keep_going; then
820                                         verbose "$@" || true
821                                 else
822                                         verbose "$@"
823                                 fi
824                                 cd "$d0"
825                         fi
826                 done
827                 ;;
828         clean)
829                 "$SELF" fix_config
830                 "$SELF" update -N
831                 force=false
832                 gotoupstream=false
833                 fetchupstream=false
834                 gotomaster=false
835                 rmuntracked=false
836                 killbranches=false
837                 # usage:
838                 #   ./all clean [-m] [-f | -fu | -fU] [-r] [-D]
839                 #   ./all clean --reclone
840                 found=false
841                 for X in "$@"; do
842                         if [ x"$X" = x"--reclone" ]; then
843                                 force=true
844                                 fetchupstream=true
845                                 gotoupstream=true
846                                 gotomaster=true
847                                 rmuntracked=true
848                                 killbranches=true
849                         elif [ x"$X" = x"-f" ]; then
850                                 force=true
851                         elif [ x"$X" = x"-u" ]; then
852                                 gotoupstream=true
853                         elif [ x"$X" = x"-U" ]; then
854                                 gotoupstream=true
855                                 fetchupstream=true
856                         elif [ x"$X" = x"-fu" ]; then
857                                 force=true
858                                 gotoupstream=true
859                         elif [ x"$X" = x"-fU" ]; then
860                                 force=true
861                                 gotoupstream=true
862                                 fetchupstream=true
863                         elif [ x"$X" = x"-m" ]; then
864                                 gotomaster=true
865                         elif [ x"$X" = x"-r" ]; then
866                                 rmuntracked=true
867                         elif [ x"$X" = x"-D" ]; then
868                                 killbranches=true
869                         elif $ECHO "$X" | grep '^-FFFF*UUUU*$' >/dev/null; then
870                                 msg ''
871                                 msg "        _____"
872                                 msg "    ,--'-\\P/\`\\  FFFFFFF"
873                                 msg " __/_    B/,-.\\  FFFFFFF"
874                                 msg " /  _\\  (//  O\\\\  FFFFFF"
875                                 msg "| (O  \`) _\\._ _)\\  FFFUU"
876                                 msg "| |___/.^d0~~\"\\  \\ UUUU"
877                                 msg "|     |\`~'     \\ |  UUUU"
878                                 msg "|     |    __,C>|| UUUU"
879                                 msg "\\    /_ ,-/,-'   |  UUUU"
880                                 msg " \\\\_ \\_>~'      /  UUUU-"
881                                 msg ''
882                         else
883                                 msg "Unknown arg: $X"
884                         fi
885                         found=true
886                 done
887                 if ! $found; then
888                         rmuntracked=true
889                 fi
890                 clean_()
891                 {
892                         setrepovars "$@"
893                         verbose cd "$d0/$d"
894                         if $gotoupstream; then
895                                 if ! $force; then
896                                         msg "Must also use -f (delete local changes) when using -u"
897                                         exit 1
898                                 fi
899                                 if $gotomaster; then
900                                         if $fetchupstream; then
901                                                 verbose git fetch origin
902                                                 verbose git remote prune origin
903                                         fi
904                                         verbose git checkout -f "$branch"
905                                         verbose git reset --hard origin/"$branch"
906                                 else
907                                         r=`git symbolic-ref HEAD`
908                                         r=${r#refs/heads/}
909                                         rem=`git config "branch.$r.remote" || $ECHO origin`
910                                         bra=`git config "branch.$r.merge" || $ECHO "$r"`
911                                         upstream="$rem/${bra#refs/heads/}"
912                                         if $fetchupstream; then
913                                                 for t in `git tag -l "xonotic-v"*`; do
914                                                         verbose git tag -d "$t"
915                                                 done
916                                                 verbose git fetch "$rem"
917                                                 verbose git remote prune "$rem"
918                                         fi
919                                         if ! git rev-parse "$upstream" >/dev/null 2>&1; then
920                                                 upstream="origin/$branch"
921                                         fi
922                                         verbose git reset --hard "$upstream"
923                                 fi
924                         elif $gotomaster; then
925                                 if $force; then
926                                         verbose git checkout -f "$branch"
927                                         verbose git reset --hard
928                                 else
929                                         verbose git checkout "$branch"
930                                 fi
931                         elif $force; then
932                                 verbose git reset --hard
933                         fi
934                         if $rmuntracked; then
935                                 case "$d" in
936                                         .)
937                                                 verbose git clean -df || true
938                                                 ;;
939                                         *)
940                                                 verbose git clean -xdf || true
941                                                 ;;
942                                 esac
943                         fi
944                         if $killbranches; then
945                                 git for-each-ref --format='%(refname)' refs/heads/ | while IFS= read -r B; do
946                                         if [ x"$B" != x"`git symbolic-ref HEAD`" ]; then
947                                                 verbose git branch -D "${B#refs/heads/}"
948                                         fi
949                                 done
950                                 git rev-parse refs/heads/master >/dev/null 2>&1 || verbose git branch --track master origin/master || true
951                                 git rev-parse "refs/heads/$branch" >/dev/null 2>&1 || verbose git branch --track "$branch" origin/"$branch" || true
952                         fi
953                         checkself "$cmd" "$@"
954                 }
955                 allrepos ifrepoenabled 0 clean_
956                 ;;
957         help)
958                 $ECHO "  $SELF branch <branch>"
959                 $ECHO "  $SELF branch <remote> <branch> [<srcbranch>]"
960                 $ECHO "  $SELF checkout|switch <branch>"
961                 $ECHO "  $SELF checkout|switch <remote>/<branch>"
962                 $ECHO "  $SELF clean [-m] [-f | -fu | -fU] [-r] [-D]"
963                 $ECHO "  $SELF clean --reclone"
964                 $ECHO "  $SELF each|foreach [-k] command..."
965                 $ECHO "  $SELF fix_upstream_rebase"
966                 $ECHO "  $SELF keygen"
967                 $ECHO "  $SELF push|commit [-s]"
968                 $ECHO "  $SELF update|pull [-N] [-s | -h [-p] | -g [-p]] [-l de|nl|default]"
969                 $ECHO "  $SELF grep \"<regex>\""
970                 handled=false
971                 ;;
972         grep)
973                 for d in $repos; do
974                         if verbose cd "$d0/$d"; then
975                                 git grep -In "$@" || true
976                                 cd "$d0"
977                         fi
978                 done
979                 ;;
980         *)
981                 handled=false
982                 ;;
983 esac