]> git.xonotic.org Git - xonotic/xonotic.git/blob - misc/tools/all/git.subr
Try implementing some grep functionality for ./all script.
[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 ! { time -p sh -c 'true'; } >/dev/null 2>&1; 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                 { time -p 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 "$url" "$d0/$d"
652                                 git config xonotic.all.mirrorselection "$o"
653                                 enter "$d0/$d" verbose
654                                 fix_git_config "$url" "$pushurl"
655                                 if [ "$branch" != "master" ]; then
656                                         verbose git checkout --track -b "$branch" origin/"$branch"
657                                 fi
658                                 cd "$d0"
659                         fi
660                 }
661                 allrepos ifrepoenabled 0 pull_
662                 ;;
663         checkout|switch)
664                 checkoutflags=
665                 if [ x"$1" = x"-f" ]; then
666                         checkoutflags=-f
667                         shift
668                 fi
669                 remote=$1
670                 branch=$2
671                 if [ -z "$branch" ]; then
672                         case "$remote" in
673                                 origin/*)
674                                         askbranch=${remote#origin/}
675                                         remote=origin
676                                         ;;
677                                 *)
678                                         askbranch=$remote
679                                         remote=origin
680                                         ;;
681                         esac
682                 fi
683                 if [ -n "$checkoutflags" ]; then
684                         set -- -f "$@" # to make checkself work again
685                 fi
686                 exists=false
687                 checkout_()
688                 {
689                         setrepovars "$@"
690                         enter "$d0/$d" verbose
691                         b=$askbranch
692                         if [ -n "$b" ] && git rev-parse "refs/heads/$b" >/dev/null 2>&1; then
693                                 exists=true
694                                 verbose git checkout $checkoutflags "$b"
695                         elif [ -n "$b" ] && git rev-parse "refs/remotes/$remote/$b" >/dev/null 2>&1; then
696                                 exists=true
697                                 verbose git checkout $checkoutflags --track -b "$b" "$remote/$b"
698                         else
699                                 b=$branch
700                                 if git rev-parse "refs/heads/$b" >/dev/null 2>&1; then
701                                         verbose git checkout $checkoutflags "$b"
702                                 elif git rev-parse "refs/remotes/$remote/$b" >/dev/null 2>&1; then
703                                         verbose git checkout $checkoutflags --track -b "$b" "$remote/$b"
704                                 else
705                                         $ECHO "WTF? Not even branch $b doesn't exist in $d"
706                                         exit 1
707                                 fi
708                         fi
709                         cd "$d00"
710                         checkself "$cmd" "$@"
711                         cd "$d0"
712                 }
713                 allrepos ifrepoenabled 0 checkout_
714                 if ! $exists; then
715                         $ECHO "The requested branch was not found in any repository."
716                 fi
717                 exec "$SELF" branch
718                 ;;
719         branch)
720                 remote=$1
721                 askbranch=$2
722                 srcbranch=$3
723                 if [ -z "$askbranch" ]; then
724                         askbranch=$remote
725                         remote=origin
726                 fi
727                 branch_show_()
728                 {
729                         setrepovars "$@"
730                         enter "$d0/$d"
731                         r=`git symbolic-ref HEAD`
732                         r=${r#refs/heads/}
733                         dv=`visible_repo_name "$d"`
734                         $ECHO "$dv is at $r"
735                         cd "$d0"
736                 }
737                 if [ -n "$askbranch" ]; then
738                         branch_()
739                         {
740                                 setrepovars "$@"
741                                 dv=`visible_repo_name "$d"`
742                                 enter "$d0/$d" verbose
743                                 if git rev-parse "refs/heads/$askbranch" >/dev/null 2>&1; then
744                                         $ECHO "Already having this branch in $dv."
745                                 else
746                                         if yesno "Branch in $dv?"; then
747                                                 if [ -n "$srcbranch" ]; then
748                                                         b=$srcbranch
749                                                 else
750                                                         b=$branch
751                                                         verbose git fetch origin || true
752                                                 fi
753                                                 verbose git checkout -b "$askbranch" "$b"
754                                                 verbose git config "branch.$askbranch.remote" "$remote"
755                                                 verbose git config "branch.$askbranch.merge" "refs/heads/$askbranch"
756                                         fi
757                                 fi
758                                 cd "$d0"
759                         }
760                         allrepos ifrepoenabled 0 branch_
761                 fi
762                 allrepos ifrepoenabled 0 branch_show_
763                 ;;
764         push|commit)
765                 submit=$1
766                 push_()
767                 {
768                         setrepovars "$@"
769                         dv=`visible_repo_name "$d"`
770                         enter "$d0/$d" verbose
771                         r=`git symbolic-ref HEAD`
772                         r=${r#refs/heads/}
773                         diffdata=`git diff --color HEAD`
774                         if [ -n "$diffdata" ]; then
775                                 # we have uncommitted changes
776                                 if yesno "Uncommitted changes in \"$r\" in $dv. Commit?" '$ECHO "$diffdata" | less -r'; then
777                                         verbose git commit -a
778                                 fi
779                         fi
780                         rem=`git config "branch.$r.remote" || $ECHO origin`
781                         bra=`git config "branch.$r.merge" || $ECHO "$r"`
782                         upstream="$rem/${bra#refs/heads/}"
783                         if ! git rev-parse "$upstream" >/dev/null 2>&1; then
784                                 upstream="origin/$branch"
785                         fi
786                         logdata=`git log --color "$upstream".."$r"`
787                         if [ -n "$logdata" ]; then
788                                 if yesno "Push \"$r\" in $dv?" '$ECHO "$logdata" | less -r'; then
789                                         verbose git push "$rem" HEAD
790                                 fi
791                         fi
792                         if [ x"$submit" = x"-s" ]; then
793                                 case "$r" in
794                                         */*)
795                                                 verbose git push "$rem" HEAD:"${bra%%/*}/finished/${bra#*/}"
796                                                 ;;
797                                 esac
798                         fi
799                         cd "$d0"
800                 }
801                 allrepos ifrepoenabled 0 push_
802                 ;;
803         each|foreach)
804                 keep_going=false
805                 if [ x"$1" = x"-k" ]; then
806                         keep_going=true
807                         shift
808                 fi
809                 for d in $repos; do
810                         if verbose cd "$d0/$d"; then
811                                 if $keep_going; then
812                                         verbose "$@" || true
813                                 else
814                                         verbose "$@"
815                                 fi
816                                 cd "$d0"
817                         fi
818                 done
819                 ;;
820         clean)
821                 "$SELF" fix_config
822                 "$SELF" update -N
823                 force=false
824                 gotoupstream=false
825                 fetchupstream=false
826                 gotomaster=false
827                 rmuntracked=false
828                 killbranches=false
829                 # usage:
830                 #   ./all clean [-m] [-f | -fu | -fU] [-r] [-D]
831                 #   ./all clean --reclone
832                 found=false
833                 for X in "$@"; do
834                         if [ x"$X" = x"--reclone" ]; then
835                                 force=true
836                                 fetchupstream=true
837                                 gotoupstream=true
838                                 gotomaster=true
839                                 rmuntracked=true
840                                 killbranches=true
841                         elif [ x"$X" = x"-f" ]; then
842                                 force=true
843                         elif [ x"$X" = x"-u" ]; then
844                                 gotoupstream=true
845                         elif [ x"$X" = x"-U" ]; then
846                                 gotoupstream=true
847                                 fetchupstream=true
848                         elif [ x"$X" = x"-fu" ]; then
849                                 force=true
850                                 gotoupstream=true
851                         elif [ x"$X" = x"-fU" ]; then
852                                 force=true
853                                 gotoupstream=true
854                                 fetchupstream=true
855                         elif [ x"$X" = x"-m" ]; then
856                                 gotomaster=true
857                         elif [ x"$X" = x"-r" ]; then
858                                 rmuntracked=true
859                         elif [ x"$X" = x"-D" ]; then
860                                 killbranches=true
861                         elif $ECHO "$X" | grep '^-FFFF*UUUU*$' >/dev/null; then
862                                 msg ''
863                                 msg "        _____"
864                                 msg "    ,--'-\\P/\`\\  FFFFFFF"
865                                 msg " __/_    B/,-.\\  FFFFFFF"
866                                 msg " /  _\\  (//  O\\\\  FFFFFF"
867                                 msg "| (O  \`) _\\._ _)\\  FFFUU"
868                                 msg "| |___/.^d0~~\"\\  \\ UUUU"
869                                 msg "|     |\`~'     \\ |  UUUU"
870                                 msg "|     |    __,C>|| UUUU"
871                                 msg "\\    /_ ,-/,-'   |  UUUU"
872                                 msg " \\\\_ \\_>~'      /  UUUU-"
873                                 msg ''
874                         else
875                                 msg "Unknown arg: $X"
876                         fi
877                         found=true
878                 done
879                 if ! $found; then
880                         rmuntracked=true
881                 fi
882                 clean_()
883                 {
884                         setrepovars "$@"
885                         verbose cd "$d0/$d"
886                         if $gotoupstream; then
887                                 if ! $force; then
888                                         msg "Must also use -f (delete local changes) when using -u"
889                                         exit 1
890                                 fi
891                                 if $gotomaster; then
892                                         if $fetchupstream; then
893                                                 verbose git fetch origin
894                                                 verbose git remote prune origin
895                                         fi
896                                         verbose git checkout -f "$branch"
897                                         verbose git reset --hard origin/"$branch"
898                                 else
899                                         r=`git symbolic-ref HEAD`
900                                         r=${r#refs/heads/}
901                                         rem=`git config "branch.$r.remote" || $ECHO origin`
902                                         bra=`git config "branch.$r.merge" || $ECHO "$r"`
903                                         upstream="$rem/${bra#refs/heads/}"
904                                         if $fetchupstream; then
905                                                 for t in `git tag -l "xonotic-v"*`; do
906                                                         verbose git tag -d "$t"
907                                                 done
908                                                 verbose git fetch "$rem"
909                                                 verbose git remote prune "$rem"
910                                         fi
911                                         if ! git rev-parse "$upstream" >/dev/null 2>&1; then
912                                                 upstream="origin/$branch"
913                                         fi
914                                         verbose git reset --hard "$upstream"
915                                 fi
916                         elif $gotomaster; then
917                                 if $force; then
918                                         verbose git checkout -f "$branch"
919                                         verbose git reset --hard
920                                 else
921                                         verbose git checkout "$branch"
922                                 fi
923                         elif $force; then
924                                 verbose git reset --hard
925                         fi
926                         if $rmuntracked; then
927                                 case "$d" in
928                                         .)
929                                                 verbose git clean -df || true
930                                                 ;;
931                                         *)
932                                                 verbose git clean -xdf || true
933                                                 ;;
934                                 esac
935                         fi
936                         if $killbranches; then
937                                 git for-each-ref --format='%(refname)' refs/heads/ | while IFS= read -r B; do
938                                         if [ x"$B" != x"`git symbolic-ref HEAD`" ]; then
939                                                 verbose git branch -D "${B#refs/heads/}"
940                                         fi
941                                 done
942                                 git rev-parse refs/heads/master >/dev/null 2>&1 || verbose git branch --track master origin/master || true
943                                 git rev-parse "refs/heads/$branch" >/dev/null 2>&1 || verbose git branch --track "$branch" origin/"$branch" || true
944                         fi
945                         checkself "$cmd" "$@"
946                 }
947                 allrepos ifrepoenabled 0 clean_
948                 ;;
949         help)
950                 $ECHO "  $SELF branch <branch>"
951                 $ECHO "  $SELF branch <remote> <branch> [<srcbranch>]"
952                 $ECHO "  $SELF checkout|switch <branch>"
953                 $ECHO "  $SELF checkout|switch <remote>/<branch>"
954                 $ECHO "  $SELF clean [-m] [-f | -fu | -fU] [-r] [-D]"
955                 $ECHO "  $SELF clean --reclone"
956                 $ECHO "  $SELF each|foreach [-k] command..."
957                 $ECHO "  $SELF fix_upstream_rebase"
958                 $ECHO "  $SELF keygen"
959                 $ECHO "  $SELF push|commit [-s]"
960                 $ECHO "  $SELF update|pull [-N] [-s | -h [-p] | -g [-p]] [-l de|nl|default]"
961                 handled=false
962                 ;;
963         grep)
964                 for d in $repos; do
965                         if verbose cd "$d0/$d"; then
966                                         git grep -I "$@" || true
967                                 cd "$d0"
968                         fi
969                 done
970                 ;;
971         *)
972                 handled=false
973                 ;;
974 esac