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