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