topgit_*_prepare.awk: do not append ^{} to missing
[topgit/pro.git] / tg-update.sh
blobb8b894f821e6bcd16d3c13a2c0279ffd76efd8da
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,2018
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 # options validation guarantees that at most one of basemsg or basefile is set
112 [ -z "$basemsg" ] || msgopt='-m "$basemsg"'
113 if [ -n "$basefile" ]; then
114 # git merge does not accept a -F <msgfile> option so we have to fake it
115 basefilemsg="$(cat "$basefile")" || die "could not read file '$basefile'"
116 msgopt='-m "$basefilemsg"'
118 editopt=
119 if [ -n "$editmode" ]; then
120 if [ "$editmode" = "0" ]; then
121 editopt="--no-edit"
122 else
123 editopt="--edit"
126 if [ -z "$basemsg$basefile" ]; then
127 [ -n "$editopt" ] || editopt="--edit"
128 basemsg="tg update --base $tgbranch $2"
129 msgopt='-m "$basemsg"'
130 else
131 [ -n "$editopt" ] || editopt="--no-edit"
133 ncopt=
134 [ -z "$basenc" ] || ncopt="--no-commit"
135 eval git merge --no-ff --no-log --no-stat $auhopt $ncopt $editopt "$msgopt" "refs/$topbases/$tgbranch" -- || exit
136 [ -n "$basenc" ] || checkout_symref_full "$current"
137 exit
140 state_dir="$git_dir/tg-update"
141 mergeours=
142 mergetheirs=
143 mergeresult=
144 stashhash=
145 next_no_auto=
146 merging_topfiles=
148 is_active() {
149 [ -d "$state_dir" ] || return 1
150 [ -s "$state_dir/fullcmd" ] || return 1
151 [ -f "$state_dir/remote" ] || return 1
152 [ -f "$state_dir/skipms" ] || return 1
153 [ -f "$state_dir/all" ] || return 1
154 [ -s "$state_dir/current" ] || return 1
155 [ -s "$state_dir/stashhash" ] || return 1
156 [ -s "$state_dir/name" ] || return 1
157 [ -s "$state_dir/names" ] || return 1
158 [ -f "$state_dir/processed" ] || return 1
159 [ -f "$state_dir/no_auto" ] || return 1
160 [ -f "$state_dir/setautoupdate" ] || return 1
161 [ -f "$state_dir/merging_topfiles" ] || return 1
162 [ -f "$state_dir/mergeours" ] || return 1
163 [ -f "$state_dir/mergeours" ] || return 1
164 if [ -s "$state_dir/mergeours" ]; then
165 [ -s "$state_dir/mergetheirs" ] || return 1
166 else
167 ! [ -s "$state_dir/mergetheirs" ] || return 1
171 restore_state() {
172 is_active || die "programmer error"
173 IFS= read -r fullcmd <"$state_dir/fullcmd" && [ -n "$fullcmd" ]
174 IFS= read -r base_remote <"$state_dir/remote" || :
175 IFS= read -r skipms <"$state_dir/skipms" || :
176 IFS= read -r all <"$state_dir/all" || :
177 IFS= read -r current <"$state_dir/current" && [ -n "$current" ]
178 IFS= read -r stashhash <"$state_dir/stashhash" && [ -n "$stashhash" ]
179 IFS= read -r name <"$state_dir/name" && [ -n "$name" ]
180 IFS= read -r names <"$state_dir/names" && [ -n "$names" ]
181 IFS= read -r processed <"$state_dir/processed" || :
182 IFS= read -r next_no_auto <"$state_dir/no_auto" || :
183 IFS= read -r setautoupdate <"$state_dir/setautoupdate" || :
184 # merging_topfiles is for outside info but not to be restored
185 IFS= read -r mergeours <"$state_dir/mergeours" || :
186 IFS= read -r mergetheirs <"$state_dir/mergetheirs" || :
187 if [ -n "$mergeours" ] && [ -n "$mergetheirs" ]; then
188 headhash="$(git rev-parse --quiet --verify HEAD --)" || :
189 if [ -n "$headhash" ]; then
190 parents="$(git --no-pager log -n 1 --format='format:%P' "$headhash" -- 2>/dev/null)" || :
191 if [ "$parents" = "$mergeours $mergetheirs" ]; then
192 mergeresult="$headhash"
195 if [ -z "$mergeresult" ]; then
196 mergeours=
197 mergetheirs=
200 restored=1
203 clear_state() {
204 ! [ -e "$state_dir" ] || rm -rf "$state_dir" >/dev/null 2>&1 || :
207 restarted=
208 isactive=
209 ! is_active || isactive=1
210 isactiveopt=
211 if [ -z "$isactive" ] && [ $# -eq 1 ]; then
212 case "$1" in --abort|--stop|--continue|--skip) isactiveopt=1; esac
214 if [ -n "$isactive" ] || [ -n "$isactiveopt" ]; then
215 [ $# -eq 1 ] && [ x"$1" != x"--status" ] || { do_status; exit 0; }
216 ensure_work_tree
217 if [ -z "$isactive" ]; then
218 clear_state
219 info "No update is currently active"
220 exit 0
222 case "$1" in
223 --abort)
224 current=
225 stashhash=
226 IFS= read -r current <"$state_dir/current" || :
227 IFS= read -r stashhash <"$state_dir/stashhash" || :
228 clear_state
229 if [ -n "$stashhash" ]; then
230 tg revert -f -q -q --no-stash "$stashhash" >/dev/null 2>&1 || :
232 if [ -n "$current" ]; then
233 info "Ok, update aborted, returning to ${current#refs/heads/}"
234 checkout_symref_full -f "$current"
235 else
236 info "Ok, update aborted. Now, you just need to"
237 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
239 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
240 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
241 exit 0
243 --stop)
244 clear_state
245 info "Ok, update stopped. Now, you just need to"
246 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
247 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
248 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
249 exit 0
251 --continue|--skip)
252 restore_state
253 if [ "$1" = "--skip" ]; then
254 info "Ok, I will try to continue without updating this branch."
255 git reset --hard -q
256 case " $processed " in *" $name "*);;*)
257 processed="${processed:+$processed }$name"
258 esac
260 # assume user fixed it
261 # we could be left on a detached HEAD if we were resolving
262 # a conflict while merging a base in, fix it with a checkout
263 git checkout -q $iowopt "$(strip_ref "$name")"
266 do_status
267 exit 1
268 esac
270 clear_state
272 if [ -z "$restored" ]; then
273 setautoupdate=1
274 [ "$(git config --get --bool topgit.setAutoUpdate 2>/dev/null)" != "false" ] ||
275 setautoupdate=
277 while [ -n "$1" ]; do
278 arg="$1"; shift
279 case "$arg" in
281 usage;;
282 -a|--all)
283 [ -z "$names$pattern" ] || usage 1
284 all=1;;
285 --skip-missing)
286 skipms=1;;
287 --stash)
288 stash=1;;
289 --no-stash)
290 stash=;;
291 --auto|--auto-update|--set-auto|--set-auto-update)
292 setautoupdate=1;;
293 --no-auto|--no-auto-update|--no-set-auto|--no-set-auto-update)
294 setautoupdate=;;
295 --quiet|-q)
296 quiet=1;;
297 --base)
298 basemode=1;;
299 --edit|-e)
300 editmode=1;;
301 --no-edit)
302 editmode=0;;
303 --no-commit)
304 basenc=1;;
305 --force|-f)
306 basefrc=1;;
307 -m|--message)
308 [ $# -gt 0 ] && [ -n "$1" ] || die "option $arg requires an argument"
309 basemsg="$1"
310 shift;;
311 -m?*)
312 basemsg="${1#-m}";;
313 --message=*)
314 basemsg="${1#--message=}";;
315 -F|--file)
316 [ $# -gt 0 ] && [ -n "$1" ] || die "option $arg requires an argument"
317 basefile="$1"
318 shift;;
319 -F?*)
320 basefile="${1#-F}";;
321 --file=*)
322 basefile="${1#--file=}"
323 [ -n "$basefile" ] || die "option --file= requires an argument"
325 -?*)
326 usage 1;;
328 break;;
332 if [ -z "$all" ]; then
333 namecnt=$(( $namecnt + 1 ))
334 [ "$namecnt" != "1" ] || name1="$arg"
335 [ "$namecnt" != "2" ] || name2="$arg"
336 names="${names:+$names }$arg"
337 else
338 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$arg")"
341 esac
342 done
343 ensure_work_tree
344 while [ $# -gt 0 ]; do
345 if [ -z "$all" ]; then
346 namecnt=$(( $namecnt + 1 ))
347 [ "$namecnt" != "1" ] || name1="$1"
348 [ "$namecnt" != "2" ] || name2="$1"
349 names="${names:+$names }$*"
350 else
351 pattern="${pattern:+$pattern }refs/$topbases/$(strip_ref "$1")"
353 shift
354 done
355 [ -n "$basemode" ] || [ -z "$editmode$basemsg$basefile$basenc$basefrc" ] || usage 1
356 [ -z "$basemode" ] || [ -z "$all$skipms" ] || usage 1
357 [ -z "$basemode" ] || [ -z "$basemsg" ] || [ -z "$basefile" ] || usage 1
358 [ -z "$basemode" ] || [ "$namecnt" -eq 2 ] || usage 1
360 current="$(git symbolic-ref -q HEAD)" || :
361 if [ -n "$current" ]; then
362 [ -n "$(git rev-parse --verify --quiet HEAD --)" ] ||
363 die "cannot return to unborn branch; switch to another branch"
364 else
365 current="$(git rev-parse --verify --quiet HEAD)" ||
366 die "cannot return to invalid HEAD; switch to another branch"
369 [ -z "$basemode" ] || do_base_mode "$name1" "$name2"
371 origpattern="$pattern"
372 [ -z "$pattern" ] && pattern="refs/$topbases"
374 processed=
375 [ -n "$all$names" ] || names="HEAD"
376 if [ -z "$all" ]; then
377 clean_names() {
378 names=
379 while [ $# -gt 0 ]; do
380 name="$(verify_topgit_branch "$1")"
381 case " $names " in *" $name "*);;*)
382 names="${names:+$names }$name"
383 esac
384 shift
385 done
387 clean_names $names
388 if [ "$namecnt" -eq 1 ]; then
389 case "$fullcmd" in *" @"|*" HEAD")
390 namecnt=0
391 fullcmd="${fullcmd% *}"
392 esac
394 [ "$namecnt" -ne 0 ] || fullcmd="$fullcmd $names"
396 ensure_clean_tree
399 save_state() {
400 mkdir -p "$state_dir"
401 printf '%s\n' "$fullcmd" >"$state_dir/fullcmd"
402 printf '%s\n' "$base_remote" >"$state_dir/remote"
403 printf '%s\n' "$skipms" >"$state_dir/skipms"
404 printf '%s\n' "$all" >"$state_dir/all"
405 printf '%s\n' "$current" >"$state_dir/current"
406 printf '%s\n' "$stashhash" >"$state_dir/stashhash"
407 printf '%s\n' "$name" >"$state_dir/name"
408 printf '%s\n' "$names" >"$state_dir/names"
409 printf '%s\n' "$processed" >"$state_dir/processed"
410 printf '%s\n' "$no_auto" >"$state_dir/no_auto"
411 printf '%s\n' "$setautoupdate" >"$state_dir/setautoupdate"
412 # this one is an external flag and needs to be zero length for false
413 printf '%s' "$merging_topfiles" >"$state_dir/merging_topfiles"
414 printf '%s\n' "$1" >"$state_dir/mergeours"
415 printf '%s\n' "$2" >"$state_dir/mergetheirs"
418 stash_now_if_requested() {
419 [ -z "$TG_RECURSIVE" ] || return 0
420 [ -z "$stashhash" ] || return 0
421 ensure_ident_available
422 msg="tgupdate: autostash before update"
423 if [ -n "$all" ]; then
424 msg="$msg --all${origpattern:+ $origpattern}"
425 else
426 msg="$msg $names"
428 set -- $names
429 if [ -n "$stash" ]; then
430 tg tag -q -q -m "$msg" --stash "$@" &&
431 stashhash="$(git rev-parse --quiet --verify refs/tgstash --)" &&
432 [ -n "$stashhash" ] &&
433 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
434 die "requested --stash failed"
435 else
436 tg tag --anonymous "$@" &&
437 stashhash="$(git rev-parse --quiet --verify TG_STASH --)" &&
438 [ -n "$stashhash" ] &&
439 [ "$(git cat-file -t "$stashhash" -- 2>/dev/null)" = "tag" ] ||
440 die "anonymous --stash failed"
442 [ -z "$next_no_auto" ] || no_auto="$next_no_auto"
443 next_no_auto=
446 recursive_update() {
447 _ret=0
448 on_base=
450 if [ -n "$TG_RECURSIVE" ]; then
451 TG_RECURSIVE="==> [$1]${TG_RECURSIVE#==>}"
452 else
453 TG_RECURSIVE="==> [$1]$lf"
455 update_branch "$1"
456 ) || _ret=$?
457 [ $_ret -eq 3 ] && exit 3
458 return $_ret
461 # If HEAD is a symref to "$1" detach it at its current value
462 detach_symref_head_on_branch() {
463 _hsr="$(git symbolic-ref -q HEAD --)" && [ -n "$_hsr" ] || return 0
464 _hrv="$(git rev-parse --quiet --verify HEAD --)" && [ -n "$_hrv" ] ||
465 die "cannot detach_symref_head_on_branch from unborn branch $_hsr"
466 git update-ref --no-deref -m "detaching HEAD from $_hsr to safely update it" HEAD "$_hrv"
469 # git_topmerge will need this even on success and since it might otherwise
470 # be called many times do it just the once here and now
471 repotoplvl="$(git rev-parse --show-toplevel)" && [ -n "$repotoplvl" ] && [ -d "$repotoplvl" ] ||
472 die "git rev-parse --show-toplevel failed"
474 # Run an in-tree recursive merge but make sure we get the desired version of
475 # any .topdeps and .topmsg files. The $auhopt and --no-stat options are
476 # always implicitly in effect. If successful, a new commit is performed on HEAD.
478 # The "git merge-recursive" tool (and others) must be run to get the desired
479 # result. And --no-ff is always implicitly in effect as well.
481 # NOTE: [optional] arguments MUST appear in the order shown
482 # [optional] '-v' varname => optional variable to return original HEAD hash in
483 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
484 # [optional] '--name' <name-for-ours [--name <name-for-theirs>]
485 # $1 => '-m' MUST be '-m'
486 # $2 => commit message
487 # $3 => commit-ish to merge as "theirs"
488 git_topmerge()
490 _ovar=
491 [ "$1" != "-v" ] || [ $# -lt 2 ] || [ -z "$2" ] || { _ovar="$2"; shift 2; }
492 _mmode=
493 case "$1" in --theirs|--remove|--merge) _mmode="${1#--}"; shift; esac
494 _nameours=
495 _nametheirs=
496 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
497 _nameours="$2"
498 shift 2
499 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
500 _nametheirs="$2"
501 shift 2
504 : "${_nameours:=HEAD}"
505 [ "$#" -eq 3 ] && [ "$1" = "-m" ] && [ -n "$2" ] && [ -n "$3" ] ||
506 die "programmer error: invalid arguments to git_topmerge: $*"
507 _ours="$(git rev-parse --verify HEAD^0)" || die "git rev-parse failed"
508 _theirs="$(git rev-parse --verify "$3^0")" || die "git rev-parse failed"
509 [ -z "$_ovar" ] || eval "$_ovar="'"$_ours"'
510 eval "GITHEAD_$_ours="'"$_nameours"' && eval export "GITHEAD_$_ours"
511 if [ -n "$_nametheirs" ]; then
512 eval "GITHEAD_$_theirs="'"$_nametheirs"' && eval export "GITHEAD_$_theirs"
514 _mdriver='touch %A'
515 if [ "$_mmode" = "merge" ]; then
516 TG_L1="$_nameours" && export TG_L1
517 TG_L2="merged common ancestors" && export TG_L2
518 TG_L3="${_nametheirs:-$3}" && export TG_L3
519 _mdriver='git merge-file -L "$TG_L1" -L "$TG_L2" -L "$TG_L3" --marker-size=%L %A %O %B'
521 _msg="$2"
522 _mt=
523 _mb="$(git merge-base --all "$_ours" "$_theirs")" && [ -n "$_mb" ] ||
524 { _mt=1; _mb="$(git hash-object -w -t tree --stdin < /dev/null)"; }
525 # any .topdeps or .topmsg output needs to be stripped from stdout
526 tmpstdout="$tg_tmp_dir/stdout.$$"
527 _ret=0
528 git -c "merge.ours.driver=$_mdriver" merge-recursive \
529 $_mb -- "$_ours" "$_theirs" >"$tmpstdout" || _ret=$?
530 # success or failure is not relevant until after fixing up the
531 # .topdeps and .topmsg files and running rerere unless _ret >= 126
532 [ $_ret -lt 126 ] || return $_ret
533 if [ "$_mmode" = "merge" ]; then
534 cat "$tmpstdout"
535 else
536 case "$_mmode" in
537 theirs) _source="$_theirs";;
538 remove) _source="";;
539 *) _source="$_ours";;
540 esac
541 _newinfo=
542 [ -z "$_source" ] ||
543 _newinfo="$(git cat-file --batch-check="%(objecttype) %(objectname)$tab%(rest)" <<-EOT |
544 $_source:.topdeps .topdeps
545 $_source:.topmsg .topmsg
547 sed -n 's/^blob /100644 /p'
549 [ -z "$_newinfo" ] || _newinfo="$lf$_newinfo"
550 git update-index --index-info <<-EOT ||
551 0 $nullsha$tab.topdeps
552 0 $nullsha$tab.topmsg$_newinfo
554 die "git update-index failed"
555 if [ "$_mmode" = "remove" ] &&
556 { [ -e "$repotoplvl/.topdeps" ] || [ -e "$repotoplvl/.topmsg" ]; }
557 then
558 rm -r -f "$repotoplvl/.topdeps" "$repotoplvl/.topmsg" >/dev/null 2>&1 || :
559 else
560 for zapbad in "$repotoplvl/.topdeps" "$repotoplvl/.topmsg"; do
561 if [ -e "$zapbad" ] && { [ -L "$zapbad" ] || [ ! -f "$zapbad" ]; }; then
562 rm -r -f "$zapbad"
564 done
565 (cd "$repotoplvl" && git checkout-index -q -f -u -- .topdeps .topmsg) ||
566 die "git checkout-index failed"
568 # dump output without any .topdeps or .topmsg messages
569 sed -e '/ \.topdeps/d' -e '/ \.topmsg/d' <"$tmpstdout"
571 # rerere will be a nop unless rerere.enabled is true, but might complete the merge!
572 eval git "${setautoupdate:+-c rerere.autoupdate=1}" rerere || :
573 git ls-files --unmerged --full-name --abbrev :/ >"$tmpstdout" 2>&1 ||
574 die "git ls-files failed"
575 if [ -s "$tmpstdout" ]; then
576 [ "$_ret" != "0" ] || _ret=1
577 else
578 _ret=0
580 if [ $_ret -ne 0 ]; then
581 # merge failed, spit out message, enter "merge" mode and return
583 printf '%s\n\n# Conflicts:\n' "$_msg"
584 sed -n "/$tab/s/^[^$tab]*/#/p" <"$tmpstdout" | sort -u
585 } >"$git_dir/MERGE_MSG"
586 git update-ref MERGE_HEAD "$_theirs" || :
587 echo 'Automatic merge failed; fix conflicts and then commit the result.'
588 rm -f "$tmpstdout"
589 return $_ret
591 # commit time at last!
592 thetree="$(git write-tree)" || die "git write-tree failed"
593 # avoid an extra "already up-to-date" commit (can't happen if _mt though)
594 origtree=
595 [ -n "$_mt" ] || {
596 origtree="$(git rev-parse --quiet --verify "$_ours^{tree}" --)" &&
597 [ -n "$origtree" ]
598 } || die "git rev-parse failed"
599 if [ "$origtree" != "$thetree" ] || ! contained_by "$_theirs" "$_ours"; then
600 thecommit="$(git commit-tree -p "$_ours" -p "$_theirs" -m "$_msg" "$thetree")" &&
601 [ -n "$thecommit" ] || die "git commit-tree failed"
602 git update-ref -m "$_msg" HEAD "$thecommit" || die "git update-ref failed"
604 # mention how the merge was made
605 echo "Merge made by the 'recursive' strategy."
606 rm -f "$tmpstdout"
607 return 0
610 # run git_topmerge with the passed in arguments (it always does --no-stat)
611 # then return the exit status of git_topmerge
612 # if the returned exit status is no error show a shortstat before
613 # returning assuming the merge was done into the previous HEAD but exclude
614 # .topdeps and .topmsg info from the stat unless doing a --merge
615 # if the first argument is --merge or --theirs or --remove handle .topmsg/.topdeps
616 # as follows:
617 # (default) .topmsg and .topdeps always keep ours
618 # --merge a normal merge takes place
619 # --theirs .topmsg and .topdeps always keep theirs
620 # --remove .topmsg and .topdeps are removed from the result and working tree
621 # note this function should only be called after attempt_index_merge fails as
622 # it implicity always does --no-ff (except for --merge which will --ff)
623 git_merge() {
624 _ret=0
625 git_topmerge -v _oldhead "$@" || _ret=$?
626 _exclusions=
627 [ "$1" = "--merge" ] || _exclusions=":/ :!/.topdeps :!/.topmsg"
628 [ "$_ret" != "0" ] || git --no-pager diff-tree --shortstat "$_oldhead" HEAD^0 -- $_exclusions
629 return $_ret
632 # $1 => .topfile handling ([--]merge, [--]theirs, [--]remove or else do ours)
633 # $2 => current "HEAD"
634 # $3 => proposed fast-forward-to "HEAD"
635 # result is success if fast-forward satisfies $1
636 topff_ok() {
637 case "${1#--}" in
638 merge|theirs)
639 # merge and theirs will always be correct
641 remove)
642 # okay if both blobs are "missing" in $3
643 printf '%s\n' "$3:.topdeps" "$3:.topmsg" |
644 git cat-file --batch-check="%(objectname) %(objecttype)" |
646 read _tdo _tdt &&
647 read _tmo _tmt &&
648 [ "$_tdt" = "missing" ] &&
649 [ "$_tmt" = "missing" ]
650 } || return 1
653 # "ours"
654 # okay if both blobs are the same (same hash or missing)
655 printf '%s\n' "$2:.topdeps" "$2:.topmsg" "$3:.topdeps" "$3:.topmsg" |
656 git cat-file --batch-check="%(objectname) %(objecttype)" |
658 read _td1o _td1t &&
659 read _tm1o _tm1t &&
660 read _td2o _td2t &&
661 read _tm2o _tm2t &&
662 { [ "$_td1t" = "$_td2t" ] &&
663 { [ "$_td1o" = "$_td2o" ] ||
664 [ "$_td1t" = "missing" ]; }; } &&
665 { [ "$_tm1t" = "$_tm2t" ] &&
666 { [ "$_tm1o" = "$_tm2o" ] ||
667 [ "$_tm1t" = "missing" ]; }; }
668 } || return 1
670 esac
671 return 0
674 # similar to git_merge but operates exclusively using a separate index and temp dir
675 # only trivial aggressive automatic (i.e. simple) merges are supported
677 # [optional] '--no-auto' to suppress "automatic" merging, merge fails instead
678 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
679 # $1 => '' to discard result, 'refs/?*' to update the specified ref or a varname
680 # $2 => '-m' MUST be '-m'
681 # $3 => commit message AND, if $1 matches refs/?* the update-ref message
682 # $4 => commit-ish to merge as "ours"
683 # $5 => commit-ish to merge as "theirs"
684 # [$6...] => more commit-ishes to merge as "theirs" in octopus
686 # all merging is done in a separate index (or temporary files for simple merges)
687 # if successful the ref or var is updated with the result
688 # otherwise everything is left unchanged and a silent failure occurs
689 # if successful and $1 matches refs/?* it WILL BE UPDATED to a new commit using the
690 # message and appropriate parents AND HEAD WILL BE DETACHED first if it's a symref
691 # to the same ref
692 # otherwise if $1 does not match refs/?* and is not empty the named variable will
693 # be set to contain the resulting commit from the merge
694 # the working tree and index ARE LEFT COMPLETELY UNTOUCHED no matter what
695 v_attempt_index_merge() {
696 _noauto=
697 if [ "$1" = "--no-auto" ]; then
698 _noauto=1
699 shift
701 _exclusions=
702 [ "$1" = "--merge" ] || _exclusions=":/ :!/.topdeps :!/.topmsg"
703 _mstyle=
704 if [ "$1" = "--merge" ] || [ "$1" = "--theirs" ] || [ "$1" = "--remove" ]; then
705 _mmode="${1#--}"
706 shift
707 if [ "$_mmode" = "merge" ] || [ "$_mmode" = "theirs" ]; then
708 _mstyle="-top$_mmode"
711 [ "$#" -ge 5 ] && [ "$2" = "-m" ] && [ -n "$3" ] && [ -n "$4" ] && [ -n "$5" ] ||
712 die "programmer error: invalid arguments to v_attempt_index_merge: $*"
713 _var="$1"
714 _msg="$3"
715 _head="$4"
716 shift 4
717 rh="$(git rev-parse --quiet --verify "$_head^0" --)" && [ -n "$rh" ] || return 1
718 orh="$rh"
719 oth=
720 _mmsg=
721 newc=
722 _nodt=
723 _same=
724 _mt=
725 _octo=
726 if [ $# -gt 1 ]; then
727 if [ "$_mmode" = "merge" ] || [ "$_mmode" = "theirs" ]; then
728 die "programmer error: invalid octopus .topfile strategy to v_attempt_index_merge: --$_mode"
730 ihl="$(git merge-base --independent "$@")" || return 1
731 set -- $ihl
732 [ $# -ge 1 ] && [ -n "$1" ] || return 1
734 [ $# -eq 1 ] || _octo=1
735 mb="$(git merge-base ${_octo:+--octopus} "$rh" "$@")" && [ -n "$mb" ] || {
736 mb="$(git hash-object -w -t tree --stdin < /dev/null)"
737 _mt=1
739 if [ -z "$_mt" ]; then
740 if [ -n "$_octo" ]; then
741 while [ $# -gt 1 ] && mbh="$(git merge-base "$rh" "$1")" && [ -n "$mbh" ]; do
742 if [ "$rh" = "$mbh" ]; then
743 if topff_ok "$_mmode" "$rh" "$1"; then
744 _mmsg="Fast-forward (no commit created)"
745 rh="$1"
746 shift
747 else
748 break
750 elif [ "$1" = "$mbh" ]; then
751 shift
752 else
753 break;
755 done
756 if [ $# -eq 1 ]; then
757 _octo=
758 mb="$(git merge-base "$rh" "$1")" && [ -n "$mb" ] || return 1
761 if [ -z "$_octo" ]; then
762 r1="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$r1" ] || return 1
763 oth="$r1"
764 set -- "$r1"
765 if [ "$rh" = "$mb" ]; then
766 if topff_ok "$_mmode" "$rh" "$r1"; then
767 _mmsg="Fast-forward (no commit created)"
768 newc="$r1"
769 _nodt=1
770 _mstyle=
772 elif [ "$r1" = "$mb" ]; then
773 [ -n "$_mmsg" ] || _mmsg="Already up-to-date!"
774 newc="$rh"
775 _nodt=1
776 _same=1
777 _mstyle=
781 if [ -z "$newc" ]; then
782 if [ "$_mmode" = "theirs" ] && [ -z "$oth" ]; then
783 oth="$(git rev-parse --quiet --verify "$1^0" --)" && [ -n "$oth" ] || return 1
784 set -- "$oth"
786 inew="$tg_tmp_dir/index.$$"
787 ! [ -e "$inew" ] || rm -f "$inew"
788 itmp="$tg_tmp_dir/output.$$"
789 imrg="$tg_tmp_dir/auto.$$"
790 [ -z "$_octo" ] || >"$imrg"
791 _auto=
792 _parents=
793 _newrh="$rh"
794 while :; do
795 if [ -n "$_parents" ]; then
796 if contained_by "$1" "$_newrh"; then
797 shift
798 continue
801 GIT_INDEX_FILE="$inew" git read-tree -m --aggressive -i "$mb" "$rh" "$1" || { rm -f "$inew" "$imrg"; return 1; }
802 GIT_INDEX_FILE="$inew" git ls-files --unmerged --full-name --abbrev :/ >"$itmp" 2>&1 || { rm -f "$inew" "$itmp" "$imrg"; return 1; }
803 ! [ -s "$itmp" ] || {
804 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
805 rm -f "$inew" "$itmp" "$imrg"
806 return 1
808 if [ -s "$itmp" ]; then
809 if [ -n "$_noauto" ]; then
810 rm -f "$inew" "$itmp" "$imrg"
811 return 1
813 if [ -n "$_octo" ]; then
814 cat "$itmp" >>"$imrg"
815 else
816 cat "$itmp"
818 _auto=" automatic"
821 _mstyle=
822 rm -f "$itmp"
823 _parents="${_parents:+$_parents }-p $1"
824 if [ $# -gt 1 ]; then
825 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
826 rh="$newt"
827 shift
828 continue
830 break;
831 done
832 if [ "$_mmode" != "merge" ]; then
833 case "$_mmode" in
834 theirs) _source="$oth";;
835 remove) _source="";;
836 *) _source="$orh";;
837 esac
838 _newinfo=
839 [ -z "$_source" ] ||
840 _newinfo="$(git cat-file --batch-check="%(objecttype) %(objectname)$tab%(rest)" <<-EOT |
841 $_source:.topdeps .topdeps
842 $_source:.topmsg .topmsg
844 sed -n 's/^blob /100644 /p'
846 [ -z "$_newinfo" ] || _newinfo="$lf$_newinfo"
847 GIT_INDEX_FILE="$inew" git update-index --index-info <<-EOT || { rm -f "$inew" "$imrg"; return 1; }
848 0 $nullsha$tab.topdeps
849 0 $nullsha$tab.topmsg$_newinfo
852 newt="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] || { rm -f "$inew" "$imrg"; return 1; }
853 [ -z "$_octo" ] || sort -u <"$imrg"
854 rm -f "$inew" "$imrg"
855 newc="$(git commit-tree -p "$orh" $_parents -m "$_msg" "$newt")" && [ -n "$newc" ] || return 1
856 _mmsg="Merge made by the 'trivial aggressive$_auto${_octo:+ octopus}' strategy."
858 case "$_var" in
859 refs/?*)
860 if [ -n "$_same" ]; then
861 _same=
862 if rv="$(git rev-parse --quiet --verify "$_var" --)" && [ "$rv" = "$newc" ]; then
863 _same=1
866 if [ -z "$_same" ]; then
867 detach_symref_head_on_branch "$_var" || return 1
868 # git update-ref returns 0 even on failure :(
869 git update-ref -m "$_msg" "$_var" "$newc" || return 1
873 eval "$_var="'"$newc"'
875 esac
876 echo "$_mmsg"
877 [ -n "$_nodt" ] || git --no-pager diff-tree --shortstat "$orh" "$newc" -- $_exclusions
878 return 0
881 # shortcut that passes $3 as a preceding argument (which must match refs/?*)
882 attempt_index_merge() {
883 _noauto=
884 _mmode=
885 if [ "$1" = "--no-auto" ]; then
886 _noauto="$1"
887 shift
889 if [ "$1" = "--merge" ] || [ "$1" = "--theirs" ] || [ "$1" = "--remove" ]; then
890 _mmode="$1"
891 shift
893 case "$3" in refs/?*);;*)
894 die "programmer error: invalid arguments to attempt_index_merge: $*"
895 esac
896 v_attempt_index_merge $_noauto $_mmode "$3" "$@"
899 on_base=
900 do_base_switch() {
901 [ -n "$1" ] || return 0
903 [ "$1" != "$on_base" ] ||
904 [ "$(git symbolic-ref -q HEAD)" != "refs/$topbases/$1" ]
905 then
906 switch_to_base "$1"
907 on_base="$1"
911 update_branch_internal() {
912 # We are cacheable until the first change
913 become_cacheable
915 _update_name="$1"
916 ## First, take care of our base
918 _depcheck="$(get_temp tg-depcheck)"
919 missing_deps=
920 needs_update "$_update_name" >"$_depcheck" || :
921 if [ -n "$missing_deps" ]; then
922 msg="Some dependencies are missing: $missing_deps"
923 if [ -n "$skipms" ]; then
924 info "$msg; skipping"
925 elif [ -z "$all" ]; then
926 die "$msg"
927 else
928 info "$msg; skipping branch $_update_name"
929 return 0
932 # allow automatic simple merges by default until a failure occurs
933 no_auto=
934 if [ -s "$_depcheck" ]; then
935 # (1) last word is $_update_name, remove it
936 # (2) keep only immediate dependencies of a chain adding a leading '+'
937 # (3) one-level deep dependencies get a '-' prefix instead
938 <"$_depcheck" sed \
939 -e 's/ [^ ]* *$//; # (1)' \
940 -e 's/.* \([^ ]*\)$/+\1/; # (2)' \
941 -e 's/^\([^+]\)/-\1/; # (3)' |
942 # now each line is +branch or -branch (+ == recurse)
943 >"$_depcheck.ideps" \
944 uniq -s 1 # fold branch lines; + always comes before - and thus wins within uniq
946 stash_now_if_requested
948 while read -r depline; do
949 dep="${depline#?}"
950 action="${depline%$dep}"
952 # We do not distinguish between dependencies out-of-date
953 # and base/remote out-of-date cases for $dep here,
954 # but thanks to needs_update returning : or :refs/remotes/...
955 # for the latter, we do correctly recurse here
956 # in both cases.
958 if [ x"$action" = x+ ]; then
959 case " $missing_deps " in *" $dep "*)
960 info "Skipping recursing to missing dependency: $dep"
961 continue
962 esac
963 info "Recursing to $dep..."
964 recursive_update "$dep" || exit 3
966 done <"$_depcheck.ideps"
968 # Create a list of all the fully qualified ref names that need
969 # to be merged into $_update_name's base. This will be done
970 # as an octopus merge if there are no conflicts.
971 deplist=
972 deplines=
973 set --
974 while read -r dep; do
975 dep="${dep#?}"
976 case "$dep" in
978 d="${dep#?}"
979 set -- "$@" "$d"
980 case "$d" in
981 "refs/heads"/*)
982 d="${d#refs/heads/}"
983 deplist="${deplist:+$deplist }$d"
984 deplines="$deplines$d$lf"
987 d="${d#refs/}"
988 deplist="${deplist:+$deplist }$d"
989 deplines="$deplines$d$lf"
991 esac
994 set -- "$@" "refs/heads/$dep"
995 deplist="${deplist:+$deplist }$dep"
996 deplines="$deplines$dep$lf"
998 esac
999 done <"$_depcheck.ideps"
1001 # Make sure we end up on the correct base branch
1002 on_base=
1003 if [ $# -ge 2 ]; then
1004 info "Updating $_update_name base with deps: $deplist"
1005 become_non_cacheable
1006 msg="tgupdate: octopus merge $# deps into $_update_name base$lf$lf$deplines"
1007 if attempt_index_merge --remove -m "$msg" "refs/$topbases/$_update_name" "$@"; then
1008 set --
1009 else
1010 info "Octopus merge failed; falling back to multiple 3-way merges"
1011 no_auto="--no-auto"
1015 for fulldep in "$@"; do
1016 # This will be either a proper topic branch
1017 # or a remote base. (branch_needs_update() is called
1018 # only on the _dependencies_, not our branch itself!)
1020 case "$fulldep" in
1021 "refs/heads"/*)
1022 dep="${fulldep#refs/heads/}";;
1023 "refs"/*)
1024 dep="${fulldep#refs/}";;
1026 dep="$fulldep";; # this should be a programmer error
1027 esac
1029 info "Updating $_update_name base with $dep changes..."
1030 become_non_cacheable
1031 msg="tgupdate: merge $dep into $_update_name base"
1033 ! attempt_index_merge $no_auto --remove -m "$msg" "refs/$topbases/$_update_name" "$fulldep^0" &&
1035 # We need to switch to the base branch
1036 # ...but only if we aren't there yet (from failed previous merge)
1037 do_base_switch "$_update_name" || die "do_base_switch failed" &&
1038 git_merge --remove --name "$_update_name base" --name "$dep" -m "$msg" "$fulldep^0"
1040 then
1041 rm "$_depcheck"
1042 save_state
1043 unset TG_RECURSIVE
1044 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1045 info "(use \`$tgdisplayac status\` to see more options)"
1046 exit 3
1048 done
1049 else
1050 info "The base is up-to-date."
1054 ## Second, update our head with the remote branch
1056 plusextra=
1057 merge_with="refs/$topbases/$_update_name"
1058 brmmode=
1059 if has_remote "$_update_name"; then
1060 _rname="refs/remotes/$base_remote/$_update_name"
1061 if branch_contains "refs/heads/$_update_name" "$_rname"; then
1062 info "The $_update_name head is up-to-date wrt. its remote branch."
1063 else
1064 stash_now_if_requested
1065 info "Reconciling $_update_name base with remote branch updates..."
1066 become_non_cacheable
1067 msg="tgupdate: merge ${_rname#refs/} onto $_update_name base"
1068 checkours=
1069 checktheirs=
1070 got_merge_with=
1071 brmmode="--merge"
1072 if [ -n "$mergeresult" ]; then
1073 checkours="$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" || :
1074 checktheirs="$(git rev-parse --verify --quiet "$_rname^0" --)" || :
1075 if [ "$mergeours" = "$checkours" ] && [ "$mergetheirs" = "$checktheirs" ]; then
1076 got_merge_with="$mergeresult"
1080 [ -z "$got_merge_with" ] &&
1081 ! v_attempt_index_merge $no_auto --theirs "merge_with" -m "$msg" "refs/$topbases/$_update_name" "$_rname^0" &&
1083 # *DETACH* our HEAD now!
1084 no_auto="--no-auto"
1085 git checkout -q --detach $iowopt "refs/$topbases/$_update_name" || die "git checkout failed" &&
1086 git_merge --theirs --name "$_update_name base content" --name "${_rname#refs/}" -m "$msg" "$_rname^0" &&
1087 merge_with="$(git rev-parse --verify HEAD --)"
1089 then
1090 save_state \
1091 "$(git rev-parse --verify --quiet "refs/$topbases/$_update_name^0" --)" \
1092 "$(git rev-parse --verify --quiet "$_rname^0" --)"
1093 unset TG_RECURSIVE
1094 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1095 info "(use \`$tgdisplayac status\` to see more options)"
1096 exit 3
1098 # Go back but remember we want to merge with this, not base
1099 [ -z "$got_merge_with" ] || merge_with="$got_merge_with"
1100 plusextra="${_rname#refs/} + "
1105 ## Third, update our head with the base
1107 if branch_contains "refs/heads/$_update_name" "$merge_with"; then
1108 info "The $_update_name head is up-to-date wrt. the base."
1109 return 0
1111 stash_now_if_requested
1112 info "Updating $_update_name against ${plusextra}new base..."
1113 become_non_cacheable
1114 msg="tgupdate: merge ${plusextra}$_update_name base into $_update_name"
1115 b4deps=
1116 if [ -n "$brmmode" ] && [ "$base_remote" ]; then
1117 b4deps="$(git rev-parse --verify --quiet "refs/heads/$_update_name:.topdeps" --)" && [ -n "$b4deps" ] ||
1118 b4deps="$(git hash-object -t blob -w --stdin </dev/null)"
1121 ! attempt_index_merge $no_auto $brmmode -m "$msg" "refs/heads/$_update_name" "$merge_with^0" &&
1123 # Home, sweet home...
1124 # (We want to always switch back, in case we were
1125 # on the base from failed previous merge.)
1126 git checkout -q $iowopt "$_update_name" || die "git checkout failed" &&
1127 git_merge $brmmode --name "$_update_name" --name "${plusextra}$topbases/$_update_name" -m "$msg" "$merge_with^0"
1129 then
1130 no_auto=
1131 merging_topfiles="${brmmode:+1}"
1132 save_state
1133 unset TG_RECURSIVE
1134 info "Please commit merge resolution and call \`$tgdisplayac update --continue\`"
1135 info "(use \`$tgdisplayac status\` to see more options)"
1136 exit 3
1139 # Fourth, auto create locally any newly depended on branches we got from the remote
1141 _result=0
1142 if [ -n "$b4deps" ] &&
1143 l8rdeps="$(git rev-parse --verify --quiet "refs/heads/$_update_name:.topdeps" --)" &&
1144 [ -n "$l8rdeps" ] && [ "$b4deps" != "$l8rdeps" ]
1145 then
1146 _olddeps=
1147 while read -r newdep; do
1148 if [ -n "$newdep" ]; then
1149 if auto_create_local_remote "$newdep"; then
1150 _result=75
1151 else
1152 if ref_exists "refs/heads/$newdep"; then
1153 # maybe the line just moved around
1154 [ -n "$_olddeps" ] && [ -f "$_olddeps" ] || {
1155 _olddeps="$(get_temp b4deps)" &&
1156 git cat-file blob "$b4deps" >"$_olddeps"
1158 if awk -v "newdep=$newdep" '$0 == newdep {exit 1}' <"$_olddeps"; then
1159 # nope, it's a new head already existing locally
1160 _result=75
1162 else
1163 # helpfully check to see if there's such a remote branch
1164 _rntgb=
1165 ! ref_exists "refs/remotes/$base_remote/$newdep" || _rntgb=1
1166 # maybe a blocking local orphan base too
1167 _blocked=
1168 if [ -n "$_rntgb" ] &&
1169 ref_exists "refs/remotes/$base_remote/${topbases#heads/}/$newdep" &&
1170 ref_exists "refs/$topbases/$newdep"
1171 then
1172 _blocked=1
1174 # spew the flexibly adjustable warning
1175 warn "-------------------------------------"
1176 warn "MISSING DEPENDENCY MERGED FROM REMOTE"
1177 warn "-------------------------------------"
1178 warn "Local Branch: $_update_name"
1179 warn " Remote Name: $base_remote"
1180 warn " Dependency: $newdep"
1181 if [ -n "$_blocked" ]; then
1182 warn "Blocking Ref: refs/$topbases/$newdep"
1183 elif [ -n "$_rntgb" ]; then
1184 warn "Existing Ref: refs/remotes/$base_remote/$newdep"
1186 warn ""
1187 if [ -n "$_blocked" ]; then
1188 warn "There is no local branch by that name, but"
1189 warn "there IS a remote TopGit branch available by"
1190 warn "that name, but creation of a local version has"
1191 warn "been blocked by existence of the ref shown above."
1192 elif [ -n "$_rntgb" ]; then
1193 warn "There is no local branch or remote TopGit"
1194 warn "branch available by that name, but there is an"
1195 warn "existing non-TopGit remote branch ref shown above."
1196 warn "Non-TopGit branches are not set up automatically"
1197 warn "by TopGit and must be maintained manually."
1198 else
1199 warn "There is no local branch or remote branch"
1200 warn "(TopGit or otherwise) available by that name."
1202 warn "-------------------------------------"
1206 done <<-EOT
1207 $(git diff --ignore-space-at-eol "$b4deps" "$l8rdeps" -- | diff_added_lines)
1210 return $_result
1213 update_branch() {
1214 _ubicode=0
1215 _maxdeploop=3
1216 update_branch_internal "$@" || _ubicode=$?
1217 while [ "$_maxdeploop" -gt 0 ] && [ "$_ubicode" = "75" ]; do
1218 _maxdeploop="$(( $maxdeploop - 1 ))"
1219 info "Updating $1 again with newly added dependencies..."
1220 _ubicode=0
1221 update_branch_internal "$@" || _ubicode=$?
1222 done
1223 return $_ubicode
1226 # We are "read-only" and cacheable until the first change
1227 tg_read_only=1
1228 v_create_ref_cache
1230 do_non_annihilated_branches_patterns() {
1231 while read -r _pat && [ -n "$_pat" ]; do
1232 set -- "$@" "$_pat"
1233 done
1234 non_annihilated_branches "$@"
1237 do_non_annihilated_branches() {
1238 if [ -z "$pattern" ]; then
1239 non_annihilated_branches
1240 else
1241 do_non_annihilated_branches_patterns <<-EOT
1242 $(sed 'y/ /\n/' <<-LIST
1243 $pattern
1244 LIST
1250 if [ -n "$all" ] && [ -z "$restored" ]; then
1251 names=
1252 while read name && [ -n "$name" ]; do
1253 case " $names " in *" $name "*);;*)
1254 names="${names:+$names }$name"
1255 esac
1256 done <<-EOT
1257 $(do_non_annihilated_branches)
1261 for name in $names; do
1262 case " $processed " in *" $name "*) continue; esac
1263 [ -z "$all" ] && case "$names" in *" "*) ! :; esac || info "Proccessing $name..."
1264 update_branch "$name" || exit
1265 processed="${processed:+$processed }$name"
1266 done
1268 [ -z "$all" ] && case "$names" in *" "*) ! :; esac ||
1269 info "Returning to ${current#refs/heads/}..."
1270 checkout_symref_full "$current"
1271 ! [ -f "$git_dir/TGMERGE_MSG" ] || [ -e "$git_dir/MERGE_MSG" ] ||
1272 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" || :
1273 ec=$?
1274 tmpdir_cleanup || :
1275 git gc --auto || :
1276 exit $ec