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