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