]> git.xonotic.org Git - xonotic/xonotic.git/blob - misc/tools/all/git.subr
Fix macOS SDL2 framework permissions
[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_ef25519.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."
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_ef25519.pub`"
470                 elif [ -f ~/.ssh/id_rsa.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."
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_rsa.pub`"
485                 elif [ -f ~/.ssh/id_dsa.pub ]; then
486                         msg ""
487                         msg "A key already exists and no new one will be generated. If you"
488                         msg "already have done the procedure for getting your key approved, you"
489                         msg "can skip the following paragraph and already use the repository."
490                         msg ""
491                         msg "To get access, your key has to be approved first. For that, visit"
492                         msg "$gitsite_url, then log in, enter the"
493                         msg "\"xonotic\" project, create an \"Issue\" tagged \"Repository Access\""
494                         msg "to apply for access."
495                         msg ""
496                         msg "After that, go to your profile settings, \"SSH Keys\", \"Add SSH Key\""
497                         msg "and paste the following output:"
498                         msg ""
499                         msg "`cat ~/.ssh/id_dsa.pub`"
500                 else
501                         msg ""
502                         msg "No key has been generated yet. One will be generated now."
503                         msg "If other people are using your computer, it is recommended"
504                         msg "to specify a passphrase. Otherwise you can simply hit ENTER"
505                         msg "when asked for a passphrase."
506                         msg ""
507                         ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa
508                         msg ""
509                         msg "To get access, your key has to be approved first. For that, visit"
510                         msg "$gitsite_url, then log in, enter the"
511                         msg "\"xonotic\" project, create an \"Issue\" tagged \"Repository Access\""
512                         msg "to apply for access."
513                         msg ""
514                         msg "After that, go to your profile settings, \"SSH Keys\", \"Add SSH Key\""
515                         msg "and paste the following output:"
516                         msg ""
517                         msg "`cat ~/.ssh/id_rsa.pub`"
518                 fi
519                 msg ""
520                 msg "Note that you will only have write access to branches that start"
521                 msg "with your user name."
522                 msg
523                 msg "Once you have gotten access, run ./all update -p"
524                 ;;
525         update|pull)
526                 allow_pull=true
527                 need_bestmirror=false
528
529                 newprotocol=
530                 newpushprotocol=
531                 newlocation=
532
533                 case "`git config xonotic.all.mirrorselection 2>/dev/null || true`" in
534                         done)
535                                 ;;
536                         try_same)
537                                 need_bestmirror=true
538                                 ;;
539                         try_all)
540                                 newprotocol="git http"
541                                 newlocation="any"
542                                 need_bestmirror=true
543                                 ;;
544                         *)
545                                 newprotocol= # same protocol
546                                 newlocation="any"
547                                 need_bestmirror=true
548                                 ;;
549                 esac
550
551                 if $need_bestmirror; then
552                         found=false
553                         identifymirror_()
554                         {
555                                 if [ x"$base" = x"$3" ]; then
556                                         found=true
557                                 fi
558                         }
559                         allmirrors identifymirror_
560                         if ! $found; then
561                                 msg ""
562                                 msg "Current mirror not found = $base"
563                                 msg "but the last pull attempt failed."
564                                 msg ""
565                                 msg "Use ./all update -l any to switch to the best mirror."
566                                 msg ""
567                                 need_bestmirror=false
568                         fi
569                 fi
570
571                 while :; do
572                         if [ x"$1" = x"-N" ]; then
573                                 allow_pull=false
574                         elif [ x"$1" = x"-p" ]; then
575                                 newpushprotocol=ssh
576                                 need_bestmirror=true
577                         elif [ x"$1" = x"-s" ]; then
578                                 newprotocol=ssh
579                                 need_bestmirror=true
580                         elif [ x"$1" = x"-g" ]; then
581                                 newprotocol=git
582                                 need_bestmirror=true
583                         elif [ x"$1" = x"-h" ]; then
584                                 newprotocol=http
585                                 need_bestmirror=true
586                         elif [ x"$1" = x"-l" ]; then
587                                 newlocation=$2
588                                 need_bestmirror=true
589                                 shift
590                         else
591                                 break
592                         fi
593                         shift
594                 done
595
596                 if $need_bestmirror; then
597                         newbase=`bestmirror "$base" "$newprotocol" "$newlocation"`
598                         if [ -z "$newbase" ]; then
599                                 msg "Could not find any good mirror. Maybe try again later."
600                                 git config xonotic.all.mirrorselection try_all
601                                 exit 1
602                         fi
603                         if [ -n "$newpushprotocol" ]; then
604                                 if [ -n "$pushbase" ]; then
605                                         newpushbase=`bestmirror "$pushbase" "$newpushprotocol" "$newlocation"`
606                                 else
607                                         newpushbase=`bestmirror "$base" "$newpushprotocol" "$newlocation"`
608                                 fi
609                         else
610                                 newpushbase=$pushbase
611                         fi
612
613                         if [ x"$base" != x"$newbase" ] || [ x"$pushbase" != x"$newpushbase" ]; then
614                                 base=$newbase
615                                 pushbase=$newpushbase
616                                 seturl_()
617                                 {
618                                         setrepovars "$@"
619                                         if [ x"$d" = x"." ]; then
620                                                 fix_git_config "$url" "$pushurl"
621                                         fi
622                                 }
623                                 allrepos ifrepoenabled 0 seturl_
624                         fi
625                         git config xonotic.all.mirrorselection done
626                 fi
627
628                 "$SELF" fix_config
629
630                 pull_()
631                 {
632                         setrepovars "$@"
633                         if [ -f "$d0/$d/.git/config" ]; then
634                                 # if we have .no file, skip
635                                 if [ -f "$d0/$d.no" ]; then
636                                         msg "Repository $d disabled by a .no file, delete $d.no to enable; thus, not updated"
637                                         return
638                                 fi
639                                 if $allow_pull; then
640                                         enter "$d0/$d" verbose
641                                         r=`git symbolic-ref HEAD`
642                                         r=${r#refs/heads/}
643                                         if git config branch.$r.remote >/dev/null 2>&1; then
644                                                 o=`( cd "$d0" && git config xonotic.all.mirrorselection 2>/dev/null || true )`
645                                                 ( cd "$d0" && git config xonotic.all.mirrorselection try_same )
646                                                 if ! verbose git pull; then
647                                                         if fix_upstream_rebase_mergefail; then
648                                                                 check_mergeconflict "$d"
649                                                                 $ECHO "Pulling failed. Press ENTER to continue, or Ctrl-C to abort."
650                                                                 read -r DUMMY
651                                                         fi
652                                                 else
653                                                         ( cd "$d0" && git config xonotic.all.mirrorselection "$o" )
654                                                         fix_upstream_rebase_mergeok || true
655                                                 fi
656                                         fi
657
658                                         cd "$d00"
659                                         checkself "$cmd" "$@"
660                                         cd "$d0/$d"
661                                         verbose git remote prune origin
662                                         cd "$d0"
663                                 fi
664                         else
665                                 if [ -d "$d0/$d" ]; then
666                                         if yesno "$d0/$d is in the way, get rid of it and reclone?"; then
667                                                 verbose rm -rf "$d0/$d"
668                                         else
669                                                 echo "Note: $d0/$d will stay broken."
670                                                 return
671                                         fi
672                                 fi
673                                 o=`git config xonotic.all.mirrorselection 2>/dev/null || true`
674                                 git config xonotic.all.mirrorselection try_same
675                                 verbose git clone --branch "$branch" "$url" "$d0/$d"
676                                 git config xonotic.all.mirrorselection "$o"
677                                 enter "$d0/$d" verbose
678                                 fix_git_config "$url" "$pushurl"
679                                 cd "$d0"
680                         fi
681                 }
682                 allrepos ifrepoenabled 0 pull_
683                 ;;
684         checkout|switch)
685                 checkoutflags=
686                 if [ x"$1" = x"-f" ]; then
687                         checkoutflags=-f
688                         shift
689                 fi
690                 remote=$1
691                 branch=$2
692                 if [ -z "$branch" ]; then
693                         case "$remote" in
694                                 origin/*)
695                                         askbranch=${remote#origin/}
696                                         remote=origin
697                                         ;;
698                                 *)
699                                         askbranch=$remote
700                                         remote=origin
701                                         ;;
702                         esac
703                 fi
704                 if [ -n "$checkoutflags" ]; then
705                         set -- -f "$@" # to make checkself work again
706                 fi
707                 exists=false
708                 checkout_()
709                 {
710                         setrepovars "$@"
711                         enter "$d0/$d" verbose
712                         b=$askbranch
713                         if [ -n "$b" ] && git rev-parse "refs/heads/$b" >/dev/null 2>&1; then
714                                 exists=true
715                                 verbose git checkout $checkoutflags "$b"
716                         elif [ -n "$b" ] && git rev-parse "refs/remotes/$remote/$b" >/dev/null 2>&1; then
717                                 exists=true
718                                 verbose git checkout $checkoutflags --track -b "$b" "$remote/$b"
719                         else
720                                 b=$branch
721                                 if git rev-parse "refs/heads/$b" >/dev/null 2>&1; then
722                                         [ -n "$b" ] || exists=true
723                                         verbose git checkout $checkoutflags "$b"
724                                 elif git rev-parse "refs/remotes/$remote/$b" >/dev/null 2>&1; then
725                                         [ -n "$b" ] || exists=true
726                                         verbose git checkout $checkoutflags --track -b "$b" "$remote/$b"
727                                 else
728                                         $ECHO "WTF? Not even branch $b doesn't exist in $d"
729                                         exit 1
730                                 fi
731                         fi
732                         cd "$d00"
733                         checkself "$cmd" "$@"
734                         cd "$d0"
735                 }
736                 allrepos ifrepoenabled 0 checkout_
737                 if ! $exists; then
738                         $ECHO "The requested branch was not found in any repository."
739                 fi
740                 exec "$SELF" branch
741                 ;;
742         branch)
743                 remote=$1
744                 askbranch=$2
745                 srcbranch=$3
746                 if [ -z "$askbranch" ]; then
747                         askbranch=$remote
748                         remote=origin
749                 fi
750                 branch_show_()
751                 {
752                         setrepovars "$@"
753                         enter "$d0/$d"
754                         r=`git symbolic-ref HEAD`
755                         r=${r#refs/heads/}
756                         dv=`visible_repo_name "$d"`
757                         $ECHO "$dv is at $r"
758                         cd "$d0"
759                 }
760                 if [ -n "$askbranch" ]; then
761                         branch_()
762                         {
763                                 setrepovars "$@"
764                                 dv=`visible_repo_name "$d"`
765                                 enter "$d0/$d" verbose
766                                 if git rev-parse "refs/heads/$askbranch" >/dev/null 2>&1; then
767                                         $ECHO "Already having this branch in $dv."
768                                 else
769                                         if yesno "Branch in $dv?"; then
770                                                 if [ -n "$srcbranch" ]; then
771                                                         b=$srcbranch
772                                                 else
773                                                         b=$branch
774                                                         verbose git fetch origin || true
775                                                 fi
776                                                 verbose git checkout -b "$askbranch" "$b"
777                                                 verbose git config "branch.$askbranch.remote" "$remote"
778                                                 verbose git config "branch.$askbranch.merge" "refs/heads/$askbranch"
779                                         fi
780                                 fi
781                                 cd "$d0"
782                         }
783                         allrepos ifrepoenabled 0 branch_
784                 fi
785                 allrepos ifrepoenabled 0 branch_show_
786                 ;;
787         push|commit)
788                 submit=$1
789                 push_()
790                 {
791                         setrepovars "$@"
792                         dv=`visible_repo_name "$d"`
793                         enter "$d0/$d" verbose
794                         r=`git symbolic-ref HEAD`
795                         r=${r#refs/heads/}
796                         diffdata=`git diff --color HEAD`
797                         if [ -n "$diffdata" ]; then
798                                 # we have uncommitted changes
799                                 if yesno "Uncommitted changes in \"$r\" in $dv. Commit?" '$ECHO "$diffdata" | less -r'; then
800                                         verbose git commit -a
801                                 fi
802                         fi
803                         rem=`git config "branch.$r.remote" || $ECHO origin`
804                         bra=`git config "branch.$r.merge" || $ECHO "$r"`
805                         upstream="$rem/${bra#refs/heads/}"
806                         if ! git rev-parse "$upstream" >/dev/null 2>&1; then
807                                 upstream="origin/$branch"
808                         fi
809                         logdata=`git log --color "$upstream".."$r"`
810                         if [ -n "$logdata" ]; then
811                                 if yesno "Push \"$r\" in $dv?" '$ECHO "$logdata" | less -r'; then
812                                         verbose git push "$rem" HEAD
813                                 fi
814                         fi
815                         if [ x"$submit" = x"-s" ]; then
816                                 case "$r" in
817                                         */*)
818                                                 verbose git push "$rem" HEAD:"${bra%%/*}/finished/${bra#*/}"
819                                                 ;;
820                                 esac
821                         fi
822                         cd "$d0"
823                 }
824                 allrepos ifrepoenabled 0 push_
825                 ;;
826         each|foreach)
827                 keep_going=false
828                 if [ x"$1" = x"-k" ]; then
829                         keep_going=true
830                         shift
831                 fi
832                 for d in $repos; do
833                         if verbose cd "$d0/$d"; then
834                                 if $keep_going; then
835                                         verbose "$@" || true
836                                 else
837                                         verbose "$@"
838                                 fi
839                                 cd "$d0"
840                         fi
841                 done
842                 ;;
843         clean)
844                 "$SELF" fix_config
845                 "$SELF" update -N
846                 force=false
847                 gotoupstream=false
848                 fetchupstream=false
849                 gotomaster=false
850                 rmuntracked=false
851                 killbranches=false
852                 # usage:
853                 #   ./all clean [-m] [-f | -fu | -fU] [-r] [-D]
854                 #   ./all clean --reclone
855                 found=false
856                 for X in "$@"; do
857                         if [ x"$X" = x"--reclone" ]; then
858                                 force=true
859                                 fetchupstream=true
860                                 gotoupstream=true
861                                 gotomaster=true
862                                 rmuntracked=true
863                                 killbranches=true
864                         elif [ x"$X" = x"-f" ]; then
865                                 force=true
866                         elif [ x"$X" = x"-u" ]; then
867                                 gotoupstream=true
868                         elif [ x"$X" = x"-U" ]; then
869                                 gotoupstream=true
870                                 fetchupstream=true
871                         elif [ x"$X" = x"-fu" ]; then
872                                 force=true
873                                 gotoupstream=true
874                         elif [ x"$X" = x"-fU" ]; then
875                                 force=true
876                                 gotoupstream=true
877                                 fetchupstream=true
878                         elif [ x"$X" = x"-m" ]; then
879                                 gotomaster=true
880                         elif [ x"$X" = x"-r" ]; then
881                                 rmuntracked=true
882                         elif [ x"$X" = x"-D" ]; then
883                                 killbranches=true
884                         elif $ECHO "$X" | grep '^-FFFF*UUUU*$' >/dev/null; then
885                                 msg ''
886                                 msg "        _____"
887                                 msg "    ,--'-\\P/\`\\  FFFFFFF"
888                                 msg " __/_    B/,-.\\  FFFFFFF"
889                                 msg " /  _\\  (//  O\\\\  FFFFFF"
890                                 msg "| (O  \`) _\\._ _)\\  FFFUU"
891                                 msg "| |___/.^d0~~\"\\  \\ UUUU"
892                                 msg "|     |\`~'     \\ |  UUUU"
893                                 msg "|     |    __,C>|| UUUU"
894                                 msg "\\    /_ ,-/,-'   |  UUUU"
895                                 msg " \\\\_ \\_>~'      /  UUUU-"
896                                 msg ''
897                         else
898                                 msg "Unknown arg: $X"
899                         fi
900                         found=true
901                 done
902                 if ! $found; then
903                         rmuntracked=true
904                 fi
905                 clean_()
906                 {
907                         setrepovars "$@"
908                         verbose cd "$d0/$d"
909                         if $gotoupstream; then
910                                 if ! $force; then
911                                         msg "Must also use -f (delete local changes) when using -u"
912                                         exit 1
913                                 fi
914                                 if $gotomaster; then
915                                         if $fetchupstream; then
916                                                 verbose git fetch origin
917                                                 verbose git remote prune origin
918                                         fi
919                                         verbose git checkout -f "$branch"
920                                         verbose git reset --hard origin/"$branch"
921                                 else
922                                         r=`git symbolic-ref HEAD`
923                                         r=${r#refs/heads/}
924                                         rem=`git config "branch.$r.remote" || $ECHO origin`
925                                         bra=`git config "branch.$r.merge" || $ECHO "$r"`
926                                         upstream="$rem/${bra#refs/heads/}"
927                                         if $fetchupstream; then
928                                                 for t in `git tag -l "xonotic-v"*`; do
929                                                         verbose git tag -d "$t"
930                                                 done
931                                                 verbose git fetch "$rem"
932                                                 verbose git remote prune "$rem"
933                                         fi
934                                         if ! git rev-parse "$upstream" >/dev/null 2>&1; then
935                                                 upstream="origin/$branch"
936                                         fi
937                                         verbose git reset --hard "$upstream"
938                                 fi
939                         elif $gotomaster; then
940                                 if $force; then
941                                         verbose git checkout -f "$branch"
942                                         verbose git reset --hard
943                                 else
944                                         verbose git checkout "$branch"
945                                 fi
946                         elif $force; then
947                                 verbose git reset --hard
948                         fi
949                         if $rmuntracked; then
950                                 case "$d" in
951                                         .)
952                                                 verbose git clean -df || true
953                                                 ;;
954                                         *)
955                                                 verbose git clean -xdf || true
956                                                 ;;
957                                 esac
958                         fi
959                         if $killbranches; then
960                                 git for-each-ref --format='%(refname)' refs/heads/ | while IFS= read -r B; do
961                                         if [ x"$B" != x"`git symbolic-ref HEAD`" ]; then
962                                                 verbose git branch -D "${B#refs/heads/}"
963                                         fi
964                                 done
965                                 git rev-parse "refs/heads/$branch" >/dev/null 2>&1 || verbose git branch --track "$branch" origin/"$branch" || true
966                         fi
967                         checkself "$cmd" "$@"
968                 }
969                 allrepos ifrepoenabled 0 clean_
970                 ;;
971         help)
972                 $ECHO "  $SELF branch <branch>"
973                 $ECHO "  $SELF branch <remote> <branch> [<srcbranch>]"
974                 $ECHO "  $SELF checkout|switch <branch>"
975                 $ECHO "  $SELF checkout|switch <remote>/<branch>"
976                 $ECHO "  $SELF clean [-m] [-f | -fu | -fU] [-r] [-D]"
977                 $ECHO "  $SELF clean --reclone"
978                 $ECHO "  $SELF each|foreach [-k] command..."
979                 $ECHO "  $SELF fix_upstream_rebase"
980                 $ECHO "  $SELF keygen"
981                 $ECHO "  $SELF push|commit [-s]"
982                 $ECHO "  $SELF update|pull [-N] [-s | -h [-p] | -g [-p]] [-l de|nl|default]"
983                 $ECHO "  $SELF grep \"<regex>\""
984                 handled=false
985                 ;;
986         grep)
987                 for d in $repos; do
988                         if verbose cd "$d0/$d"; then
989                                 git grep -In "$@" || true
990                                 cd "$d0"
991                         fi
992                 done
993                 ;;
994         *)
995                 handled=false
996                 ;;
997 esac