test-lib.sh: make sure exported variables get cached
[topgit/pro.git] / tg-update.sh
blob1ce9b4529bbdfb8134e3c731ae101de2e7a29fef
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#refs/heads/}"
115 checkout_symref_full -f "$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 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
121 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
122 else
123 info "No update was active"
125 exit 0
127 --stop)
128 clear_state
129 info "Ok, update stopped. Now, you just need to"
130 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
131 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
132 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
133 exit 0
135 --continue|--skip)
136 restore_state
137 if [ "$1" = "--skip" ]; then
138 info "Ok, I will try to continue without updating this branch."
139 git reset --hard -q
140 case " $processed " in *" $name "*);;*)
141 processed="${processed:+$processed }$name"
142 esac
144 # assume user fixed it
145 # we could be left on a detached HEAD if we were resolving
146 # a conflict while merging a base in, fix it with a checkout
147 git checkout -q "$(strip_ref "$name")"
150 do_status
151 exit 1
152 esac
154 clear_state
156 if [ -z "$restored" ]; then
157 while [ -n "$1" ]; do
158 arg="$1"; shift
159 case "$arg" in
160 -a|--all)
161 [ -z "$names$pattern" ] || usage 1
162 all=1;;
163 --skip-missing)
164 skipms=1;;
165 --stash)
166 stash=1;;
167 --no-stash)
168 stash=;;
170 usage;;
172 usage 1;;
174 if [ -z "$all" ]; then
175 names="${names:+$names }$arg"
176 else
177 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$arg")"
180 esac
181 done
182 origpattern="$pattern"
183 [ -z "$pattern" ] && pattern="refs/$topbases"
185 processed=
186 current="$(git symbolic-ref -q HEAD)" || :
187 if [ -n "$current" ]; then
188 [ -n "$(git rev-parse --verify --quiet HEAD --)" ] ||
189 die "cannot return to unborn branch; switch to another branch"
190 else
191 current="$(git rev-parse --verify --quiet HEAD)" ||
192 die "cannot return to invalid HEAD; switch to another branch"
194 [ -n "$all$names" ] || names="HEAD"
195 if [ -z "$all" ]; then
196 clean_names() {
197 names=
198 while [ $# -gt 0 ]; do
199 name="$(verify_topgit_branch "$1")"
200 case " $names " in *" $name "*);;*)
201 names="${names:+$names }$name"
202 esac
203 shift
204 done
206 clean_names $names
208 ensure_clean_tree
211 save_state() {
212 mkdir -p "$state_dir"
213 printf '%s\n' "$fullcmd" >"$state_dir/fullcmd"
214 printf '%s\n' "$base_remote" >"$state_dir/remote"
215 printf '%s\n' "$skipms" >"$state_dir/skipms"
216 printf '%s\n' "$all" >"$state_dir/all"
217 printf '%s\n' "$current" >"$state_dir/current"
218 printf '%s\n' "$stashhash" >"$state_dir/stashhash"
219 printf '%s\n' "$name" >"$state_dir/name"
220 printf '%s\n' "$names" >"$state_dir/names"
221 printf '%s\n' "$processed" >"$state_dir/processed"
222 printf '%s\n' "$1" >"$state_dir/mergeours"
223 printf '%s\n' "$2" >"$state_dir/mergetheirs"
226 stash_now_if_requested() {
227 [ -z "$TG_RECURSIVE" ] || return 0
228 [ -z "$stashhash" ] || return 0
229 ensure_ident_available
230 msg="tgupdate: autostash before update"
231 if [ -n "$all" ]; then
232 msg="$msg --all${origpattern:+ $origpattern}"
233 else
234 msg="$msg $names"
236 set -- $names
237 if [ -n "$stash" ]; then
238 $tg tag -q -q -m "$msg" --stash "$@" &&
239 stashhash="$(git rev-parse --quiet --verify refs/tgstash --)" &&
240 [ -n "$stashhash" ] &&
241 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
242 die "requested --stash failed"
243 else
244 $tg tag --anonymous "$@" &&
245 stashhash="$(git rev-parse --quiet --verify TG_STASH --)" &&
246 [ -n "$stashhash" ] &&
247 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
248 die "anonymous --stash failed"
252 recursive_update() {
253 _ret=0
254 on_base=
256 TG_RECURSIVE="[$1] $TG_RECURSIVE"
257 PS1="[$1] $PS1"
258 export PS1
259 update_branch "$1"
260 ) || _ret=$?
261 [ $_ret -eq 3 ] && exit 3
262 return $_ret
265 # If HEAD is a symref to "$1" detach it at its current value
266 detach_symref_head_on_branch() {
267 _hsr="$(git symbolic-ref -q HEAD --)" && [ -n "$_hsr" ] || return 0
268 _hrv="$(git rev-parse --quiet --verify HEAD --)" && [ -n "$_hrv" ] ||
269 die "cannot detach_symref_head_on_branch from unborn branch $_hsr"
270 git update-ref --no-deref -m "detaching HEAD from $_hsr to safely update it" HEAD "$_hrv"
273 # run git merge with the passed in arguments AND --no-stat
274 # return the exit status of git merge
275 # if the returned exit status is no error show a shortstat before
276 # returning assuming the merge was done into the previous HEAD
277 git_merge() {
278 _oldhead="$(git rev-parse --verify HEAD^0)"
279 _ret=0
280 git merge $auhopt --no-stat "$@" || _ret=$?
281 [ "$_ret" != "0" ] || git --no-pager diff-tree --shortstat "$_oldhead" HEAD^0 --
282 return $_ret
285 # similar to git_merge but operates exclusively using a separate index and temp dir
286 # only trivial aggressive automatic (i.e. simple) merges are supported
288 # [optional] '--no-auto' to suppress "automatic" merging, merge fails instead
289 # $1 => '' to discard result, 'refs/?*' to update the specified ref or a varname
290 # $2 => '-m' MUST be '-m'
291 # $3 => commit message AND, if $1 matches refs/?* the update-ref message
292 # $4 => commit-ish to merge as "ours"
293 # $5 => commit-ish to merge as "theirs"
294 # [$6...] => more commit-ishes to merge as "theirs" in octopus
296 # all merging is done in a separate index (or temporary files for simple merges)
297 # if successful the ref or var is updated with the result
298 # otherwise everything is left unchanged and a silent failure occurs
299 # if successful and $1 matches refs/?* it WILL BE UPDATED to a new commit using the
300 # message and appropriate parents AND HEAD WILL BE DETACHED first if it's a symref
301 # to the same ref
302 # otherwise if $1 does not match refs/?* and is not empty the named variable will
303 # be set to contain the resulting commit from the merge
304 # the working tree and index ARE LEFT COMPLETELY UNTOUCHED no matter what
305 v_attempt_index_merge() {
306 _noauto=
307 if [ "$1" = "--no-auto" ]; then
308 _noauto=1
309 shift
311 [ "$#" -ge 5 ] && [ "$2" = "-m" ] && [ -n "$3" ] && [ -n "$4" ] && [ -n "$5" ] ||
312 die "programmer error: invalid arguments to v_attempt_index_merge: $*"
313 _var="$1"
314 _msg="$3"
315 _head="$4"
316 shift 4
317 rh="$(git rev-parse --quiet --verify "$_head^0" --)" && [ -n "$rh" ] || return 1
318 orh="$rh"
319 _mmsg=
320 newc=
321 _nodt=
322 _same=
323 _mt=
324 _octo=
325 if [ $# -gt 1 ]; then
326 ihl="$(git merge-base --independent "$@")" || return 1
327 set -- $ihl
328 [ $# -ge 1 ] && [ -n "$1" ] || return 1
330 [ $# -eq 1 ] || _octo=1
331 mb="$(git merge-base ${_octo:+--octopus} "$rh" "$@")" && [ -n "$mb" ] || {
332 mb="$(git hash-object -w -t tree --stdin < /dev/null)"
333 _mt=1
335 if [ -z "$_mt" ]; then
336 if [ -n "$_octo" ]; then
337 while [ $# -gt 1 ] && mbh="$(git merge-base "$rh" "$1")" && [ -n "$mbh" ]; do
338 if [ "$rh" = "$mbh" ]; then
339 _mmsg="Fast-forward (no commit created)"
340 rh="$1"
341 shift
342 elif [ "$1" = "$mbh" ]; then
343 shift
344 else
345 break;
347 done
348 if [ $# -eq 1 ]; then
349 _octo=
350 mb="$(git merge-base "$rh" "$1")" && [ -n "$mb" ] || return 1
353 if [ -z "$_octo" ]; then
354 r1="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$r1" ] || return 1
355 set -- "$r1"
356 if [ "$rh" = "$mb" ]; then
357 _mmsg="Fast-forward (no commit created)"
358 newc="$r1"
359 _nodt=1
360 elif [ "$r1" = "$mb" ]; then
361 [ -n "$_mmsg" ] || _mmsg="Already up-to-date!"
362 newc="$rh"
363 _nodt=1
364 _same=1
368 if [ -z "$newc" ]; then
369 inew="$tg_tmp_dir/index.$$"
370 ! [ -e "$inew" ] || rm -f "$inew"
371 itmp="$tg_tmp_dir/output.$$"
372 imrg="$tg_tmp_dir/auto.$$"
373 [ -z "$_octo" ] || >"$imrg"
374 _auto=
375 _parents=
376 _newrh="$rh"
377 while :; do
378 if [ -n "$_parents" ]; then
379 if [ "$(git rev-list --count --max-count=1 "$1" --not "$_newrh" --)" = "0" ]; then
380 shift
381 continue
384 GIT_INDEX_FILE="$inew" git read-tree -m --aggressive -i "$mb" "$rh" "$1" || { rm -f "$inew" "$imrg"; return 1; }
385 GIT_INDEX_FILE="$inew" git ls-files --unmerged --full-name --abbrev :/ >"$itmp" 2>&1 || { rm -f "$inew" "$itmp" "$imrg"; return 1; }
386 ! [ -s "$itmp" ] || {
387 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
388 rm -f "$inew" "$itmp" "$imrg"
389 return 1
391 if [ -s "$itmp" ]; then
392 if [ -n "$_noauto" ]; then
393 rm -f "$inew" "$itmp" "$imrg"
394 return 1
396 if [ -n "$_octo" ]; then
397 cat "$itmp" >>"$imrg"
398 else
399 cat "$itmp"
401 _auto=" automatic"
404 rm -f "$itmp"
405 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
406 _parents="${_parents:+$_parents }-p $1"
407 if [ $# -gt 1 ]; then
408 rh="$newt"
409 shift
410 continue
412 break;
413 done
414 [ -z "$_octo" ] || LC_ALL=C sort -u <"$imrg"
415 rm -f "$inew" "$imrg"
416 newc="$(git commit-tree -p "$orh" $_parents -m "$_msg" "$newt")" && [ -n "$newc" ] || return 1
417 _mmsg="Merge made by the 'trivial aggressive$_auto${_octo:+ octopus}' strategy."
419 case "$_var" in
420 refs/?*)
421 if [ -n "$_same" ]; then
422 _same=
423 if rv="$(git rev-parse --quiet --verify "$_var" --)" && [ "$rv" = "$newc" ]; then
424 _same=1
427 if [ -z "$_same" ]; then
428 detach_symref_head_on_branch "$_var" || return 1
429 # git update-ref returns 0 even on failure :(
430 git update-ref -m "$_msg" "$_var" "$newc" || return 1
434 eval "$_var="'"$newc"'
436 esac
437 echo "$_mmsg"
438 [ -n "$_nodt" ] || git --no-pager diff-tree --shortstat "$orh" "$newc" --
439 return 0
442 # shortcut that passes $3 as a preceding argument (which must match refs/?*)
443 attempt_index_merge() {
444 _noauto=
445 if [ "$1" = "--no-auto" ]; then
446 _noauto="$1"
447 shift
449 case "$3" in refs/?*);;*)
450 die "programmer error: invalid arguments to attempt_index_merge: $*"
451 esac
452 v_attempt_index_merge $_noauto "$3" "$@"
455 on_base=
456 do_base_switch() {
457 [ -n "$1" ] || return 0
459 [ "$1" != "$on_base" ] ||
460 [ "$(git symbolic-ref -q HEAD)" != "refs/$topbases/$1" ]
461 then
462 switch_to_base "$1"
463 on_base="$1"
467 update_branch() {
468 # We are cacheable until the first change
469 become_cacheable
471 _update_name="$1"
472 ## First, take care of our base
474 _depcheck="$(get_temp tg-depcheck)"
475 missing_deps=
476 needs_update "$_update_name" >"$_depcheck" || :
477 if [ -n "$missing_deps" ]; then
478 msg="Some dependencies are missing: $missing_deps"
479 if [ -n "$skipms" ]; then
480 info "$msg; skipping"
481 elif [ -z "$all" ]; then
482 die "$msg"
483 else
484 info "$msg; skipping branch $_update_name"
485 return
488 if [ -s "$_depcheck" ]; then
489 <"$_depcheck" \
490 sed 's/ [^ ]* *$//' | # last is $_update_name
491 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
492 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
493 >"$_depcheck.ideps" \
494 uniq -s 1 # fold branch lines; + always comes before - and thus wins within uniq
496 stash_now_if_requested
498 while read -r depline; do
499 dep="${depline#?}"
500 action="${depline%$dep}"
502 # We do not distinguish between dependencies out-of-date
503 # and base/remote out-of-date cases for $dep here,
504 # but thanks to needs_update returning : or refs/remotes/<remote>/<name>
505 # for the latter, we do correctly recurse here
506 # in both cases.
508 if [ x"$action" = x+ ]; then
509 case " $missing_deps " in *" $dep "*)
510 info "Skipping recursing to missing dependency: $dep"
511 continue
512 esac
513 info "Recursing to $dep..."
514 recursive_update "$dep" || exit 3
516 done <"$_depcheck.ideps"
518 # Create a list of all the fully qualified ref names that need
519 # to be merged into $_update_name's base. This will be done
520 # as an octopus merge if there are no conflicts.
521 deplist=
522 deplines=
523 set --
524 while read -r dep; do
525 dep="${dep#?}"
526 case "$dep" in
527 "refs"/*)
528 set -- "$@" "$dep"
529 case "$dep" in
530 "refs/heads"/*)
531 d="${dep#refs/heads/}"
532 deplist="${deplist:+$deplist }$d"
533 deplines="$deplines$d$lf"
536 d="${dep#refs/}"
537 deplist="${deplist:+$deplist }$d"
538 deplines="$deplines$d$lf"
540 esac
543 set -- "$@" "refs/heads/$dep"
544 deplist="${deplist:+$deplist }$dep"
545 deplines="$deplines$dep$lf"
547 esac
548 done <"$_depcheck.ideps"
550 # Make sure we end up on the correct base branch
551 on_base=
552 no_auto=
553 if [ $# -ge 2 ]; then
554 info "Updating $_update_name base with deps: $deplist"
555 become_non_cacheable
556 msg="tgupdate: octopus merge $# deps into $topbases/$_update_name$lf$lf$deplines"
557 if attempt_index_merge -m "$msg" "refs/$topbases/$_update_name" "$@"; then
558 set --
559 else
560 info "Octopus merge failed; falling back to multiple 3-way merges"
561 no_auto="--no-auto"
565 for fulldep in "$@"; do
566 # This will be either a proper topic branch
567 # or a remote base. (branch_needs_update() is called
568 # only on the _dependencies_, not our branch itself!)
570 case "$fulldep" in
571 "refs/heads"/*)
572 dep="${fulldep#refs/heads/}";;
573 "refs"/*)
574 dep="${fulldep#refs/}";;
576 dep="$fulldep";; # this should be a programmer error
577 esac
579 info "Updating $_update_name base with $dep changes..."
580 become_non_cacheable
581 msg="tgupdate: merge $dep into $topbases/$_update_name"
583 ! attempt_index_merge $no_auto -m "$msg" "refs/$topbases/$_update_name" "$fulldep^0" &&
585 # We need to switch to the base branch
586 # ...but only if we aren't there yet (from failed previous merge)
587 do_base_switch "$_update_name" || die "do_base_switch failed" &&
588 git_merge -m "$msg" "$fulldep^0"
590 then
591 rm "$_depcheck"
592 save_state
593 info "Please commit merge resolution and call \`$tgdisplay update --continue\`"
594 info "(use \`$tgdisplay status\` to see more options)"
595 exit 3
597 done
598 else
599 info "The base is up-to-date."
603 ## Second, update our head with the remote branch
605 plusextra=
606 merge_with="refs/$topbases/$_update_name"
607 if has_remote "$_update_name"; then
608 _rname="refs/remotes/$base_remote/$_update_name"
609 if branch_contains "refs/heads/$_update_name" "$_rname"; then
610 info "The $_update_name head is up-to-date wrt. its remote branch."
611 else
612 stash_now_if_requested
613 info "Reconciling $_update_name base with remote branch updates..."
614 become_non_cacheable
615 msg="tgupdate: merge ${_rname#refs/} onto $topbases/$_update_name"
616 checkours=
617 checktheirs=
618 got_merge_with=
619 if [ -n "$mergeresult" ]; then
620 checkours="$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" || :
621 checktheirs="$(git rev-parse --verify --quiet "$_rname^0" --)" || :
622 if [ "$mergeours" = "$checkours" ] && [ "$mergetheirs" = "$checktheirs" ]; then
623 got_merge_with="$mergeresult"
627 [ -z "$got_merge_with" ] &&
628 ! v_attempt_index_merge "merge_with" -m "$msg" "refs/$topbases/$_update_name" "$_rname^0" &&
630 # *DETACH* our HEAD now!
631 git checkout -q --detach "refs/$topbases/$_update_name" || die "git checkout failed" &&
632 git_merge -m "$msg" "$_rname^0" &&
633 merge_with="$(git rev-parse --verify HEAD --)"
635 then
636 save_state \
637 "$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" \
638 "$(git rev-parse --verify --quiet "$_rname^0" --)"
639 info "Please commit merge resolution and call \`$tgdisplay update --continue\`"
640 info "(use \`$tgdisplay status\` to see more options)"
641 exit 3
643 # Go back but remember we want to merge with this, not base
644 [ -z "$got_merge_with" ] || merge_with="$got_merge_with"
645 plusextra="${_rname#refs/}+"
650 ## Third, update our head with the base
652 if branch_contains "refs/heads/$_update_name" "$merge_with"; then
653 info "The $_update_name head is up-to-date wrt. the base."
654 return 0
656 stash_now_if_requested
657 info "Updating $_update_name against new base..."
658 become_non_cacheable
659 msg="tgupdate: merge ${plusextra}$topbases/$_update_name into $_update_name"
661 ! attempt_index_merge -m "$msg" "refs/heads/$_update_name" "$merge_with^0" &&
663 # Home, sweet home...
664 # (We want to always switch back, in case we were
665 # on the base from failed previous merge.)
666 git checkout -q "$_update_name" || die "git checkout failed" &&
667 git_merge -m "$msg" "$merge_with^0"
669 then
670 save_state
671 info "Please commit merge resolution and call \`$tgdisplay update --continue\`"
672 info "(use \`$tgdisplay status\` to see more options)"
673 exit 3
677 # We are "read-only" and cacheable until the first change
678 tg_read_only=1
679 v_create_ref_cache
681 do_non_annihilated_branches_patterns() {
682 while read -r _pat && [ -n "$_pat" ]; do
683 set -- "$@" "$_pat"
684 done
685 non_annihilated_branches "$@"
688 do_non_annihilated_branches() {
689 if [ -z "$pattern" ]; then
690 non_annihilated_branches
691 else
692 do_non_annihilated_branches_patterns <<-EOT
693 $(sed 'y/ /\n/' <<-LIST
694 $pattern
695 LIST
701 if [ -n "$all" ] && [ -z "$restored" ]; then
702 names=
703 while read name && [ -n "$name" ]; do
704 case " $names " in *" $name "*);;*)
705 names="${names:+$names }$name"
706 esac
707 done <<-EOT
708 $(do_non_annihilated_branches)
712 for name in $names; do
713 case " $processed " in *" $name "*) continue; esac
714 [ -z "$all" ] && case "$names" in *" "*) ! :; esac || info "Proccessing $name..."
715 update_branch "$name" || exit
716 processed="${processed:+$processed }$name"
717 done
719 [ -z "$all" ] && case "$names" in *" "*) ! :; esac ||
720 info "Returning to ${current#refs/heads/}..."
721 checkout_symref_full "$current"
722 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
723 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :