README: fix typo
[topgit/pro.git] / tg-update.sh
blob09de91cd60f464039a6d1b37cc46897b79b80484
1 #!/bin/sh
2 # TopGit - A different patch queue manager
3 # Copyright (C) Petr Baudis <pasky@suse.cz> 2008
4 # Copyright (C) Kyle J. McKay <mackyle@gmail.com> 2015,2016,2017
5 # All rights reserved.
6 # GPLv2
8 names= # Branch(es) to update
9 all= # Update all branches
10 pattern= # Branch selection filter for -a
11 current= # Branch we are currently on
12 skipms= # skip missing dependencies
13 stash= # tgstash refs before changes
15 if [ "$(git config --get --bool topgit.autostash 2>/dev/null)" != "false" ]; then
16 # topgit.autostash is true
17 stash=1
20 ## Parse options
22 USAGE="\
23 Usage: ${tgname:-tg} [...] update [--[no-]stash] [--skip-missing] ([<name>...] | -a [<pattern>...])
24 Or: ${tgname:-tg} --continue | -skip | --stop | --abort"
26 usage()
28 if [ "${1:-0}" != 0 ]; then
29 printf '%s\n' "$USAGE" >&2
30 else
31 printf '%s\n' "$USAGE"
33 exit ${1:-0}
36 state_dir="$git_dir/tg-update"
37 mergeours=
38 mergetheirs=
39 mergeresult=
40 stashhash=
42 is_active() {
43 [ -d "$state_dir" ] || return 1
44 [ -s "$state_dir/fullcmd" ] || return 1
45 [ -f "$state_dir/remote" ] || return 1
46 [ -f "$state_dir/skipms" ] || return 1
47 [ -f "$state_dir/all" ] || return 1
48 [ -s "$state_dir/current" ] || return 1
49 [ -s "$state_dir/stashhash" ] || return 1
50 [ -s "$state_dir/name" ] || return 1
51 [ -s "$state_dir/names" ] || return 1
52 [ -f "$state_dir/processed" ] || return 1
53 [ -f "$state_dir/mergeours" ] || return 1
54 [ -f "$state_dir/mergeours" ] || return 1
55 if [ -s "$state_dir/mergeours" ]; then
56 [ -s "$state_dir/mergetheirs" ] || return 1
57 else
58 ! [ -s "$state_dir/mergetheirs" ] || return 1
62 restore_state() {
63 is_active || die "programmer error"
64 IFS= read -r fullcmd <"$state_dir/fullcmd" && [ -n "$fullcmd" ]
65 IFS= read -r base_remote <"$state_dir/remote" || :
66 IFS= read -r skipms <"$state_dir/skipms" || :
67 IFS= read -r all <"$state_dir/all" || :
68 IFS= read -r current <"$state_dir/current" && [ -n "$current" ]
69 IFS= read -r stashhash <"$state_dir/stashhash" && [ -n "$stashhash" ]
70 IFS= read -r name <"$state_dir/name" && [ -n "$name" ]
71 IFS= read -r names <"$state_dir/names" && [ -n "$names" ]
72 IFS= read -r processed <"$state_dir/processed" || :
73 IFS= read -r mergeours <"$state_dir/mergeours" || :
74 IFS= read -r mergetheirs <"$state_dir/mergetheirs" || :
75 if [ -n "$mergeours" ] && [ -n "$mergetheirs" ]; then
76 headhash="$(git rev-parse --quiet --verify HEAD --)" || :
77 if [ -n "$headhash" ]; then
78 parents="$(git --no-pager log -n 1 --format='format:%P' "$headhash" -- 2>/dev/null)" || :
79 if [ "$parents" = "$mergeours $mergetheirs" ]; then
80 mergeresult="$headhash"
83 if [ -z "$mergeresult" ]; then
84 mergeours=
85 mergetheirs=
88 restored=1
91 clear_state() {
92 ! [ -e "$state_dir" ] || rm -rf "$state_dir" >/dev/null 2>&1 || :
95 restarted=
96 isactive=
97 ! is_active || isactive=1
98 if [ -n "$isactive" ] || [ $# -eq 1 -a x"$1" = x"--abort" ]; then
99 [ $# -eq 1 ] && [ x"$1" != x"--status" ] || { do_status; exit 0; }
100 case "$1" in
101 --abort)
102 current=
103 stashhash=
104 if [ -n "$isactive" ]; then
105 IFS= read -r current <"$state_dir/current" || :
106 IFS= read -r stashhash <"$state_dir/stashhash" || :
108 clear_state
109 if [ -n "$isactive" ]; then
110 if [ -n "$stashhash" ]; then
111 $tg revert -f -q -q --no-stash "$stashhash" >/dev/null 2>&1 || :
113 if [ -n "$current" ]; then
114 info "Ok, update aborted, returning to $current"
115 git checkout -f -q "$current"
116 else
117 info "Ok, update aborted. Now, you just need to"
118 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
120 else
121 info "No update was active"
123 exit 0
125 --stop)
126 clear_state
127 info "Ok, update stopped. Now, you just need to"
128 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
129 exit 0
131 --continue|--skip)
132 restore_state
133 if [ "$1" = "--skip" ]; then
134 info "Ok, I will try to continue without updating this branch."
135 git reset --hard -q
136 case " $processed " in *" $name "*);;*)
137 processed="${processed:+$processed }$name"
138 esac
140 # assume user fixed it
141 # we could be left on a detached HEAD if we were resolving
142 # a conflict while merging a base in, fix it with a checkout
143 git checkout -q "$(strip_ref "$name")"
146 do_status
147 exit 1
148 esac
150 clear_state
152 if [ -z "$restored" ]; then
153 while [ -n "$1" ]; do
154 arg="$1"; shift
155 case "$arg" in
156 -a|--all)
157 [ -z "$names$pattern" ] || usage 1
158 all=1;;
159 --skip-missing)
160 skipms=1;;
161 --stash)
162 stash=1;;
163 --no-stash)
164 stash=;;
166 usage;;
168 usage 1;;
170 if [ -z "$all" ]; then
171 names="${names:+$names }$arg"
172 else
173 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$arg")"
176 esac
177 done
178 origpattern="$pattern"
179 [ -z "$pattern" ] && pattern="refs/$topbases"
181 processed=
182 current="$(strip_ref "$(git symbolic-ref -q HEAD)")" || :
183 [ -n "$all$names" ] || names="HEAD"
184 if [ -z "$all" ]; then
185 clean_names() {
186 names=
187 while [ $# -gt 0 ]; do
188 name="$(verify_topgit_branch "$1")"
189 case " $names " in *" $name "*);;*)
190 names="${names:+$names }$name"
191 esac
192 shift
193 done
195 clean_names $names
196 else
197 [ -n "$current" ] || die "cannot return to detached HEAD; switch to another branch"
198 [ -n "$(git rev-parse --verify --quiet HEAD --)" ] ||
199 die "cannot return to unborn branch; switch to another branch"
201 ensure_clean_tree
204 save_state() {
205 mkdir -p "$state_dir"
206 printf '%s\n' "$fullcmd" >"$state_dir/fullcmd"
207 printf '%s\n' "$base_remote" >"$state_dir/remote"
208 printf '%s\n' "$skipms" >"$state_dir/skipms"
209 printf '%s\n' "$all" >"$state_dir/all"
210 printf '%s\n' "$current" >"$state_dir/current"
211 printf '%s\n' "$stashhash" >"$state_dir/stashhash"
212 printf '%s\n' "$name" >"$state_dir/name"
213 printf '%s\n' "$names" >"$state_dir/names"
214 printf '%s\n' "$processed" >"$state_dir/processed"
215 printf '%s\n' "$1" >"$state_dir/mergeours"
216 printf '%s\n' "$2" >"$state_dir/mergetheirs"
219 stash_now_if_requested() {
220 [ -z "$TG_RECURSIVE" ] || return 0
221 [ -z "$stashhash" ] || return 0
222 ensure_ident_available
223 msg="tgupdate: autostash before update"
224 if [ -n "$all" ]; then
225 msg="$msg --all${origpattern:+ $origpattern}"
226 else
227 msg="$msg $names"
229 set -- $names
230 if [ -n "$stash" ]; then
231 $tg tag -q -q -m "$msg" --stash "$@" &&
232 stashhash="$(git rev-parse --quiet --verify refs/tgstash --)" &&
233 [ -n "$stashhash" ] &&
234 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
235 die "requested --stash failed"
236 else
237 $tg tag --anonymous "$@" &&
238 stashhash="$(git rev-parse --quiet --verify TG_STASH --)" &&
239 [ -n "$stashhash" ] &&
240 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
241 die "anonymous --stash failed"
245 recursive_update() {
246 _ret=0
247 on_base=
249 TG_RECURSIVE="[$1] $TG_RECURSIVE"
250 PS1="[$1] $PS1"
251 export PS1
252 update_branch "$1"
253 ) || _ret=$?
254 [ $_ret -eq 3 ] && exit 3
255 return $_ret
258 # If HEAD is a symref to "$1" detach it at its current value
259 detach_symref_head_on_branch() {
260 _hsr="$(git symbolic-ref -q HEAD --)" && [ -n "$_hsr" ] || return 0
261 _hrv="$(git rev-parse --quiet --verify HEAD --)" && [ -n "$_hrv" ] ||
262 die "cannot detach_symref_head_on_branch from unborn branch $_hsr"
263 git update-ref --no-deref -m "detaching HEAD from $_hsr to safely update it" HEAD "$_hrv"
266 # run git merge with the passed in arguments AND --no-stat
267 # return the exit status of git merge
268 # if the returned exit status is no error show a shortstat before
269 # returning assuming the merge was done into the previous HEAD
270 git_merge() {
271 _oldhead="$(git rev-parse --verify HEAD^0)"
272 _ret=0
273 git merge $auhopt --no-stat "$@" || _ret=$?
274 [ "$_ret" != "0" ] || git --no-pager diff-tree --shortstat "$_oldhead" HEAD^0 --
275 return $_ret
278 # similar to git_merge but operates exclusively using a separate index and temp dir
279 # only trivial aggressive automatic (i.e. simple) merges are supported
281 # $1 => '' to discard result, 'refs/?*' to update the specified ref or a varname
282 # $2 => '-m' MUST be '-m'
283 # $3 => commit message AND, if $1 matches refs/?* the update-ref message
284 # $4 => commit-ish to merge as "ours"
285 # $5 => commit-ish to merge as "theirs"
286 # [$6...] => more commit-ishes to merge as "theirs" in octopus
288 # all merging is done in a separate index (or temporary files for simple merges)
289 # if successful the ref or var is updated with the result
290 # otherwise everything is left unchanged and a silent failure occurs
291 # if successful and $1 matches refs/?* it WILL BE UPDATED to a new commit using the
292 # message and appropriate parents AND HEAD WILL BE DETACHED first if it's a symref
293 # to the same ref
294 # otherwise if $1 does not match refs/?* and is not empty the named variable will
295 # be set to contain the resulting commit from the merge
296 # the working tree and index ARE LEFT COMPLETELY UNTOUCHED no matter what
297 v_attempt_index_merge() {
298 [ "$#" -ge 5 ] && [ "$2" = "-m" ] && [ -n "$3" ] && [ -n "$4" ] && [ -n "$5" ] ||
299 die "programmer error: invalid arguments to v_attempt_index_merge: $*"
300 _var="$1"
301 _msg="$3"
302 _head="$4"
303 shift 4
304 rh="$(git rev-parse --quiet --verify "$_head^0" --)" && [ -n "$rh" ] || return 1
305 orh="$rh"
306 _mmsg=
307 newc=
308 _nodt=
309 _same=
310 _mt=
311 _octo=
312 if [ $# -gt 1 ]; then
313 ihl="$(git merge-base --independent "$@")" || return 1
314 set -- $ihl
315 [ $# -ge 1 ] && [ -n "$1" ] || return 1
317 [ $# -eq 1 ] || _octo=1
318 mb="$(git merge-base ${_octo:+--octopus} "$rh" "$@")" && [ -n "$mb" ] || {
319 mb="$(git hash-object -w -t tree --stdin < /dev/null)"
320 _mt=1
322 if [ -z "$_mt" ]; then
323 if [ -n "$_octo" ]; then
324 while [ $# -gt 1 ] && mbh="$(git merge-base "$rh" "$1")" && [ -n "$mbh" ]; do
325 if [ "$rh" = "$mbh" ]; then
326 _mmsg="Fast-forward (no commit created)"
327 rh="$1"
328 shift
329 elif [ "$1" = "$mbh" ]; then
330 shift
331 else
332 break;
334 done
335 if [ $# -eq 1 ]; then
336 _octo=
337 mb="$(git merge-base "$rh" "$1")" && [ -n "$mb" ] || return 1
340 if [ -z "$_octo" ]; then
341 r1="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$r1" ] || return 1
342 set -- "$r1"
343 if [ "$rh" = "$mb" ]; then
344 _mmsg="Fast-forward (no commit created)"
345 newc="$r1"
346 _nodt=1
347 elif [ "$r1" = "$mb" ]; then
348 [ -n "$_mmsg" ] || _mmsg="Already up-to-date!"
349 newc="$rh"
350 _nodt=1
351 _same=1
355 if [ -z "$newc" ]; then
356 inew="$tg_tmp_dir/index.$$"
357 ! [ -e "$inew" ] || rm -f "$inew"
358 itmp="$tg_tmp_dir/output.$$"
359 imrg="$tg_tmp_dir/auto.$$"
360 [ -z "$_octo" ] || >"$imrg"
361 _auto=
362 _parents=
363 _newrh="$rh"
364 while :; do
365 if [ -n "$_parents" ]; then
366 if [ "$(git rev-list --count --max-count=1 "$1" --not "$_newrh" --)" = "0" ]; then
367 shift
368 continue
371 GIT_INDEX_FILE="$inew" git read-tree -m --aggressive -i "$mb" "$rh" "$1" || { rm -f "$inew" "$imrg"; return 1; }
372 GIT_INDEX_FILE="$inew" git ls-files --unmerged --full-name --abbrev :/ >"$itmp" 2>&1 || { rm -f "$inew" "$itmp" "$imrg"; return 1; }
373 ! [ -s "$itmp" ] || {
374 if ! GIT_INDEX_FILE="$inew" TG_TMP_DIR="$tg_tmp_dir" git merge-index -q "$TG_INST_CMDDIR/tg--index-merge-one-file" -a >"$itmp" 2>&1; then
375 rm -f "$inew" "$itmp" "$imrg"
376 return 1
378 if [ -s "$itmp" ]; then
379 if [ -n "$_octo" ]; then
380 cat "$itmp" >>"$imrg"
381 else
382 cat "$itmp"
384 _auto=" automatic"
387 rm -f "$itmp"
388 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
389 _parents="${_parents:+$_parents }-p $1"
390 if [ $# -gt 1 ]; then
391 rh="$newt"
392 shift
393 continue
395 break;
396 done
397 [ -z "$_octo" ] || LC_ALL=C sort -u <"$imrg"
398 rm -f "$inew" "$imrg"
399 newc="$(git commit-tree -p "$orh" $_parents -m "$_msg" "$newt")" && [ -n "$newc" ] || return 1
400 _mmsg="Merge made by the 'trivial aggressive$_auto${_octo:+ octopus}' strategy."
402 case "$_var" in
403 refs/?*)
404 if [ -n "$_same" ]; then
405 _same=
406 if rv="$(git rev-parse --quiet --verify "$_var" --)" && [ "$rv" = "$newc" ]; then
407 _same=1
410 if [ -z "$_same" ]; then
411 detach_symref_head_on_branch "$_var" || return 1
412 # git update-ref returns 0 even on failure :(
413 git update-ref -m "$_msg" "$_var" "$newc" || return 1
417 eval "$_var="'"$newc"'
419 esac
420 echo "$_mmsg"
421 [ -n "$_nodt" ] || git --no-pager diff-tree --shortstat "$orh" "$newc" --
422 return 0
425 # shortcut that passes $3 as a preceding argument (which must match refs/?*)
426 attempt_index_merge() {
427 case "$3" in refs/?*);;*)
428 die "programmer error: invalid arguments to attempt_index_merge: $*"
429 esac
430 v_attempt_index_merge "$3" "$@"
433 on_base=
434 do_base_switch() {
435 [ -n "$1" ] || return 0
437 [ "$1" != "$on_base" ] ||
438 [ "$(git symbolic-ref -q HEAD)" != "refs/$topbases/$1" ]
439 then
440 switch_to_base "$1"
441 on_base="$1"
445 update_branch() {
446 # We are cacheable until the first change
447 become_cacheable
449 _update_name="$1"
450 ## First, take care of our base
452 _depcheck="$(get_temp tg-depcheck)"
453 missing_deps=
454 needs_update "$_update_name" >"$_depcheck" || :
455 if [ -n "$missing_deps" ]; then
456 msg="Some dependencies are missing: $missing_deps"
457 if [ -n "$skipms" ]; then
458 info "$msg; skipping"
459 elif [ -z "$all" ]; then
460 die "$msg"
461 else
462 info "$msg; skipping branch $_update_name"
463 return
466 if [ -s "$_depcheck" ]; then
467 <"$_depcheck" \
468 sed 's/ [^ ]* *$//' | # last is $_update_name
469 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
470 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
471 >"$_depcheck.ideps" \
472 uniq -s 1 # fold branch lines; + always comes before - and thus wins within uniq
474 stash_now_if_requested
476 while read -r depline; do
477 dep="${depline#?}"
478 action="${depline%$dep}"
480 # We do not distinguish between dependencies out-of-date
481 # and base/remote out-of-date cases for $dep here,
482 # but thanks to needs_update returning : or refs/remotes/<remote>/<name>
483 # for the latter, we do correctly recurse here
484 # in both cases.
486 if [ x"$action" = x+ ]; then
487 case " $missing_deps " in *" $dep "*)
488 info "Skipping recursing to missing dependency: $dep"
489 continue
490 esac
491 info "Recursing to $dep..."
492 recursive_update "$dep" || exit 3
494 done <"$_depcheck.ideps"
496 # Create a list of all the fully qualified ref names that need
497 # to be merged into $_update_name's base. This will be done
498 # as an octopus merge if there are no conflicts.
499 deplist=
500 deplines=
501 set --
502 while read -r dep; do
503 dep="${dep#?}"
504 case "$dep" in
505 "refs"/*)
506 set -- "$@" "$dep"
507 case "$dep" in
508 "refs/heads"/*)
509 d="${dep#refs/heads/}"
510 deplist="${deplist:+$deplist }$d"
511 deplines="$deplines$d$lf"
514 d="${dep#refs/}"
515 deplist="${deplist:+$deplist }$d"
516 deplines="$deplines$d$lf"
518 esac
521 set -- "$@" "refs/heads/$dep"
522 deplist="${deplist:+$deplist }$dep"
523 deplines="$deplines$dep$lf"
525 esac
526 done <"$_depcheck.ideps"
528 # Make sure we end up on the correct base branch
529 on_base=
530 if [ $# -ge 2 ]; then
531 info "Updating $_update_name base with deps: $deplist"
532 become_non_cacheable
533 msg="tgupdate: octopus merge $# deps into $topbases/$_update_name$lf$lf$deplines"
534 if attempt_index_merge -m "$msg" "refs/$topbases/$_update_name" "$@"; then
535 set --
536 else
537 info "Octopus merge failed; falling back to multiple 3-way merges"
541 for fulldep in "$@"; do
542 # This will be either a proper topic branch
543 # or a remote base. (branch_needs_update() is called
544 # only on the _dependencies_, not our branch itself!)
546 case "$fulldep" in
547 "refs/heads"/*)
548 dep="${fulldep#refs/heads/}";;
549 "refs"/*)
550 dep="${fulldep#refs/}";;
552 dep="$fulldep";; # this should be a programmer error
553 esac
555 info "Updating $_update_name base with $dep changes..."
556 become_non_cacheable
557 msg="tgupdate: merge $dep into $topbases/$_update_name"
559 ! attempt_index_merge -m "$msg" "refs/$topbases/$_update_name" "$fulldep^0" &&
561 # We need to switch to the base branch
562 # ...but only if we aren't there yet (from failed previous merge)
563 do_base_switch "$_update_name" || die "do_base_switch failed" &&
564 git_merge -m "$msg" "$fulldep^0"
566 then
567 rm "$_depcheck"
568 save_state
569 info "Please commit merge resolution and call \`$tgdisplay update --continue\`"
570 info "(use \`$tgdisplay status\` to see more options)"
571 exit 3
573 done
574 else
575 info "The base is up-to-date."
579 ## Second, update our head with the remote branch
581 plusextra=
582 merge_with="refs/$topbases/$_update_name"
583 if has_remote "$_update_name"; then
584 _rname="refs/remotes/$base_remote/$_update_name"
585 if branch_contains "refs/heads/$_update_name" "$_rname"; then
586 info "The $_update_name head is up-to-date wrt. its remote branch."
587 else
588 stash_now_if_requested
589 info "Reconciling $_update_name base with remote branch updates..."
590 become_non_cacheable
591 msg="tgupdate: merge ${_rname#refs/} onto $topbases/$_update_name"
592 checkours=
593 checktheirs=
594 got_merge_with=
595 if [ -n "$mergeresult" ]; then
596 checkours="$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" || :
597 checktheirs="$(git rev-parse --verify --quiet "$_rname^0" --)" || :
598 if [ "$mergeours" = "$checkours" ] && [ "$mergetheirs" = "$checktheirs" ]; then
599 got_merge_with="$mergeresult"
603 [ -z "$got_merge_with" ] &&
604 ! v_attempt_index_merge "merge_with" -m "$msg" "refs/$topbases/$_update_name" "$_rname^0" &&
606 # *DETACH* our HEAD now!
607 git checkout -q --detach "refs/$topbases/$_update_name" || die "git checkout failed" &&
608 git_merge -m "$msg" "$_rname^0" &&
609 merge_with="$(git rev-parse --verify HEAD --)"
611 then
612 save_state \
613 "$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" \
614 "$(git rev-parse --verify --quiet "$_rname^0" --)"
615 info "Please commit merge resolution and call \`$tgdisplay update --continue\`"
616 info "(use \`$tgdisplay status\` to see more options)"
617 exit 3
619 # Go back but remember we want to merge with this, not base
620 [ -z "$got_merge_with" ] || merge_with="$got_merge_with"
621 plusextra="${_rname#refs/}+"
626 ## Third, update our head with the base
628 if branch_contains "refs/heads/$_update_name" "$merge_with"; then
629 info "The $_update_name head is up-to-date wrt. the base."
630 return 0
632 stash_now_if_requested
633 info "Updating $_update_name against new base..."
634 become_non_cacheable
635 msg="tgupdate: merge ${plusextra}$topbases/$_update_name into $_update_name"
637 ! attempt_index_merge -m "$msg" "refs/heads/$_update_name" "$merge_with^0" &&
639 # Home, sweet home...
640 # (We want to always switch back, in case we were
641 # on the base from failed previous merge.)
642 git checkout -q "$_update_name" || die "git checkout failed" &&
643 git_merge -m "$msg" "$merge_with^0"
645 then
646 save_state
647 info "Please commit merge resolution and call \`$tgdisplay update --continue\`"
648 info "(use \`$tgdisplay status\` to see more options)"
649 exit 3
653 # We are "read-only" and cacheable until the first change
654 tg_read_only=1
655 v_create_ref_cache
657 do_non_annihilated_branches_patterns() {
658 while read -r _pat && [ -n "$_pat" ]; do
659 set -- "$@" "$_pat"
660 done
661 non_annihilated_branches "$@"
664 do_non_annihilated_branches() {
665 if [ -z "$pattern" ]; then
666 non_annihilated_branches
667 else
668 do_non_annihilated_branches_patterns <<-EOT
669 $(sed 'y/ /\n/' <<-LIST
670 $pattern
671 LIST
677 if [ -n "$all" ] && [ -z "$restored" ]; then
678 names=
679 while read name && [ -n "$name" ]; do
680 case " $names " in *" $name "*);;*)
681 names="${names:+$names }$name"
682 esac
683 done <<-EOT
684 $(do_non_annihilated_branches)
688 for name in $names; do
689 case " $processed " in *" $name "*) continue; esac
690 [ -z "$all" ] && case "$names" in *" "*) ! :; esac || info "Proccessing $name..."
691 update_branch "$name" || exit
692 processed="${processed:+$processed }$name"
693 done
695 [ -z "$all" ] && case "$names" in *" "*) ! :; esac ||
696 info "Returning to $current..."
697 git checkout -q "$current"