testlib: make --run implicitly activate --quiet without --no-quiet
[topgit/pro.git] / tg-update.sh
blobfbca187ee42653ba0f8b4ac141f813bd8e394770
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 name1= # first name
10 name2= # second name
11 namecnt=0 # how many names were seen
12 all= # Update all branches
13 pattern= # Branch selection filter for -a
14 current= # Branch we are currently on
15 skipms= # skip missing dependencies
16 stash= # tgstash refs before changes
17 quiet= # be quieter
18 basemode= # true if --base active
19 editmode= # 0, 1 or empty to force none, force edit or default
20 basemsg= # message for --base merge commit
21 basefile= # message file for --base merge commit
22 basenc= # --no-commit on merge
23 basefrc= # --force non-ff update
24 setautoupdate=1 # temporarily set rerere.autoUpdate to true
26 if [ "$(git config --get --bool topgit.autostash 2>/dev/null)" != "false" ]; then
27 # topgit.autostash is true (or unset)
28 stash=1
31 ## Parse options
33 USAGE="\
34 Usage: ${tgname:-tg} [...] update [--[no-]stash] [--skip-missing] ([<name>...] | -a [<pattern>...])
35 Or: ${tgname:-tg} [...] update --base [-F <file> | -m <msg>] [--[no-]edit] [-f] <base-branch> <ref>
36 Or: ${tgname:-tg} [...] update --continue | -skip | --stop | --abort"
38 usage()
40 if [ "${1:-0}" != 0 ]; then
41 printf '%s\n' "$USAGE" >&2
42 else
43 printf '%s\n' "$USAGE"
45 exit ${1:-0}
48 # --base mode comes here with $1 set to <base-branch> and $2 set to <ref>
49 # and all options already parsed and validated into above-listed flags
50 # this function should exit after returning to "$current"
51 do_base_mode()
53 v_verify_topgit_branch tgbranch "$1"
54 depcnt="$(git cat-file blob "refs/heads/$tgbranch:.topdeps" 2>/dev/null | awk 'END {print NR}')"
55 if [ $depcnt -gt 0 ]; then
56 grammar="dependency"
57 [ $depcnt -eq 1 ] || grammar="dependencies"
58 die "'$tgbranch' is not a TopGit [BASE] branch (it has $depcnt $grammar)"
60 newrev="$(git rev-parse --verify "$2^0" --)" && [ -n "$newrev" ] ||
61 die "not a valid commit-ish: $2"
62 baserev="$(ref_exists_rev "refs/$topbases/$tgbranch")" && [ -n "$baserev" ] ||
63 die "unable to get current base commit for branch '$tgbranch'"
64 if [ "$baserev" = "$newrev" ]; then
65 [ -n "$quiet" ] || echo "No change"
66 exit 0
68 alreadymerged=
69 ! contained_by "$newrev" "refs/heads/$tgbranch" || alreadymerged=1
70 if [ -z "$basefrc" ] && ! contained_by "$baserev" "$newrev"; then
71 die "Refusing non-fast-forward update of base without --force"
74 # check that we can checkout the branch
76 [ -n "$alreadymerged" ] || git read-tree -n -u -m "refs/heads/$tgbranch" ||
77 die "git checkout \"$branch\" would fail"
79 # and make sure everything's clean and we know who we are
81 [ -n "$alreadymerged" ] || ensure_clean_tree
82 ensure_ident_available
84 # always auto stash even if it's just to the anonymous stash TG_STASH
86 stashmsg="tgupdate: autostash before --base $tgbranch update"
87 if [ -n "$stash" ]; then
88 tg tag -q -q -m "$stashmsg" --stash "$tgbranch" &&
89 stashhash="$(git rev-parse --quiet --verify refs/tgstash --)" &&
90 [ -n "$stashhash" ] &&
91 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
92 die "requested --stash failed"
93 else
94 tg tag --anonymous "$tgbranch" &&
95 stashhash="$(git rev-parse --quiet --verify TG_STASH --)" &&
96 [ -n "$stashhash" ] &&
97 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
98 die "anonymous --stash failed"
101 # proceed with the update
103 git update-ref -m "tg update --base $tgbranch $2" "refs/$topbases/$tgbranch" "$newrev" "$baserev" ||
104 die "Unable to update base ref"
105 if [ -n "$alreadymerged" ]; then
106 [ -n "$quiet" ] || echo "Already contained in branch (base updated)"
107 exit 0
109 git checkout -q $iowopt "$tgbranch" || die "git checkout failed"
110 msgopt=
111 [ -z "$basemsg" ] || msgopt='-m "$basemsg"'
112 fileopt=
113 [ -z "$basefile" ] || fileopt='-F "$basefile"'
114 if [ -n "$editmode" ]; then
115 if [ "$editmode" = "0" ]; then
116 editopt="--no-edit"
117 else
118 editopt="--edit"
120 else
121 if [ -z "$basemsg$basefile" ]; then
122 editopt="--edit"
123 basemsg="tg update --base $tgbranch $2"
124 msgopt='-m "$basemsg"'
125 else
126 editopt="--no-edit"
129 ncopt=
130 [ -z "$basenc" ] || ncopt="--no-commit"
131 eval git merge --no-ff --no-log --no-stat $auhopt $ncopt $editopt "$msgopt" "$fileopt" "refs/$topbases/$tgbranch" -- || exit
132 [ -n "$basenc" ] || checkout_symref_full "$current"
133 exit
136 state_dir="$git_dir/tg-update"
137 mergeours=
138 mergetheirs=
139 mergeresult=
140 stashhash=
141 next_no_auto=
142 merging_topfiles=
144 is_active() {
145 [ -d "$state_dir" ] || return 1
146 [ -s "$state_dir/fullcmd" ] || return 1
147 [ -f "$state_dir/remote" ] || return 1
148 [ -f "$state_dir/skipms" ] || return 1
149 [ -f "$state_dir/all" ] || return 1
150 [ -s "$state_dir/current" ] || return 1
151 [ -s "$state_dir/stashhash" ] || return 1
152 [ -s "$state_dir/name" ] || return 1
153 [ -s "$state_dir/names" ] || return 1
154 [ -f "$state_dir/processed" ] || return 1
155 [ -f "$state_dir/no_auto" ] || return 1
156 [ -f "$state_dir/setautoupdate" ] || return 1
157 [ -f "$state_dir/merging_topfiles" ] || return 1
158 [ -f "$state_dir/mergeours" ] || return 1
159 [ -f "$state_dir/mergeours" ] || return 1
160 if [ -s "$state_dir/mergeours" ]; then
161 [ -s "$state_dir/mergetheirs" ] || return 1
162 else
163 ! [ -s "$state_dir/mergetheirs" ] || return 1
167 restore_state() {
168 is_active || die "programmer error"
169 IFS= read -r fullcmd <"$state_dir/fullcmd" && [ -n "$fullcmd" ]
170 IFS= read -r base_remote <"$state_dir/remote" || :
171 IFS= read -r skipms <"$state_dir/skipms" || :
172 IFS= read -r all <"$state_dir/all" || :
173 IFS= read -r current <"$state_dir/current" && [ -n "$current" ]
174 IFS= read -r stashhash <"$state_dir/stashhash" && [ -n "$stashhash" ]
175 IFS= read -r name <"$state_dir/name" && [ -n "$name" ]
176 IFS= read -r names <"$state_dir/names" && [ -n "$names" ]
177 IFS= read -r processed <"$state_dir/processed" || :
178 IFS= read -r next_no_auto <"$state_dir/no_auto" || :
179 IFS= read -r setautoupdate <"$state_dir/setautoupdate" || :
180 # merging_topfiles is for outside info but not to be restored
181 IFS= read -r mergeours <"$state_dir/mergeours" || :
182 IFS= read -r mergetheirs <"$state_dir/mergetheirs" || :
183 if [ -n "$mergeours" ] && [ -n "$mergetheirs" ]; then
184 headhash="$(git rev-parse --quiet --verify HEAD --)" || :
185 if [ -n "$headhash" ]; then
186 parents="$(git --no-pager log -n 1 --format='format:%P' "$headhash" -- 2>/dev/null)" || :
187 if [ "$parents" = "$mergeours $mergetheirs" ]; then
188 mergeresult="$headhash"
191 if [ -z "$mergeresult" ]; then
192 mergeours=
193 mergetheirs=
196 restored=1
199 clear_state() {
200 ! [ -e "$state_dir" ] || rm -rf "$state_dir" >/dev/null 2>&1 || :
203 restarted=
204 isactive=
205 ! is_active || isactive=1
206 isactiveopt=
207 if [ -z "$isactive" ] && [ $# -eq 1 ]; then
208 case "$1" in --abort|--stop|--continue|--skip) isactiveopt=1; esac
210 if [ -n "$isactive" ] || [ -n "$isactiveopt" ]; then
211 [ $# -eq 1 ] && [ x"$1" != x"--status" ] || { do_status; exit 0; }
212 ensure_work_tree
213 if [ -z "$isactive" ]; then
214 clear_state
215 info "No update is currently active"
216 exit 0
218 case "$1" in
219 --abort)
220 current=
221 stashhash=
222 IFS= read -r current <"$state_dir/current" || :
223 IFS= read -r stashhash <"$state_dir/stashhash" || :
224 clear_state
225 if [ -n "$stashhash" ]; then
226 tg revert -f -q -q --no-stash "$stashhash" >/dev/null 2>&1 || :
228 if [ -n "$current" ]; then
229 info "Ok, update aborted, returning to ${current#refs/heads/}"
230 checkout_symref_full -f "$current"
231 else
232 info "Ok, update aborted. Now, you just need to"
233 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
235 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
236 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
237 exit 0
239 --stop)
240 clear_state
241 info "Ok, update stopped. Now, you just need to"
242 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
243 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
244 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
245 exit 0
247 --continue|--skip)
248 restore_state
249 if [ "$1" = "--skip" ]; then
250 info "Ok, I will try to continue without updating this branch."
251 git reset --hard -q
252 case " $processed " in *" $name "*);;*)
253 processed="${processed:+$processed }$name"
254 esac
256 # assume user fixed it
257 # we could be left on a detached HEAD if we were resolving
258 # a conflict while merging a base in, fix it with a checkout
259 git checkout -q $iowopt "$(strip_ref "$name")"
262 do_status
263 exit 1
264 esac
266 clear_state
268 if [ -z "$restored" ]; then
269 setautoupdate=1
270 [ "$(git config --get --bool topgit.setAutoUpdate 2>/dev/null)" != "false" ] ||
271 setautoupdate=
273 while [ -n "$1" ]; do
274 arg="$1"; shift
275 case "$arg" in
277 usage;;
278 -a|--all)
279 [ -z "$names$pattern" ] || usage 1
280 all=1;;
281 --skip-missing)
282 skipms=1;;
283 --stash)
284 stash=1;;
285 --no-stash)
286 stash=;;
287 --auto|--auto-update|--set-auto|--set-auto-update)
288 setautoupdate=1;;
289 --no-auto|--no-auto-update|--no-set-auto|--no-set-auto-update)
290 setautoupdate=;;
291 --quiet|-q)
292 quiet=1;;
293 --base)
294 basemode=1;;
295 --edit|-e)
296 editmode=1;;
297 --no-edit)
298 editmode=0;;
299 --no-commit)
300 basenc=1;;
301 --force|-f)
302 basefrc=1;;
304 [ $# -gt 0 ] && [ -n "$1" ] || die "option -m requires an argument"
305 basemsg="$1"
306 shift;;
307 -m?*)
308 basemsg="${1#-m}";;
309 --message=*)
310 basemsg="${1#--message=}";;
312 [ $# -gt 0 ] && [ -n "$1" ] || die "option -F requires an argument"
313 basefile="$1"
314 shift;;
315 -F?*)
316 basefile="${1#-F}";;
317 --file=*)
318 basefile="${1#--file=}"
319 [ -n "$basefile" ] || die "option --file= requires an argument"
321 -?*)
322 usage 1;;
324 break;;
328 if [ -z "$all" ]; then
329 namecnt=$(( $namecnt + 1 ))
330 [ "$namecnt" != "1" ] || name1="$arg"
331 [ "$namecnt" != "2" ] || name2="$arg"
332 names="${names:+$names }$arg"
333 else
334 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$arg")"
337 esac
338 done
339 ensure_work_tree
340 while [ $# -gt 0 ]; do
341 if [ -z "$all" ]; then
342 namecnt=$(( $namecnt + 1 ))
343 [ "$namecnt" != "1" ] || name1="$1"
344 [ "$namecnt" != "2" ] || name2="$1"
345 names="${names:+$names }$*"
346 else
347 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$1")"
349 shift
350 done
351 [ -n "$basemode" ] || [ -z "$editmode$basemsg$basefile$basenc$basefrc" ] || usage 1
352 [ -z "$basemode" ] || [ -z "$all$skipms" ] || usage 1
353 [ -z "$basemode" ] || [ -z "$basemsg" ] || [ -z "$basefile" ] || usage 1
354 [ -z "$basemode" ] || [ "$namecnt" -eq 2 ] || usage 1
356 current="$(git symbolic-ref -q HEAD)" || :
357 if [ -n "$current" ]; then
358 [ -n "$(git rev-parse --verify --quiet HEAD --)" ] ||
359 die "cannot return to unborn branch; switch to another branch"
360 else
361 current="$(git rev-parse --verify --quiet HEAD)" ||
362 die "cannot return to invalid HEAD; switch to another branch"
365 [ -z "$basemode" ] || do_base_mode "$name1" "$name2"
367 origpattern="$pattern"
368 [ -z "$pattern" ] && pattern="refs/$topbases"
370 processed=
371 [ -n "$all$names" ] || names="HEAD"
372 if [ -z "$all" ]; then
373 clean_names() {
374 names=
375 while [ $# -gt 0 ]; do
376 name="$(verify_topgit_branch "$1")"
377 case " $names " in *" $name "*);;*)
378 names="${names:+$names }$name"
379 esac
380 shift
381 done
383 clean_names $names
385 ensure_clean_tree
388 save_state() {
389 mkdir -p "$state_dir"
390 printf '%s\n' "$fullcmd" >"$state_dir/fullcmd"
391 printf '%s\n' "$base_remote" >"$state_dir/remote"
392 printf '%s\n' "$skipms" >"$state_dir/skipms"
393 printf '%s\n' "$all" >"$state_dir/all"
394 printf '%s\n' "$current" >"$state_dir/current"
395 printf '%s\n' "$stashhash" >"$state_dir/stashhash"
396 printf '%s\n' "$name" >"$state_dir/name"
397 printf '%s\n' "$names" >"$state_dir/names"
398 printf '%s\n' "$processed" >"$state_dir/processed"
399 printf '%s\n' "$no_auto" >"$state_dir/no_auto"
400 printf '%s\n' "$setautoupdate" >"$state_dir/setautoupdate"
401 # this one is an external flag and needs to be zero length for false
402 printf '%s' "$merging_topfiles" >"$state_dir/merging_topfiles"
403 printf '%s\n' "$1" >"$state_dir/mergeours"
404 printf '%s\n' "$2" >"$state_dir/mergetheirs"
407 stash_now_if_requested() {
408 [ -z "$TG_RECURSIVE" ] || return 0
409 [ -z "$stashhash" ] || return 0
410 ensure_ident_available
411 msg="tgupdate: autostash before update"
412 if [ -n "$all" ]; then
413 msg="$msg --all${origpattern:+ $origpattern}"
414 else
415 msg="$msg $names"
417 set -- $names
418 if [ -n "$stash" ]; then
419 tg tag -q -q -m "$msg" --stash "$@" &&
420 stashhash="$(git rev-parse --quiet --verify refs/tgstash --)" &&
421 [ -n "$stashhash" ] &&
422 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
423 die "requested --stash failed"
424 else
425 tg tag --anonymous "$@" &&
426 stashhash="$(git rev-parse --quiet --verify TG_STASH --)" &&
427 [ -n "$stashhash" ] &&
428 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
429 die "anonymous --stash failed"
431 [ -z "$next_no_auto" ] || no_auto="$next_no_auto"
432 next_no_auto=
435 recursive_update() {
436 _ret=0
437 on_base=
439 if [ -n "$TG_RECURSIVE" ]; then
440 TG_RECURSIVE="==> [$1]${TG_RECURSIVE#==>}"
441 else
442 TG_RECURSIVE="==> [$1]$lf"
444 update_branch "$1"
445 ) || _ret=$?
446 [ $_ret -eq 3 ] && exit 3
447 return $_ret
450 # If HEAD is a symref to "$1" detach it at its current value
451 detach_symref_head_on_branch() {
452 _hsr="$(git symbolic-ref -q HEAD --)" && [ -n "$_hsr" ] || return 0
453 _hrv="$(git rev-parse --quiet --verify HEAD --)" && [ -n "$_hrv" ] ||
454 die "cannot detach_symref_head_on_branch from unborn branch $_hsr"
455 git update-ref --no-deref -m "detaching HEAD from $_hsr to safely update it" HEAD "$_hrv"
458 # git_topmerge will need this even on success and since it might otherwise
459 # be called many times do it just the once here and now
460 repotoplvl="$(git rev-parse --show-toplevel)" && [ -n "$repotoplvl" ] && [ -d "$repotoplvl" ] ||
461 die "git rev-parse --show-toplevel failed"
463 # Run an in-tree recursive merge but make sure we get the desired version of
464 # any .topdeps and .topmsg files. The $auhopt and --no-stat options are
465 # always implicitly in effect. If successful, a new commit is performed on HEAD.
467 # The "git merge-recursive" tool (and others) must be run to get the desired
468 # result. And --no-ff is always implicitly in effect as well.
470 # NOTE: [optional] arguments MUST appear in the order shown
471 # [optional] '-v' varname => optional variable to return original HEAD hash in
472 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
473 # [optional] '--name' <name-for-ours [--name <name-for-theirs>]
474 # $1 => '-m' MUST be '-m'
475 # $2 => commit message
476 # $3 => commit-ish to merge as "theirs"
477 git_topmerge()
479 _ovar=
480 [ "$1" != "-v" ] || [ $# -lt 2 ] || [ -z "$2" ] || { _ovar="$2"; shift 2; }
481 _mmode=
482 case "$1" in --theirs|--remove|--merge) _mmode="${1#--}"; shift; esac
483 _nameours=
484 _nametheirs=
485 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
486 _nameours="$2"
487 shift 2
488 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
489 _nametheirs="$2"
490 shift 2
493 : "${_nameours:=HEAD}"
494 [ "$#" -eq 3 ] && [ "$1" = "-m" ] && [ -n "$2" ] && [ -n "$3" ] ||
495 die "programmer error: invalid arguments to git_topmerge: $*"
496 _ours="$(git rev-parse --verify HEAD^0)" || die "git rev-parse failed"
497 _theirs="$(git rev-parse --verify "$3^0")" || die "git rev-parse failed"
498 [ -z "$_ovar" ] || eval "$_ovar="'"$_ours"'
499 eval "GITHEAD_$_ours="'"$_nameours"' && eval export "GITHEAD_$_ours"
500 if [ -n "$_nametheirs" ]; then
501 eval "GITHEAD_$_theirs="'"$_nametheirs"' && eval export "GITHEAD_$_theirs"
503 _mdriver='touch %A'
504 if [ "$_mmode" = "merge" ]; then
505 TG_L1="$_nameours" && export TG_L1
506 TG_L2="merged common ancestors" && export TG_L2
507 TG_L3="${_nametheirs:-$3}" && export TG_L3
508 _mdriver='git merge-file -L "$TG_L1" -L "$TG_L2" -L "$TG_L3" --marker-size=%L %A %O %B'
510 _msg="$2"
511 _mt=
512 _mb="$(git merge-base --all "$_ours" "$_theirs")" && [ -n "$_mb" ] ||
513 { _mt=1; _mb="$(git hash-object -w -t tree --stdin < /dev/null)"; }
514 # any .topdeps or .topmsg output needs to be stripped from stdout
515 tmpstdout="$tg_tmp_dir/stdout.$$"
516 _ret=0
517 git -c "merge.ours.driver=$_mdriver" merge-recursive \
518 $_mb -- "$_ours" "$_theirs" >"$tmpstdout" || _ret=$?
519 # success or failure is not relevant until after fixing up the
520 # .topdeps and .topmsg files and running rerere unless _ret >= 126
521 [ $_ret -lt 126 ] || return $_ret
522 if [ "$_mmode" = "merge" ]; then
523 cat "$tmpstdout"
524 else
525 case "$_mmode" in
526 theirs) _source="$_theirs";;
527 remove) _source="";;
528 *) _source="$_ours";;
529 esac
530 _newinfo=
531 [ -z "$_source" ] ||
532 _newinfo="$(git cat-file --batch-check="%(objecttype) %(objectname)$tab%(rest)" <<-EOT |
533 $_source:.topdeps .topdeps
534 $_source:.topmsg .topmsg
536 sed -n 's/^blob /100644 /p'
538 [ -z "$_newinfo" ] || _newinfo="$lf$_newinfo"
539 git update-index --index-info <<-EOT ||
540 0 $nullsha$tab.topdeps
541 0 $nullsha$tab.topmsg$_newinfo
543 die "git update-index failed"
544 if [ "$_mmode" = "remove" ] &&
545 { [ -e "$repotoplvl/.topdeps" ] || [ -e "$repotoplvl/.topmsg" ]; }
546 then
547 rm -r -f "$repotoplvl/.topdeps" "$repotoplvl/.topmsg" >/dev/null 2>&1 || :
548 else
549 for zapbad in "$repotoplvl/.topdeps" "$repotoplvl/.topmsg"; do
550 if [ -e "$zapbad" ] && { [ -L "$zapbad" ] || [ ! -f "$zapbad" ]; }; then
551 rm -r -f "$zapbad"
553 done
554 (cd "$repotoplvl" && git checkout-index -q -f -u -- .topdeps .topmsg) ||
555 die "git checkout-index failed"
557 # dump output without any .topdeps or .topmsg messages
558 sed -e '/ \.topdeps/d' -e '/ \.topmsg/d' <"$tmpstdout"
560 # rerere will be a nop unless rerere.enabled is true, but might complete the merge!
561 eval git "${setautoupdate:+-c rerere.autoupdate=1}" rerere || :
562 git ls-files --unmerged --full-name --abbrev :/ >"$tmpstdout" 2>&1 ||
563 die "git ls-files failed"
564 if [ -s "$tmpstdout" ]; then
565 [ "$_ret" != "0" ] || _ret=1
566 else
567 _ret=0
569 if [ $_ret -ne 0 ]; then
570 # merge failed, spit out message, enter "merge" mode and return
572 printf '%s\n\n# Conflicts:\n' "$_msg"
573 sed -n "/$tab/s/^[^$tab]*/#/p" <"$tmpstdout" | sort -u
574 } >"$git_dir/MERGE_MSG"
575 git update-ref MERGE_HEAD "$_theirs" || :
576 echo 'Automatic merge failed; fix conflicts and then commit the result.'
577 rm -f "$tmpstdout"
578 return $_ret
580 # commit time at last!
581 thetree="$(git write-tree)" || die "git write-tree failed"
582 # avoid an extra "already up-to-date" commit (can't happen if _mt though)
583 origtree=
584 [ -n "$_mt" ] || origtree="$(git rev-parse --quiet --verify "$_ours^{tree}" --)" &&
585 [ -n "$origtree" ] || die "git rev-parse failed"
586 if [ "$origtree" != "$thetree" ] || ! contained_by "$_theirs" "$_ours"; then
587 thecommit="$(git commit-tree -p "$_ours" -p "$_theirs" -m "$_msg" "$thetree")" &&
588 [ -n "$thecommit" ] || die "git commit-tree failed"
589 git update-ref -m "$_msg" HEAD "$thecommit" || die "git update-ref failed"
591 # mention how the merge was made
592 echo "Merge made by the 'recursive' strategy."
593 rm -f "$tmpstdout"
594 return 0
597 # run git_topmerge with the passed in arguments (it always does --no-stat)
598 # then return the exit status of git_topmerge
599 # if the returned exit status is no error show a shortstat before
600 # returning assuming the merge was done into the previous HEAD but exclude
601 # .topdeps and .topmsg info from the stat unless doing a --merge
602 # if the first argument is --merge or --theirs or --remove handle .topmsg/.topdeps
603 # as follows:
604 # (default) .topmsg and .topdeps always keep ours
605 # --merge a normal merge takes place
606 # --theirs .topmsg and .topdeps always keep theirs
607 # --remove .topmsg and .topdeps are removed from the result and working tree
608 # note this function should only be called after attempt_index_merge fails as
609 # it implicity always does --no-ff (except for --merge which will --ff)
610 git_merge() {
611 _ret=0
612 git_topmerge -v _oldhead "$@" || _ret=$?
613 _exclusions=
614 [ "$1" = "--merge" ] || _exclusions=":/ :!/.topdeps :!/.topmsg"
615 [ "$_ret" != "0" ] || git --no-pager diff-tree --shortstat "$_oldhead" HEAD^0 -- $_exclusions
616 return $_ret
619 # $1 => .topfile handling ([--]merge, [--]theirs, [--]remove or else do ours)
620 # $2 => current "HEAD"
621 # $3 => proposed fast-forward-to "HEAD"
622 # result is success if fast-forward satisfies $1
623 topff_ok() {
624 case "${1#--}" in
625 merge|theirs)
626 # merge and theirs will always be correct
628 remove)
629 # okay if both blobs are "missing" in $3
630 printf '%s\n' "$3:.topdeps" "$3:.topmsg" |
631 git cat-file --batch-check="%(objectname) %(objecttype)" |
633 read _tdo _tdt &&
634 read _tmo _tmt &&
635 [ "$_tdt" = "missing" ] &&
636 [ "$_tmt" = "missing" ]
637 } || return 1
640 # "ours"
641 # okay if both blobs are the same (same hash or missing)
642 printf '%s\n' "$2:.topdeps" "$2:.topmsg" "$3:.topdeps" "$3:.topmsg" |
643 git cat-file --batch-check="%(objectname) %(objecttype)" |
645 read _td1o _td1t &&
646 read _tm1o _tm1t &&
647 read _td2o _td2t &&
648 read _tm2o _tm2t &&
649 { [ "$_td1t" = "$_td2t" ] &&
650 { [ "$_td1o" = "$_td2o" ] ||
651 [ "$_td1t" = "missing" ]; }; } &&
652 { [ "$_tm1t" = "$_tm2t" ] &&
653 { [ "$_tm1o" = "$_tm2o" ] ||
654 [ "$_tm1t" = "missing" ]; }; }
655 } || return 1
657 esac
658 return 0
661 # similar to git_merge but operates exclusively using a separate index and temp dir
662 # only trivial aggressive automatic (i.e. simple) merges are supported
664 # [optional] '--no-auto' to suppress "automatic" merging, merge fails instead
665 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
666 # $1 => '' to discard result, 'refs/?*' to update the specified ref or a varname
667 # $2 => '-m' MUST be '-m'
668 # $3 => commit message AND, if $1 matches refs/?* the update-ref message
669 # $4 => commit-ish to merge as "ours"
670 # $5 => commit-ish to merge as "theirs"
671 # [$6...] => more commit-ishes to merge as "theirs" in octopus
673 # all merging is done in a separate index (or temporary files for simple merges)
674 # if successful the ref or var is updated with the result
675 # otherwise everything is left unchanged and a silent failure occurs
676 # if successful and $1 matches refs/?* it WILL BE UPDATED to a new commit using the
677 # message and appropriate parents AND HEAD WILL BE DETACHED first if it's a symref
678 # to the same ref
679 # otherwise if $1 does not match refs/?* and is not empty the named variable will
680 # be set to contain the resulting commit from the merge
681 # the working tree and index ARE LEFT COMPLETELY UNTOUCHED no matter what
682 v_attempt_index_merge() {
683 _noauto=
684 if [ "$1" = "--no-auto" ]; then
685 _noauto=1
686 shift
688 _exclusions=
689 [ "$1" = "--merge" ] || _exclusions=":/ :!/.topdeps :!/.topmsg"
690 _mstyle=
691 if [ "$1" = "--merge" ] || [ "$1" = "--theirs" ] || [ "$1" = "--remove" ]; then
692 _mmode="${1#--}"
693 shift
694 if [ "$_mmode" = "merge" ] || [ "$_mmode" = "theirs" ]; then
695 _mstyle="-top$_mmode"
698 [ "$#" -ge 5 ] && [ "$2" = "-m" ] && [ -n "$3" ] && [ -n "$4" ] && [ -n "$5" ] ||
699 die "programmer error: invalid arguments to v_attempt_index_merge: $*"
700 _var="$1"
701 _msg="$3"
702 _head="$4"
703 shift 4
704 rh="$(git rev-parse --quiet --verify "$_head^0" --)" && [ -n "$rh" ] || return 1
705 orh="$rh"
706 oth=
707 _mmsg=
708 newc=
709 _nodt=
710 _same=
711 _mt=
712 _octo=
713 if [ $# -gt 1 ]; then
714 if [ "$_mmode" = "merge" ] || [ "$_mmode" = "theirs" ]; then
715 die "programmer error: invalid octopus .topfile strategy to v_attempt_index_merge: --$_mode"
717 ihl="$(git merge-base --independent "$@")" || return 1
718 set -- $ihl
719 [ $# -ge 1 ] && [ -n "$1" ] || return 1
721 [ $# -eq 1 ] || _octo=1
722 mb="$(git merge-base ${_octo:+--octopus} "$rh" "$@")" && [ -n "$mb" ] || {
723 mb="$(git hash-object -w -t tree --stdin < /dev/null)"
724 _mt=1
726 if [ -z "$_mt" ]; then
727 if [ -n "$_octo" ]; then
728 while [ $# -gt 1 ] && mbh="$(git merge-base "$rh" "$1")" && [ -n "$mbh" ]; do
729 if [ "$rh" = "$mbh" ]; then
730 if topff_ok "$_mmode" "$rh" "$1"; then
731 _mmsg="Fast-forward (no commit created)"
732 rh="$1"
733 shift
734 else
735 break
737 elif [ "$1" = "$mbh" ]; then
738 shift
739 else
740 break;
742 done
743 if [ $# -eq 1 ]; then
744 _octo=
745 mb="$(git merge-base "$rh" "$1")" && [ -n "$mb" ] || return 1
748 if [ -z "$_octo" ]; then
749 r1="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$r1" ] || return 1
750 oth="$r1"
751 set -- "$r1"
752 if [ "$rh" = "$mb" ]; then
753 if topff_ok "$_mmode" "$rh" "$r1"; then
754 _mmsg="Fast-forward (no commit created)"
755 newc="$r1"
756 _nodt=1
757 _mstyle=
759 elif [ "$r1" = "$mb" ]; then
760 [ -n "$_mmsg" ] || _mmsg="Already up-to-date!"
761 newc="$rh"
762 _nodt=1
763 _same=1
764 _mstyle=
768 if [ -z "$newc" ]; then
769 if [ "$_mmode" = "theirs" ] && [ -z "$oth" ]; then
770 oth="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$oth" ] || return 1
771 set -- "$oth"
773 inew="$tg_tmp_dir/index.$$"
774 ! [ -e "$inew" ] || rm -f "$inew"
775 itmp="$tg_tmp_dir/output.$$"
776 imrg="$tg_tmp_dir/auto.$$"
777 [ -z "$_octo" ] || >"$imrg"
778 _auto=
779 _parents=
780 _newrh="$rh"
781 while :; do
782 if [ -n "$_parents" ]; then
783 if contained_by "$1" "$_newrh"; then
784 shift
785 continue
788 GIT_INDEX_FILE="$inew" git read-tree -m --aggressive -i "$mb" "$rh" "$1" || { rm -f "$inew" "$imrg"; return 1; }
789 GIT_INDEX_FILE="$inew" git ls-files --unmerged --full-name --abbrev :/ >"$itmp" 2>&1 || { rm -f "$inew" "$itmp" "$imrg"; return 1; }
790 ! [ -s "$itmp" ] || {
791 if ! GIT_INDEX_FILE="$inew" TG_TMP_DIR="$tg_tmp_dir" git merge-index -q "$TG_INST_CMDDIR/tg--index-merge-one-file$_mstyle" -a >"$itmp" 2>&1; then
792 rm -f "$inew" "$itmp" "$imrg"
793 return 1
795 if [ -s "$itmp" ]; then
796 if [ -n "$_noauto" ]; then
797 rm -f "$inew" "$itmp" "$imrg"
798 return 1
800 if [ -n "$_octo" ]; then
801 cat "$itmp" >>"$imrg"
802 else
803 cat "$itmp"
805 _auto=" automatic"
808 _mstyle=
809 rm -f "$itmp"
810 _parents="${_parents:+$_parents }-p $1"
811 if [ $# -gt 1 ]; then
812 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
813 rh="$newt"
814 shift
815 continue
817 break;
818 done
819 if [ "$_mmode" != "merge" ]; then
820 case "$_mmode" in
821 theirs) _source="$oth";;
822 remove) _source="";;
823 *) _source="$orh";;
824 esac
825 _newinfo=
826 [ -z "$_source" ] ||
827 _newinfo="$(git cat-file --batch-check="%(objecttype) %(objectname)$tab%(rest)" <<-EOT |
828 $_source:.topdeps .topdeps
829 $_source:.topmsg .topmsg
831 sed -n 's/^blob /100644 /p'
833 [ -z "$_newinfo" ] || _newinfo="$lf$_newinfo"
834 GIT_INDEX_FILE="$inew" git update-index --index-info <<-EOT || { rm -f "$inew" "$imrg"; return 1; }
835 0 $nullsha$tab.topdeps
836 0 $nullsha$tab.topmsg$_newinfo
839 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
840 [ -z "$_octo" ] || sort -u <"$imrg"
841 rm -f "$inew" "$imrg"
842 newc="$(git commit-tree -p "$orh" $_parents -m "$_msg" "$newt")" && [ -n "$newc" ] || return 1
843 _mmsg="Merge made by the 'trivial aggressive$_auto${_octo:+ octopus}' strategy."
845 case "$_var" in
846 refs/?*)
847 if [ -n "$_same" ]; then
848 _same=
849 if rv="$(git rev-parse --quiet --verify "$_var" --)" && [ "$rv" = "$newc" ]; then
850 _same=1
853 if [ -z "$_same" ]; then
854 detach_symref_head_on_branch "$_var" || return 1
855 # git update-ref returns 0 even on failure :(
856 git update-ref -m "$_msg" "$_var" "$newc" || return 1
860 eval "$_var="'"$newc"'
862 esac
863 echo "$_mmsg"
864 [ -n "$_nodt" ] || git --no-pager diff-tree --shortstat "$orh" "$newc" -- $_exclusions
865 return 0
868 # shortcut that passes $3 as a preceding argument (which must match refs/?*)
869 attempt_index_merge() {
870 _noauto=
871 _mmode=
872 if [ "$1" = "--no-auto" ]; then
873 _noauto="$1"
874 shift
876 if [ "$1" = "--merge" ] || [ "$1" = "--theirs" ] || [ "$1" = "--remove" ]; then
877 _mmode="$1"
878 shift
880 case "$3" in refs/?*);;*)
881 die "programmer error: invalid arguments to attempt_index_merge: $*"
882 esac
883 v_attempt_index_merge $_noauto $_mmode "$3" "$@"
886 on_base=
887 do_base_switch() {
888 [ -n "$1" ] || return 0
890 [ "$1" != "$on_base" ] ||
891 [ "$(git symbolic-ref -q HEAD)" != "refs/$topbases/$1" ]
892 then
893 switch_to_base "$1"
894 on_base="$1"
898 update_branch_internal() {
899 # We are cacheable until the first change
900 become_cacheable
902 _update_name="$1"
903 ## First, take care of our base
905 _depcheck="$(get_temp tg-depcheck)"
906 missing_deps=
907 needs_update "$_update_name" >"$_depcheck" || :
908 if [ -n "$missing_deps" ]; then
909 msg="Some dependencies are missing: $missing_deps"
910 if [ -n "$skipms" ]; then
911 info "$msg; skipping"
912 elif [ -z "$all" ]; then
913 die "$msg"
914 else
915 info "$msg; skipping branch $_update_name"
916 return 0
919 # allow automatic simple merges by default until a failure occurs
920 no_auto=
921 if [ -s "$_depcheck" ]; then
922 <"$_depcheck" \
923 sed 's/ [^ ]* *$//' | # last is $_update_name
924 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
925 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
926 >"$_depcheck.ideps" \
927 uniq -s 1 # fold branch lines; + always comes before - and thus wins within uniq
929 stash_now_if_requested
931 while read -r depline; do
932 dep="${depline#?}"
933 action="${depline%$dep}"
935 # We do not distinguish between dependencies out-of-date
936 # and base/remote out-of-date cases for $dep here,
937 # but thanks to needs_update returning : or refs/remotes/<remote>/<name>
938 # for the latter, we do correctly recurse here
939 # in both cases.
941 if [ x"$action" = x+ ]; then
942 case " $missing_deps " in *" $dep "*)
943 info "Skipping recursing to missing dependency: $dep"
944 continue
945 esac
946 info "Recursing to $dep..."
947 recursive_update "$dep" || exit 3
949 done <"$_depcheck.ideps"
951 # Create a list of all the fully qualified ref names that need
952 # to be merged into $_update_name's base. This will be done
953 # as an octopus merge if there are no conflicts.
954 deplist=
955 deplines=
956 set --
957 while read -r dep; do
958 dep="${dep#?}"
959 case "$dep" in
960 "refs"/*)
961 set -- "$@" "$dep"
962 case "$dep" in
963 "refs/heads"/*)
964 d="${dep#refs/heads/}"
965 deplist="${deplist:+$deplist }$d"
966 deplines="$deplines$d$lf"
969 d="${dep#refs/}"
970 deplist="${deplist:+$deplist }$d"
971 deplines="$deplines$d$lf"
973 esac
976 set -- "$@" "refs/heads/$dep"
977 deplist="${deplist:+$deplist }$dep"
978 deplines="$deplines$dep$lf"
980 esac
981 done <"$_depcheck.ideps"
983 # Make sure we end up on the correct base branch
984 on_base=
985 if [ $# -ge 2 ]; then
986 info "Updating $_update_name base with deps: $deplist"
987 become_non_cacheable
988 msg="tgupdate: octopus merge $# deps into $_update_name base$lf$lf$deplines"
989 if attempt_index_merge --remove -m "$msg" "refs/$topbases/$_update_name" "$@"; then
990 set --
991 else
992 info "Octopus merge failed; falling back to multiple 3-way merges"
993 no_auto="--no-auto"
997 for fulldep in "$@"; do
998 # This will be either a proper topic branch
999 # or a remote base. (branch_needs_update() is called
1000 # only on the _dependencies_, not our branch itself!)
1002 case "$fulldep" in
1003 "refs/heads"/*)
1004 dep="${fulldep#refs/heads/}";;
1005 "refs"/*)
1006 dep="${fulldep#refs/}";;
1008 dep="$fulldep";; # this should be a programmer error
1009 esac
1011 info "Updating $_update_name base with $dep changes..."
1012 become_non_cacheable
1013 msg="tgupdate: merge $dep into $_update_name base"
1015 ! attempt_index_merge $no_auto --remove -m "$msg" "refs/$topbases/$_update_name" "$fulldep^0" &&
1017 # We need to switch to the base branch
1018 # ...but only if we aren't there yet (from failed previous merge)
1019 do_base_switch "$_update_name" || die "do_base_switch failed" &&
1020 git_merge --remove --name "$_update_name base" --name "$dep" -m "$msg" "$fulldep^0"
1022 then
1023 rm "$_depcheck"
1024 save_state
1025 unset TG_RECURSIVE
1026 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1027 info "(use \`$tgdisplayac status\` to see more options)"
1028 exit 3
1030 done
1031 else
1032 info "The base is up-to-date."
1036 ## Second, update our head with the remote branch
1038 plusextra=
1039 merge_with="refs/$topbases/$_update_name"
1040 brmmode=
1041 if has_remote "$_update_name"; then
1042 _rname="refs/remotes/$base_remote/$_update_name"
1043 if branch_contains "refs/heads/$_update_name" "$_rname"; then
1044 info "The $_update_name head is up-to-date wrt. its remote branch."
1045 else
1046 stash_now_if_requested
1047 info "Reconciling $_update_name base with remote branch updates..."
1048 become_non_cacheable
1049 msg="tgupdate: merge ${_rname#refs/} onto $_update_name base"
1050 checkours=
1051 checktheirs=
1052 got_merge_with=
1053 brmmode="--merge"
1054 if [ -n "$mergeresult" ]; then
1055 checkours="$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" || :
1056 checktheirs="$(git rev-parse --verify --quiet "$_rname^0" --)" || :
1057 if [ "$mergeours" = "$checkours" ] && [ "$mergetheirs" = "$checktheirs" ]; then
1058 got_merge_with="$mergeresult"
1062 [ -z "$got_merge_with" ] &&
1063 ! v_attempt_index_merge $no_auto --theirs "merge_with" -m "$msg" "refs/$topbases/$_update_name" "$_rname^0" &&
1065 # *DETACH* our HEAD now!
1066 no_auto="--no-auto"
1067 git checkout -q --detach $iowopt "refs/$topbases/$_update_name" || die "git checkout failed" &&
1068 git_merge --theirs --name "$_update_name base content" --name "${_rname#refs/}" -m "$msg" "$_rname^0" &&
1069 merge_with="$(git rev-parse --verify HEAD --)"
1071 then
1072 save_state \
1073 "$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" \
1074 "$(git rev-parse --verify --quiet "$_rname^0" --)"
1075 unset TG_RECURSIVE
1076 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1077 info "(use \`$tgdisplayac status\` to see more options)"
1078 exit 3
1080 # Go back but remember we want to merge with this, not base
1081 [ -z "$got_merge_with" ] || merge_with="$got_merge_with"
1082 plusextra="${_rname#refs/} + "
1087 ## Third, update our head with the base
1089 if branch_contains "refs/heads/$_update_name" "$merge_with"; then
1090 info "The $_update_name head is up-to-date wrt. the base."
1091 return 0
1093 stash_now_if_requested
1094 info "Updating $_update_name against ${plusextra}new base..."
1095 become_non_cacheable
1096 msg="tgupdate: merge ${plusextra}$_update_name base into $_update_name"
1097 b4deps=
1098 if [ -n "$brmmode" ] && [ "$base_remote" ]; then
1099 b4deps="$(git rev-parse --verify --quiet "refs/heads/$_update_name:.topdeps" --)" && [ -n "$b4deps" ] ||
1100 b4deps="$(git hash-object -t blob -w --stdin </dev/null)"
1103 ! attempt_index_merge $no_auto $brmmode -m "$msg" "refs/heads/$_update_name" "$merge_with^0" &&
1105 # Home, sweet home...
1106 # (We want to always switch back, in case we were
1107 # on the base from failed previous merge.)
1108 git checkout -q $iowopt "$_update_name" || die "git checkout failed" &&
1109 git_merge $brmmode --name "$_update_name" --name "${plusextra}$topbases/$_update_name" -m "$msg" "$merge_with^0"
1111 then
1112 no_auto=
1113 merging_topfiles="${brmmode:+1}"
1114 save_state
1115 unset TG_RECURSIVE
1116 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1117 info "(use \`$tgdisplayac status\` to see more options)"
1118 exit 3
1121 # Fourth, auto create locally any newly depended on branches we got from the remote
1123 _result=0
1124 if [ -n "$b4deps" ] &&
1125 l8rdeps="$(git rev-parse --verify --quiet "refs/heads/$_update_name:.topdeps" --)" &&
1126 [ -n "$l8rdeps" ] && [ "$b4deps" != "$l8rdeps" ]
1127 then
1128 _olddeps=
1129 while read -r newdep; do
1130 if [ -n "$newdep" ]; then
1131 if auto_create_local_remote "$newdep"; then
1132 _result=75
1133 else
1134 if ref_exists "refs/heads/$newdep"; then
1135 # maybe the line just moved around
1136 [ -n "$_olddeps" ] && [ -f "$_olddeps" ] || {
1137 _olddeps="$(get_temp b4deps)" &&
1138 git cat-file blob "$b4deps" >"$_olddeps"
1140 if awk -v "newdep=$newdep" '$0 == newdep {exit 1}' <"$_olddeps"; then
1141 # nope, it's a new head already existing locally
1142 _result=75
1144 else
1145 # helpfully check to see if there's such a remote branch
1146 _rntgb=
1147 ! ref_exists "refs/remotes/$base_remote/$newdep" || _rntgb=1
1148 # maybe a locking local orphan base too
1149 _blocked=
1150 if [ -n "$_rntgb" ] &&
1151 ref_exists "refs/remotes/$base_remote/${topbases#heads/}$newdep" &&
1152 ref_exists "refs/$topbases/$newdep"
1153 then
1154 _blocked=1
1156 # spew the flexibly adjustable warning
1157 warn "-------------------------------------"
1158 warn "MISSING DEPENDENCY MERGED FROM REMOTE"
1159 warn "-------------------------------------"
1160 warn "Local Branch: $_update_name"
1161 warn " Remote Name: $base_remote"
1162 warn " Dependency: $newdep"
1163 if [ -n "$_blocked" ]; then
1164 warn "Blocking Ref: refs/$topbases/$newdep"
1165 elif [ -n "$_rntgb" ]; then
1166 warn "Existing Ref: refs/remotes/$base_remote/$newdep"
1168 warn ""
1169 if [ -n "$_blocked" ]; then
1170 warn "There is no local branch by that name, but"
1171 warn "there IS a remote TopGit branch available by"
1172 warn "that name, but creation of a local version has"
1173 warn "been blocked by existence of the ref shown above."
1174 elif [ -n "$_rntgb" ]; then
1175 warn "There is no local branch or remote TopGit"
1176 warn "branch available by that name, but there is an"
1177 warn "existing non-TopGit remote branch ref shown above."
1178 warn "Non-TopGit branches are not set up automatically"
1179 warn "by TopGit and must be maintained manually."
1180 else
1181 warn "There is no local branch or remote branch"
1182 warn "(TopGit or otherwise) available by that name."
1184 warn "-------------------------------------"
1188 done <<-EOT
1189 $(git diff --ignore-space-at-eol "$b4deps" "$l8rdeps" -- | diff_added_lines)
1192 return $_result
1195 update_branch() {
1196 _ubicode=0
1197 _maxdeploop=3
1198 update_branch_internal "$@" || _ubicode=$?
1199 while [ "$_maxdeploop" -gt 0 ] && [ "$_ubicode" = "75" ]; do
1200 _maxdeploop="$(( $maxdeploop - 1 ))"
1201 info "Updating $1 again with newly added dependencies..."
1202 _ubicode=0
1203 update_branch_internal "$@" || _ubicode=$?
1204 done
1205 return $_ubicode
1208 # We are "read-only" and cacheable until the first change
1209 tg_read_only=1
1210 v_create_ref_cache
1212 do_non_annihilated_branches_patterns() {
1213 while read -r _pat && [ -n "$_pat" ]; do
1214 set -- "$@" "$_pat"
1215 done
1216 non_annihilated_branches "$@"
1219 do_non_annihilated_branches() {
1220 if [ -z "$pattern" ]; then
1221 non_annihilated_branches
1222 else
1223 do_non_annihilated_branches_patterns <<-EOT
1224 $(sed 'y/ /\n/' <<-LIST
1225 $pattern
1226 LIST
1232 if [ -n "$all" ] && [ -z "$restored" ]; then
1233 names=
1234 while read name && [ -n "$name" ]; do
1235 case " $names " in *" $name "*);;*)
1236 names="${names:+$names }$name"
1237 esac
1238 done <<-EOT
1239 $(do_non_annihilated_branches)
1243 for name in $names; do
1244 case " $processed " in *" $name "*) continue; esac
1245 [ -z "$all" ] && case "$names" in *" "*) ! :; esac || info "Proccessing $name..."
1246 update_branch "$name" || exit
1247 processed="${processed:+$processed }$name"
1248 done
1250 [ -z "$all" ] && case "$names" in *" "*) ! :; esac ||
1251 info "Returning to ${current#refs/heads/}..."
1252 checkout_symref_full "$current"
1253 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
1254 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :