tg.sh: allow --top-bases to work for remotes
[topgit/pro.git] / tg-update.sh
blob2458ab771552a5b24cb0e60480a06a6c863b1412
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 | wc -l) ))"
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 if [ -z "$isactive" ]; then
213 clear_state
214 info "No update is currently active"
215 exit 0
217 case "$1" in
218 --abort)
219 current=
220 stashhash=
221 IFS= read -r current <"$state_dir/current" || :
222 IFS= read -r stashhash <"$state_dir/stashhash" || :
223 clear_state
224 if [ -n "$stashhash" ]; then
225 tg revert -f -q -q --no-stash "$stashhash" >/dev/null 2>&1 || :
227 if [ -n "$current" ]; then
228 info "Ok, update aborted, returning to ${current#refs/heads/}"
229 checkout_symref_full -f "$current"
230 else
231 info "Ok, update aborted. Now, you just need to"
232 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
234 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
235 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
236 exit 0
238 --stop)
239 clear_state
240 info "Ok, update stopped. Now, you just need to"
241 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
242 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
243 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
244 exit 0
246 --continue|--skip)
247 restore_state
248 if [ "$1" = "--skip" ]; then
249 info "Ok, I will try to continue without updating this branch."
250 git reset --hard -q
251 case " $processed " in *" $name "*);;*)
252 processed="${processed:+$processed }$name"
253 esac
255 # assume user fixed it
256 # we could be left on a detached HEAD if we were resolving
257 # a conflict while merging a base in, fix it with a checkout
258 git checkout -q $iowopt "$(strip_ref "$name")"
261 do_status
262 exit 1
263 esac
265 clear_state
267 if [ -z "$restored" ]; then
268 setautoupdate=1
269 [ "$(git config --get --bool topgit.setAutoUpdate 2>/dev/null)" != "false" ] ||
270 setautoupdate=
272 while [ -n "$1" ]; do
273 arg="$1"; shift
274 case "$arg" in
276 usage;;
277 -a|--all)
278 [ -z "$names$pattern" ] || usage 1
279 all=1;;
280 --skip-missing)
281 skipms=1;;
282 --stash)
283 stash=1;;
284 --no-stash)
285 stash=;;
286 --auto|--auto-update|--set-auto|--set-auto-update)
287 setautoupdate=1;;
288 --no-auto|--no-auto-update|--no-set-auto|--no-set-auto-update)
289 setautoupdate=;;
290 --quiet|-q)
291 quiet=1;;
292 --base)
293 basemode=1;;
294 --edit|-e)
295 editmode=1;;
296 --no-edit)
297 editmode=0;;
298 --no-commit)
299 basenc=1;;
300 --force|-f)
301 basefrc=1;;
303 [ $# -gt 0 ] && [ -n "$1" ] || die "option -m requires an argument"
304 basemsg="$1"
305 shift;;
306 -m?*)
307 basemsg="${1#-m}";;
308 --message=*)
309 basemsg="${1#--message=}";;
311 [ $# -gt 0 ] && [ -n "$1" ] || die "option -F requires an argument"
312 basefile="$1"
313 shift;;
314 -F?*)
315 basefile="${1#-F}";;
316 --file=*)
317 basefile="${1#--file=}"
318 [ -n "$basefile" ] || die "option --file= requires an argument"
320 -?*)
321 usage 1;;
323 break;;
327 if [ -z "$all" ]; then
328 namecnt=$(( $namecnt + 1 ))
329 [ "$namecnt" != "1" ] || name1="$arg"
330 [ "$namecnt" != "2" ] || name2="$arg"
331 names="${names:+$names }$arg"
332 else
333 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$arg")"
336 esac
337 done
338 while [ $# -gt 0 ]; do
339 if [ -z "$all" ]; then
340 namecnt=$(( $namecnt + 1 ))
341 [ "$namecnt" != "1" ] || name1="$1"
342 [ "$namecnt" != "2" ] || name2="$1"
343 names="${names:+$names }$*"
344 else
345 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$1")"
347 shift
348 done
349 [ -n "$basemode" ] || [ -z "$editmode$basemsg$basefile$basenc$basefrc" ] || usage 1
350 [ -z "$basemode" ] || [ -z "$all$skipms" ] || usage 1
351 [ -z "$basemode" ] || [ -z "$basemsg" ] || [ -z "$basefile" ] || usage 1
352 [ -z "$basemode" ] || [ "$namecnt" -eq 2 ] || usage 1
354 current="$(git symbolic-ref -q HEAD)" || :
355 if [ -n "$current" ]; then
356 [ -n "$(git rev-parse --verify --quiet HEAD --)" ] ||
357 die "cannot return to unborn branch; switch to another branch"
358 else
359 current="$(git rev-parse --verify --quiet HEAD)" ||
360 die "cannot return to invalid HEAD; switch to another branch"
363 [ -z "$basemode" ] || do_base_mode "$name1" "$name2"
365 origpattern="$pattern"
366 [ -z "$pattern" ] && pattern="refs/$topbases"
368 processed=
369 [ -n "$all$names" ] || names="HEAD"
370 if [ -z "$all" ]; then
371 clean_names() {
372 names=
373 while [ $# -gt 0 ]; do
374 name="$(verify_topgit_branch "$1")"
375 case " $names " in *" $name "*);;*)
376 names="${names:+$names }$name"
377 esac
378 shift
379 done
381 clean_names $names
383 ensure_clean_tree
386 save_state() {
387 mkdir -p "$state_dir"
388 printf '%s\n' "$fullcmd" >"$state_dir/fullcmd"
389 printf '%s\n' "$base_remote" >"$state_dir/remote"
390 printf '%s\n' "$skipms" >"$state_dir/skipms"
391 printf '%s\n' "$all" >"$state_dir/all"
392 printf '%s\n' "$current" >"$state_dir/current"
393 printf '%s\n' "$stashhash" >"$state_dir/stashhash"
394 printf '%s\n' "$name" >"$state_dir/name"
395 printf '%s\n' "$names" >"$state_dir/names"
396 printf '%s\n' "$processed" >"$state_dir/processed"
397 printf '%s\n' "$no_auto" >"$state_dir/no_auto"
398 printf '%s\n' "$setautoupdate" >"$state_dir/setautoupdate"
399 # this one is an external flag and needs to be zero length for false
400 printf '%s' "$merging_topfiles" >"$state_dir/merging_topfiles"
401 printf '%s\n' "$1" >"$state_dir/mergeours"
402 printf '%s\n' "$2" >"$state_dir/mergetheirs"
405 stash_now_if_requested() {
406 [ -z "$TG_RECURSIVE" ] || return 0
407 [ -z "$stashhash" ] || return 0
408 ensure_ident_available
409 msg="tgupdate: autostash before update"
410 if [ -n "$all" ]; then
411 msg="$msg --all${origpattern:+ $origpattern}"
412 else
413 msg="$msg $names"
415 set -- $names
416 if [ -n "$stash" ]; then
417 tg tag -q -q -m "$msg" --stash "$@" &&
418 stashhash="$(git rev-parse --quiet --verify refs/tgstash --)" &&
419 [ -n "$stashhash" ] &&
420 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
421 die "requested --stash failed"
422 else
423 tg tag --anonymous "$@" &&
424 stashhash="$(git rev-parse --quiet --verify TG_STASH --)" &&
425 [ -n "$stashhash" ] &&
426 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
427 die "anonymous --stash failed"
429 [ -z "$next_no_auto" ] || no_auto="$next_no_auto"
430 next_no_auto=
433 recursive_update() {
434 _ret=0
435 on_base=
437 if [ -n "$TG_RECURSIVE" ]; then
438 TG_RECURSIVE="==> [$1]${TG_RECURSIVE#==>}"
439 else
440 TG_RECURSIVE="==> [$1]$lf"
442 update_branch "$1"
443 ) || _ret=$?
444 [ $_ret -eq 3 ] && exit 3
445 return $_ret
448 # If HEAD is a symref to "$1" detach it at its current value
449 detach_symref_head_on_branch() {
450 _hsr="$(git symbolic-ref -q HEAD --)" && [ -n "$_hsr" ] || return 0
451 _hrv="$(git rev-parse --quiet --verify HEAD --)" && [ -n "$_hrv" ] ||
452 die "cannot detach_symref_head_on_branch from unborn branch $_hsr"
453 git update-ref --no-deref -m "detaching HEAD from $_hsr to safely update it" HEAD "$_hrv"
456 # git_topmerge will need this even on success and since it might otherwise
457 # be called many times do it just the once here and now
458 repotoplvl="$(git rev-parse --show-toplevel)" && [ -n "$repotoplvl" ] && [ -d "$repotoplvl" ] ||
459 die "git rev-parse --show-toplevel failed"
461 # Run an in-tree recursive merge but make sure we get the desired version of
462 # any .topdeps and .topmsg files. The $auhopt and --no-stat options are
463 # always implicitly in effect. If successful, a new commit is performed on HEAD.
465 # The "git merge-recursive" tool (and others) must be run to get the desired
466 # result. And --no-ff is always implicitly in effect as well.
468 # NOTE: [optional] arguments MUST appear in the order shown
469 # [optional] '-v' varname => optional variable to return original HEAD hash in
470 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
471 # [optional] '--name' <name-for-ours [--name <name-for-theirs>]
472 # $1 => '-m' MUST be '-m'
473 # $2 => commit message
474 # $3 => commit-ish to merge as "theirs"
475 git_topmerge()
477 _ovar=
478 [ "$1" != "-v" ] || [ $# -lt 2 ] || [ -z "$2" ] || { _ovar="$2"; shift 2; }
479 _mmode=
480 case "$1" in --theirs|--remove|--merge) _mmode="${1#--}"; shift; esac
481 _nameours=
482 _nametheirs=
483 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
484 _nameours="$2"
485 shift 2
486 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
487 _nametheirs="$2"
488 shift 2
491 : "${_nameours:=HEAD}"
492 [ "$#" -eq 3 ] && [ "$1" = "-m" ] && [ -n "$2" ] && [ -n "$3" ] ||
493 die "programmer error: invalid arguments to git_topmerge: $*"
494 _ours="$(git rev-parse --verify HEAD^0)" || die "git rev-parse failed"
495 _theirs="$(git rev-parse --verify "$3^0")" || die "git rev-parse failed"
496 [ -z "$_ovar" ] || eval "$_ovar="'"$_ours"'
497 eval "GITHEAD_$_ours="'"$_nameours"' && eval export "GITHEAD_$_ours"
498 if [ -n "$_nametheirs" ]; then
499 eval "GITHEAD_$_theirs="'"$_nametheirs"' && eval export "GITHEAD_$_theirs"
501 _mdriver='touch %A'
502 if [ "$_mmode" = "merge" ]; then
503 TG_L1="$_nameours" && export TG_L1
504 TG_L2="merged common ancestors" && export TG_L2
505 TG_L3="${_nametheirs:-$3}" && export TG_L3
506 _mdriver='git merge-file -L "$TG_L1" -L "$TG_L2" -L "$TG_L3" --marker-size=%L %A %O %B'
508 _msg="$2"
509 _mt=
510 _mb="$(git merge-base --all "$_ours" "$_theirs")" && [ -n "$_mb" ] ||
511 { _mt=1; _mb="$(git hash-object -w -t tree --stdin < /dev/null)"; }
512 # any .topdeps or .topmsg output needs to be stripped from stdout
513 tmpstdout="$tg_tmp_dir/stdout.$$"
514 _ret=0
515 git -c "merge.ours.driver=$_mdriver" merge-recursive \
516 $_mb -- "$_ours" "$_theirs" >"$tmpstdout" || _ret=$?
517 # success or failure is not relevant until after fixing up the
518 # .topdeps and .topmsg files and running rerere unless _ret >= 126
519 [ $_ret -lt 126 ] || return $_ret
520 if [ "$_mmode" = "merge" ]; then
521 cat "$tmpstdout"
522 else
523 case "$_mmode" in
524 theirs) _source="$_theirs";;
525 remove) _source="";;
526 *) _source="$_ours";;
527 esac
528 _newinfo=
529 [ -z "$_source" ] ||
530 _newinfo="$(git cat-file --batch-check="%(objecttype) %(objectname)$tab%(rest)" <<-EOT |
531 $_source:.topdeps .topdeps
532 $_source:.topmsg .topmsg
534 sed -n 's/^blob /100644 /p'
536 [ -z "$_newinfo" ] || _newinfo="$lf$_newinfo"
537 git update-index --index-info <<-EOT ||
538 0 $nullsha$tab.topdeps
539 0 $nullsha$tab.topmsg$_newinfo
541 die "git update-index failed"
542 if [ "$_mmode" = "remove" ] &&
543 { [ -e "$repotoplvl/.topdeps" ] || [ -e "$repotoplvl/.topmsg" ]; }
544 then
545 rm -r -f "$repotoplvl/.topdeps" "$repotoplvl/.topmsg" >/dev/null 2>&1 || :
546 else
547 for zapbad in "$repotoplvl/.topdeps" "$repotoplvl/.topmsg"; do
548 if [ -e "$zapbad" ] && { [ -L "$zapbad" ] || [ ! -f "$zapbad" ]; }; then
549 rm -r -f "$zapbad"
551 done
552 (cd "$repotoplvl" && git checkout-index -q -f -u -- .topdeps .topmsg) ||
553 die "git checkout-index failed"
555 # dump output without any .topdeps or .topmsg messages
556 sed -e '/ \.topdeps/d' -e '/ \.topmsg/d' <"$tmpstdout"
558 # rerere will be a nop unless rerere.enabled is true, but might complete the merge!
559 eval git "${setautoupdate:+-c rerere.autoupdate=1}" rerere || :
560 git ls-files --unmerged --full-name --abbrev :/ >"$tmpstdout" 2>&1 ||
561 die "git ls-files failed"
562 if [ -s "$tmpstdout" ]; then
563 [ "$_ret" != "0" ] || _ret=1
564 else
565 _ret=0
567 if [ $_ret -ne 0 ]; then
568 # merge failed, spit out message, enter "merge" mode and return
570 printf '%s\n\n# Conflicts:\n' "$_msg"
571 sed -n "/$tab/s/^[^$tab]*/#/p" <"$tmpstdout" | sort -u
572 } >"$git_dir/MERGE_MSG"
573 git update-ref MERGE_HEAD "$_theirs" || :
574 echo 'Automatic merge failed; fix conflicts and then commit the result.'
575 rm -f "$tmpstdout"
576 return $_ret
578 # commit time at last!
579 thetree="$(git write-tree)" || die "git write-tree failed"
580 # avoid an extra "already up-to-date" commit (can't happen if _mt though)
581 origtree=
582 [ -n "$_mt" ] || origtree="$(git rev-parse --quiet --verify "$_ours^{tree}" --)" &&
583 [ -n "$origtree" ] || die "git rev-parse failed"
584 if [ "$origtree" != "$thetree" ] || ! contained_by "$_theirs" "$_ours"; then
585 thecommit="$(git commit-tree -p "$_ours" -p "$_theirs" -m "$_msg" "$thetree")" &&
586 [ -n "$thecommit" ] || die "git commit-tree failed"
587 git update-ref -m "$_msg" HEAD "$thecommit" || die "git update-ref failed"
589 # mention how the merge was made
590 echo "Merge made by the 'recursive' strategy."
591 rm -f "$tmpstdout"
592 return 0
595 # run git_topmerge with the passed in arguments (it always does --no-stat)
596 # then return the exit status of git_topmerge
597 # if the returned exit status is no error show a shortstat before
598 # returning assuming the merge was done into the previous HEAD but exclude
599 # .topdeps and .topmsg info from the stat unless doing a --merge
600 # if the first argument is --merge or --theirs or --remove handle .topmsg/.topdeps
601 # as follows:
602 # (default) .topmsg and .topdeps always keep ours
603 # --merge a normal merge takes place
604 # --theirs .topmsg and .topdeps always keep theirs
605 # --remove .topmsg and .topdeps are removed from the result and working tree
606 # note this function should only be called after attempt_index_merge fails as
607 # it implicity always does --no-ff (except for --merge which will --ff)
608 git_merge() {
609 _ret=0
610 git_topmerge -v _oldhead "$@" || _ret=$?
611 _exclusions=
612 [ "$1" = "--merge" ] || _exclusions=":/ :!/.topdeps :!/.topmsg"
613 [ "$_ret" != "0" ] || git --no-pager diff-tree --shortstat "$_oldhead" HEAD^0 -- $_exclusions
614 return $_ret
617 # $1 => .topfile handling ([--]merge, [--]theirs, [--]remove or else do ours)
618 # $2 => current "HEAD"
619 # $3 => proposed fast-forward-to "HEAD"
620 # result is success if fast-forward satisfies $1
621 topff_ok() {
622 case "${1#--}" in
623 merge|theirs)
624 # merge and theirs will always be correct
626 remove)
627 # okay if both blobs are "missing" in $3
628 printf '%s\n' "$3:.topdeps" "$3:.topmsg" |
629 git cat-file --batch-check="%(objectname) %(objecttype)" |
631 read _tdo _tdt &&
632 read _tmo _tmt &&
633 [ "$_tdt" = "missing" ] &&
634 [ "$_tmt" = "missing" ]
635 } || return 1
638 # "ours"
639 # okay if both blobs are the same (same hash or missing)
640 printf '%s\n' "$2:.topdeps" "$2:.topmsg" "$3:.topdeps" "$3:.topmsg" |
641 git cat-file --batch-check="%(objectname) %(objecttype)" |
643 read _td1o _td1t &&
644 read _tm1o _tm1t &&
645 read _td2o _td2t &&
646 read _tm2o _tm2t &&
647 { [ "$_td1t" = "$_td2t" ] &&
648 { [ "$_td1o" = "$_td2o" ] ||
649 [ "$_td1t" = "missing" ]; }; } &&
650 { [ "$_tm1t" = "$_tm2t" ] &&
651 { [ "$_tm1o" = "$_tm2o" ] ||
652 [ "$_tm1t" = "missing" ]; }; }
653 } || return 1
655 esac
656 return 0
659 # similar to git_merge but operates exclusively using a separate index and temp dir
660 # only trivial aggressive automatic (i.e. simple) merges are supported
662 # [optional] '--no-auto' to suppress "automatic" merging, merge fails instead
663 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
664 # $1 => '' to discard result, 'refs/?*' to update the specified ref or a varname
665 # $2 => '-m' MUST be '-m'
666 # $3 => commit message AND, if $1 matches refs/?* the update-ref message
667 # $4 => commit-ish to merge as "ours"
668 # $5 => commit-ish to merge as "theirs"
669 # [$6...] => more commit-ishes to merge as "theirs" in octopus
671 # all merging is done in a separate index (or temporary files for simple merges)
672 # if successful the ref or var is updated with the result
673 # otherwise everything is left unchanged and a silent failure occurs
674 # if successful and $1 matches refs/?* it WILL BE UPDATED to a new commit using the
675 # message and appropriate parents AND HEAD WILL BE DETACHED first if it's a symref
676 # to the same ref
677 # otherwise if $1 does not match refs/?* and is not empty the named variable will
678 # be set to contain the resulting commit from the merge
679 # the working tree and index ARE LEFT COMPLETELY UNTOUCHED no matter what
680 v_attempt_index_merge() {
681 _noauto=
682 if [ "$1" = "--no-auto" ]; then
683 _noauto=1
684 shift
686 _exclusions=
687 [ "$1" = "--merge" ] || _exclusions=":/ :!/.topdeps :!/.topmsg"
688 _mstyle=
689 if [ "$1" = "--merge" ] || [ "$1" = "--theirs" ] || [ "$1" = "--remove" ]; then
690 _mmode="${1#--}"
691 shift
692 if [ "$_mmode" = "merge" ] || [ "$_mmode" = "theirs" ]; then
693 _mstyle="-top$_mmode"
696 [ "$#" -ge 5 ] && [ "$2" = "-m" ] && [ -n "$3" ] && [ -n "$4" ] && [ -n "$5" ] ||
697 die "programmer error: invalid arguments to v_attempt_index_merge: $*"
698 _var="$1"
699 _msg="$3"
700 _head="$4"
701 shift 4
702 rh="$(git rev-parse --quiet --verify "$_head^0" --)" && [ -n "$rh" ] || return 1
703 orh="$rh"
704 oth=
705 _mmsg=
706 newc=
707 _nodt=
708 _same=
709 _mt=
710 _octo=
711 if [ $# -gt 1 ]; then
712 if [ "$_mmode" = "merge" ] || [ "$_mmode" = "theirs" ]; then
713 die "programmer error: invalid octopus .topfile strategy to v_attempt_index_merge: --$_mode"
715 ihl="$(git merge-base --independent "$@")" || return 1
716 set -- $ihl
717 [ $# -ge 1 ] && [ -n "$1" ] || return 1
719 [ $# -eq 1 ] || _octo=1
720 mb="$(git merge-base ${_octo:+--octopus} "$rh" "$@")" && [ -n "$mb" ] || {
721 mb="$(git hash-object -w -t tree --stdin < /dev/null)"
722 _mt=1
724 if [ -z "$_mt" ]; then
725 if [ -n "$_octo" ]; then
726 while [ $# -gt 1 ] && mbh="$(git merge-base "$rh" "$1")" && [ -n "$mbh" ]; do
727 if [ "$rh" = "$mbh" ]; then
728 if topff_ok "$_mmode" "$rh" "$1"; then
729 _mmsg="Fast-forward (no commit created)"
730 rh="$1"
731 shift
732 else
733 break
735 elif [ "$1" = "$mbh" ]; then
736 shift
737 else
738 break;
740 done
741 if [ $# -eq 1 ]; then
742 _octo=
743 mb="$(git merge-base "$rh" "$1")" && [ -n "$mb" ] || return 1
746 if [ -z "$_octo" ]; then
747 r1="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$r1" ] || return 1
748 oth="$r1"
749 set -- "$r1"
750 if [ "$rh" = "$mb" ]; then
751 if topff_ok "$_mmode" "$rh" "$r1"; then
752 _mmsg="Fast-forward (no commit created)"
753 newc="$r1"
754 _nodt=1
755 _mstyle=
757 elif [ "$r1" = "$mb" ]; then
758 [ -n "$_mmsg" ] || _mmsg="Already up-to-date!"
759 newc="$rh"
760 _nodt=1
761 _same=1
762 _mstyle=
766 if [ -z "$newc" ]; then
767 if [ "$_mmode" = "theirs" ] && [ -z "$oth" ]; then
768 oth="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$oth" ] || return 1
769 set -- "$oth"
771 inew="$tg_tmp_dir/index.$$"
772 ! [ -e "$inew" ] || rm -f "$inew"
773 itmp="$tg_tmp_dir/output.$$"
774 imrg="$tg_tmp_dir/auto.$$"
775 [ -z "$_octo" ] || >"$imrg"
776 _auto=
777 _parents=
778 _newrh="$rh"
779 while :; do
780 if [ -n "$_parents" ]; then
781 if contained_by "$1" "$_newrh"; then
782 shift
783 continue
786 GIT_INDEX_FILE="$inew" git read-tree -m --aggressive -i "$mb" "$rh" "$1" || { rm -f "$inew" "$imrg"; return 1; }
787 GIT_INDEX_FILE="$inew" git ls-files --unmerged --full-name --abbrev :/ >"$itmp" 2>&1 || { rm -f "$inew" "$itmp" "$imrg"; return 1; }
788 ! [ -s "$itmp" ] || {
789 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
790 rm -f "$inew" "$itmp" "$imrg"
791 return 1
793 if [ -s "$itmp" ]; then
794 if [ -n "$_noauto" ]; then
795 rm -f "$inew" "$itmp" "$imrg"
796 return 1
798 if [ -n "$_octo" ]; then
799 cat "$itmp" >>"$imrg"
800 else
801 cat "$itmp"
803 _auto=" automatic"
806 _mstyle=
807 rm -f "$itmp"
808 _parents="${_parents:+$_parents }-p $1"
809 if [ $# -gt 1 ]; then
810 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
811 rh="$newt"
812 shift
813 continue
815 break;
816 done
817 if [ "$_mmode" != "merge" ]; then
818 case "$_mmode" in
819 theirs) _source="$oth";;
820 remove) _source="";;
821 *) _source="$orh";;
822 esac
823 _newinfo=
824 [ -z "$_source" ] ||
825 _newinfo="$(git cat-file --batch-check="%(objecttype) %(objectname)$tab%(rest)" <<-EOT |
826 $_source:.topdeps .topdeps
827 $_source:.topmsg .topmsg
829 sed -n 's/^blob /100644 /p'
831 [ -z "$_newinfo" ] || _newinfo="$lf$_newinfo"
832 GIT_INDEX_FILE="$inew" git update-index --index-info <<-EOT || { rm -f "$inew" "$imrg"; return 1; }
833 0 $nullsha$tab.topdeps
834 0 $nullsha$tab.topmsg$_newinfo
837 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
838 [ -z "$_octo" ] || sort -u <"$imrg"
839 rm -f "$inew" "$imrg"
840 newc="$(git commit-tree -p "$orh" $_parents -m "$_msg" "$newt")" && [ -n "$newc" ] || return 1
841 _mmsg="Merge made by the 'trivial aggressive$_auto${_octo:+ octopus}' strategy."
843 case "$_var" in
844 refs/?*)
845 if [ -n "$_same" ]; then
846 _same=
847 if rv="$(git rev-parse --quiet --verify "$_var" --)" && [ "$rv" = "$newc" ]; then
848 _same=1
851 if [ -z "$_same" ]; then
852 detach_symref_head_on_branch "$_var" || return 1
853 # git update-ref returns 0 even on failure :(
854 git update-ref -m "$_msg" "$_var" "$newc" || return 1
858 eval "$_var="'"$newc"'
860 esac
861 echo "$_mmsg"
862 [ -n "$_nodt" ] || git --no-pager diff-tree --shortstat "$orh" "$newc" -- $_exclusions
863 return 0
866 # shortcut that passes $3 as a preceding argument (which must match refs/?*)
867 attempt_index_merge() {
868 _noauto=
869 _mmode=
870 if [ "$1" = "--no-auto" ]; then
871 _noauto="$1"
872 shift
874 if [ "$1" = "--merge" ] || [ "$1" = "--theirs" ] || [ "$1" = "--remove" ]; then
875 _mmode="$1"
876 shift
878 case "$3" in refs/?*);;*)
879 die "programmer error: invalid arguments to attempt_index_merge: $*"
880 esac
881 v_attempt_index_merge $_noauto $_mmode "$3" "$@"
884 on_base=
885 do_base_switch() {
886 [ -n "$1" ] || return 0
888 [ "$1" != "$on_base" ] ||
889 [ "$(git symbolic-ref -q HEAD)" != "refs/$topbases/$1" ]
890 then
891 switch_to_base "$1"
892 on_base="$1"
896 update_branch_internal() {
897 # We are cacheable until the first change
898 become_cacheable
900 _update_name="$1"
901 ## First, take care of our base
903 _depcheck="$(get_temp tg-depcheck)"
904 missing_deps=
905 needs_update "$_update_name" >"$_depcheck" || :
906 if [ -n "$missing_deps" ]; then
907 msg="Some dependencies are missing: $missing_deps"
908 if [ -n "$skipms" ]; then
909 info "$msg; skipping"
910 elif [ -z "$all" ]; then
911 die "$msg"
912 else
913 info "$msg; skipping branch $_update_name"
914 return 0
917 # allow automatic simple merges by default until a failure occurs
918 no_auto=
919 if [ -s "$_depcheck" ]; then
920 <"$_depcheck" \
921 sed 's/ [^ ]* *$//' | # last is $_update_name
922 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
923 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
924 >"$_depcheck.ideps" \
925 uniq -s 1 # fold branch lines; + always comes before - and thus wins within uniq
927 stash_now_if_requested
929 while read -r depline; do
930 dep="${depline#?}"
931 action="${depline%$dep}"
933 # We do not distinguish between dependencies out-of-date
934 # and base/remote out-of-date cases for $dep here,
935 # but thanks to needs_update returning : or refs/remotes/<remote>/<name>
936 # for the latter, we do correctly recurse here
937 # in both cases.
939 if [ x"$action" = x+ ]; then
940 case " $missing_deps " in *" $dep "*)
941 info "Skipping recursing to missing dependency: $dep"
942 continue
943 esac
944 info "Recursing to $dep..."
945 recursive_update "$dep" || exit 3
947 done <"$_depcheck.ideps"
949 # Create a list of all the fully qualified ref names that need
950 # to be merged into $_update_name's base. This will be done
951 # as an octopus merge if there are no conflicts.
952 deplist=
953 deplines=
954 set --
955 while read -r dep; do
956 dep="${dep#?}"
957 case "$dep" in
958 "refs"/*)
959 set -- "$@" "$dep"
960 case "$dep" in
961 "refs/heads"/*)
962 d="${dep#refs/heads/}"
963 deplist="${deplist:+$deplist }$d"
964 deplines="$deplines$d$lf"
967 d="${dep#refs/}"
968 deplist="${deplist:+$deplist }$d"
969 deplines="$deplines$d$lf"
971 esac
974 set -- "$@" "refs/heads/$dep"
975 deplist="${deplist:+$deplist }$dep"
976 deplines="$deplines$dep$lf"
978 esac
979 done <"$_depcheck.ideps"
981 # Make sure we end up on the correct base branch
982 on_base=
983 if [ $# -ge 2 ]; then
984 info "Updating $_update_name base with deps: $deplist"
985 become_non_cacheable
986 msg="tgupdate: octopus merge $# deps into $_update_name base$lf$lf$deplines"
987 if attempt_index_merge --remove -m "$msg" "refs/$topbases/$_update_name" "$@"; then
988 set --
989 else
990 info "Octopus merge failed; falling back to multiple 3-way merges"
991 no_auto="--no-auto"
995 for fulldep in "$@"; do
996 # This will be either a proper topic branch
997 # or a remote base. (branch_needs_update() is called
998 # only on the _dependencies_, not our branch itself!)
1000 case "$fulldep" in
1001 "refs/heads"/*)
1002 dep="${fulldep#refs/heads/}";;
1003 "refs"/*)
1004 dep="${fulldep#refs/}";;
1006 dep="$fulldep";; # this should be a programmer error
1007 esac
1009 info "Updating $_update_name base with $dep changes..."
1010 become_non_cacheable
1011 msg="tgupdate: merge $dep into $_update_name base"
1013 ! attempt_index_merge $no_auto --remove -m "$msg" "refs/$topbases/$_update_name" "$fulldep^0" &&
1015 # We need to switch to the base branch
1016 # ...but only if we aren't there yet (from failed previous merge)
1017 do_base_switch "$_update_name" || die "do_base_switch failed" &&
1018 git_merge --remove --name "$_update_name base" --name "$dep" -m "$msg" "$fulldep^0"
1020 then
1021 rm "$_depcheck"
1022 save_state
1023 unset TG_RECURSIVE
1024 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1025 info "(use \`$tgdisplayac status\` to see more options)"
1026 exit 3
1028 done
1029 else
1030 info "The base is up-to-date."
1034 ## Second, update our head with the remote branch
1036 plusextra=
1037 merge_with="refs/$topbases/$_update_name"
1038 brmmode=
1039 if has_remote "$_update_name"; then
1040 _rname="refs/remotes/$base_remote/$_update_name"
1041 if branch_contains "refs/heads/$_update_name" "$_rname"; then
1042 info "The $_update_name head is up-to-date wrt. its remote branch."
1043 else
1044 stash_now_if_requested
1045 info "Reconciling $_update_name base with remote branch updates..."
1046 become_non_cacheable
1047 msg="tgupdate: merge ${_rname#refs/} onto $_update_name base"
1048 checkours=
1049 checktheirs=
1050 got_merge_with=
1051 brmmode="--merge"
1052 if [ -n "$mergeresult" ]; then
1053 checkours="$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" || :
1054 checktheirs="$(git rev-parse --verify --quiet "$_rname^0" --)" || :
1055 if [ "$mergeours" = "$checkours" ] && [ "$mergetheirs" = "$checktheirs" ]; then
1056 got_merge_with="$mergeresult"
1060 [ -z "$got_merge_with" ] &&
1061 ! v_attempt_index_merge $no_auto --theirs "merge_with" -m "$msg" "refs/$topbases/$_update_name" "$_rname^0" &&
1063 # *DETACH* our HEAD now!
1064 no_auto="--no-auto"
1065 git checkout -q --detach $iowopt "refs/$topbases/$_update_name" || die "git checkout failed" &&
1066 git_merge --theirs --name "$_update_name base content" --name "${_rname#refs/}" -m "$msg" "$_rname^0" &&
1067 merge_with="$(git rev-parse --verify HEAD --)"
1069 then
1070 save_state \
1071 "$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" \
1072 "$(git rev-parse --verify --quiet "$_rname^0" --)"
1073 unset TG_RECURSIVE
1074 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1075 info "(use \`$tgdisplayac status\` to see more options)"
1076 exit 3
1078 # Go back but remember we want to merge with this, not base
1079 [ -z "$got_merge_with" ] || merge_with="$got_merge_with"
1080 plusextra="${_rname#refs/} + "
1085 ## Third, update our head with the base
1087 if branch_contains "refs/heads/$_update_name" "$merge_with"; then
1088 info "The $_update_name head is up-to-date wrt. the base."
1089 return 0
1091 stash_now_if_requested
1092 info "Updating $_update_name against ${plusextra}new base..."
1093 become_non_cacheable
1094 msg="tgupdate: merge ${plusextra}$_update_name base into $_update_name"
1095 b4deps=
1096 if [ -n "$brmmode" ] && [ "$base_remote" ]; then
1097 b4deps="$(git rev-parse --verify --quiet "refs/heads/$_update_name:.topdeps" --)" && [ -n "$b4deps" ] ||
1098 b4deps="$(git hash-object -t blob -w --stdin </dev/null)"
1101 ! attempt_index_merge $no_auto $brmmode -m "$msg" "refs/heads/$_update_name" "$merge_with^0" &&
1103 # Home, sweet home...
1104 # (We want to always switch back, in case we were
1105 # on the base from failed previous merge.)
1106 git checkout -q $iowopt "$_update_name" || die "git checkout failed" &&
1107 git_merge $brmmode --name "$_update_name" --name "${plusextra}$topbases/$_update_name" -m "$msg" "$merge_with^0"
1109 then
1110 no_auto=
1111 merging_topfiles="${brmmode:+1}"
1112 save_state
1113 unset TG_RECURSIVE
1114 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1115 info "(use \`$tgdisplayac status\` to see more options)"
1116 exit 3
1119 # Fourth, auto create locally any newly depended on branches we got from the remote
1121 _result=0
1122 if [ -n "$b4deps" ] &&
1123 l8rdeps="$(git rev-parse --verify --quiet "refs/heads/$_update_name:.topdeps" --)" &&
1124 [ -n "$l8rdeps" ] && [ "$b4deps" != "$l8rdeps" ]
1125 then
1126 _olddeps=
1127 while read -r newdep; do
1128 if [ -n "$newdep" ]; then
1129 if auto_create_local_remote "$newdep"; then
1130 _result=75
1131 else
1132 if ref_exists "refs/heads/$newdep"; then
1133 # maybe the line just moved around
1134 [ -n "$_olddeps" ] && [ -f "$_olddeps" ] || {
1135 _olddeps="$(get_temp b4deps)" &&
1136 git cat-file blob "$b4deps" >"$_olddeps"
1138 if awk -v "newdep=$newdep" '$0 == newdep {exit 1}' <"$_olddeps"; then
1139 # nope, it's a new head already existing locally
1140 _result=75
1142 else
1143 # helpfully check to see if there's such a remote branch
1144 _rntgb=
1145 ! ref_exists "refs/remotes/$base_remote/$newdep" || _rntgb=1
1146 # maybe a locking local orphan base too
1147 _blocked=
1148 if [ -n "$_rntgb" ] &&
1149 ref_exists "refs/remotes/$base_remote/${topbases#heads/}$newdep" &&
1150 ref_exists "refs/$topbases/$newdep"
1151 then
1152 _blocked=1
1154 # spew the flexibly adjustable warning
1155 warn "-------------------------------------"
1156 warn "MISSING DEPENDENCY MERGED FROM REMOTE"
1157 warn "-------------------------------------"
1158 warn "Local Branch: $_update_name"
1159 warn " Remote Name: $base_remote"
1160 warn " Dependency: $newdep"
1161 if [ -n "$_blocked" ]; then
1162 warn "Blocking Ref: refs/$topbases/$newdep"
1163 elif [ -n "$_rntgb" ]; then
1164 warn "Existing Ref: refs/remotes/$base_remote/$newdep"
1166 warn ""
1167 if [ -n "$_blocked" ]; then
1168 warn "There is no local branch by that name, but"
1169 warn "there IS a remote TopGit branch available by"
1170 warn "that name, but creation of a local version has"
1171 warn "been blocked by existence of the ref shown above."
1172 elif [ -n "$_rntgb" ]; then
1173 warn "There is no local branch or remote TopGit"
1174 warn "branch available by that name, but there is an"
1175 warn "existing non-TopGit remote branch ref shown above."
1176 warn "Non-TopGit branches are not set up automatically"
1177 warn "by TopGit and must be maintained manually."
1178 else
1179 warn "There is no local branch or remote branch"
1180 warn "(TopGit or otherwise) available by that name."
1182 warn "-------------------------------------"
1186 done <<-EOT
1187 $(git diff "$b4deps" "$l8rdeps" -- | diff_added_lines)
1190 return $_result
1193 update_branch() {
1194 _ubicode=0
1195 _maxdeploop=3
1196 update_branch_internal "$@" || _ubicode=$?
1197 while [ "$_maxdeploop" -gt 0 ] && [ "$_ubicode" = "75" ]; do
1198 _maxdeploop="$(( $maxdeploop - 1 ))"
1199 info "Updating $1 again with newly added dependencies..."
1200 _ubicode=0
1201 update_branch_internal "$@" || _ubicode=$?
1202 done
1203 return $_ubicode
1206 # We are "read-only" and cacheable until the first change
1207 tg_read_only=1
1208 v_create_ref_cache
1210 do_non_annihilated_branches_patterns() {
1211 while read -r _pat && [ -n "$_pat" ]; do
1212 set -- "$@" "$_pat"
1213 done
1214 non_annihilated_branches "$@"
1217 do_non_annihilated_branches() {
1218 if [ -z "$pattern" ]; then
1219 non_annihilated_branches
1220 else
1221 do_non_annihilated_branches_patterns <<-EOT
1222 $(sed 'y/ /\n/' <<-LIST
1223 $pattern
1224 LIST
1230 if [ -n "$all" ] && [ -z "$restored" ]; then
1231 names=
1232 while read name && [ -n "$name" ]; do
1233 case " $names " in *" $name "*);;*)
1234 names="${names:+$names }$name"
1235 esac
1236 done <<-EOT
1237 $(do_non_annihilated_branches)
1241 for name in $names; do
1242 case " $processed " in *" $name "*) continue; esac
1243 [ -z "$all" ] && case "$names" in *" "*) ! :; esac || info "Proccessing $name..."
1244 update_branch "$name" || exit
1245 processed="${processed:+$processed }$name"
1246 done
1248 [ -z "$all" ] && case "$names" in *" "*) ! :; esac ||
1249 info "Returning to ${current#refs/heads/}..."
1250 checkout_symref_full "$current"
1251 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
1252 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :