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,2019
8 names
= # Branch(es) to update
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
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)
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"
40 if [ "${1:-0}" != 0 ]; then
41 printf '%s\n' "$USAGE" >&2
43 printf '%s\n' "$USAGE"
48 # Remove any currently untracked files that also appear in
49 # $1's tree AND have an identical blob hash. Never fail, but
50 # instead simply ignore any operations with problems.
51 clear_matching_untracked
() {
52 _cmutree
="$(git rev-parse --verify "$1^
{tree
}" 2>/dev/null)" &&
53 [ -n "$_cmutree" ] ||
return 0
54 _idxtree
="$(git write-tree 2>/dev/null)" &&
55 [ -n "$_idxtree" ] ||
return 0
56 v_get_show_toplevel _tplvl
57 # Save the list of untracked files in a temp file, then feed the
58 # "A" file lines from diff-tree index to the tree to an awk script
59 # along with the list of untracked files and let it spit out
60 # a list of blob matches which includes the file mode of the match
61 # and then remove any untracked files with a matching hash and mode.
62 # Due to limitations of awk, files with '\n' in their names are skipped.
63 _utfl
="$(get_temp untracked)" ||
return 0
65 command -v readlink
>/dev
/null
2>&1 || _hvrl
=
66 git status
--porcelain -z |
tr '\n\000' '\177\n' |
awk '!/^\?\? ./||/\177/{next}{print}' >"$_utfl" ||
:
69 git diff-tree
--raw --ignore-submodules=all
--no-renames -r -z --diff-filter=A
"$_idxtree" "$_cmutree" |
70 tr '\n\000' '\177\n' |
paste - - |
awk -v u
="$_utfl" '
72 function exitnow(c) {x=c;exit x}
75 while ((e=(getline l<u))>0) {
76 if(l!~/^\?\? ./||l~/\177/)continue
81 BEGIN {if(u=="")exitnow(2);init()}
84 if($1!=":000000"||($2!="100644"&&$2!="100755"&&$2!="120000")||
85 $3!~/^0+$/||$4!~/^[0-9a-f][0-9a-f][0-9a-f][0-9a-f]+$/||
87 t=$0;sub(/^[^\t]*\t/,"",t)
88 if(t!=""&&f[t])print $4" "$2" "t
91 while read -r _uthsh _utmod _utnam
&& [ -n "$_utnam" ] && [ -n "$_uthsh" ]; do
92 case "$_utmod" in "100644"|
"100755"|
"120000");;*) continue; esac
94 [ -L "$_tplvl/$_utnam" ] ||
95 [ -f "$_tplvl/$_utnam" ]
98 "100644") test ! -L "$_tplvl/$_utnam" &&
99 test ! -x "$_tplvl/$_utnam" ||
continue;;
100 "100755") test ! -L "$_tplvl/$_utnam" &&
101 test -x "$_tplvl/$_utnam" ||
continue;;
102 "120000") test -n "$_hvrl" &&
103 test -L "$_tplvl/$_utnam" ||
continue;;
107 "100644"|
"100755") _flhsh
="$(git hash-object -t blob -- "$_tplvl/$_utnam")";;
108 "120000") _flhsh
="$(readlink -n "$_tplvl/$_utnam" 2>/dev/null |
109 git hash-object -t blob --stdin)";;
112 [ "$_flhsh" = "$_uthsh" ] ||
continue
113 rm -f "$_tplvl/$_utnam" >/dev
/null
2>&1 ||
:
120 # --base mode comes here with $1 set to <base-branch> and $2 set to <ref>
121 # and all options already parsed and validated into above-listed flags
122 # this function should exit after returning to "$current"
125 v_verify_topgit_branch tgbranch
"$1"
126 depcnt
="$(git cat-file blob "refs
/heads
/$tgbranch:.topdeps
" 2>/dev/null | awk 'END {print NR}')"
127 if [ $depcnt -gt 0 ]; then
129 [ $depcnt -eq 1 ] || grammar
="dependencies"
130 die
"'$tgbranch' is not a TopGit [BASE] branch (it has $depcnt $grammar)"
132 newrev
="$(git rev-parse --verify "$2^
0" --)" && [ -n "$newrev" ] ||
133 die
"not a valid commit-ish: $2"
134 v_ref_exists_rev baserev
"refs/$topbases/$tgbranch" && [ -n "$baserev" ] ||
135 die
"unable to get current base commit for branch '$tgbranch'"
136 if [ "$baserev" = "$newrev" ]; then
137 [ -n "$quiet" ] ||
echo "No change"
141 ! contained_by
"$newrev" "refs/heads/$tgbranch" || alreadymerged
=1
142 if [ -z "$basefrc" ] && ! contained_by
"$baserev" "$newrev"; then
143 die
"Refusing non-fast-forward update of base without --force"
146 # check that we can checkout the branch
148 [ -n "$alreadymerged" ] || git read-tree
-n -u -m "refs/heads/$tgbranch" ||
149 die
"git checkout \"$branch\" would fail"
151 # and make sure everything's clean and we know who we are
153 [ -n "$alreadymerged" ] || ensure_clean_tree
154 ensure_ident_available
156 # always auto stash even if it's just to the anonymous stash TG_STASH
158 stashmsg
="tgupdate: autostash before --base $tgbranch update"
159 if [ -n "$stash" ]; then
160 tg tag
-q -q -m "$stashmsg" --stash "$tgbranch" &&
161 stashhash
="$(git rev-parse --quiet --verify refs/tgstash --)" &&
162 [ -n "$stashhash" ] &&
163 [ "$(git cat-file -t "$stashhash" 2>/dev/null)" = "tag" ] ||
164 die
"requested --stash failed"
166 tg tag
--anonymous "$tgbranch" &&
167 stashhash
="$(git rev-parse --quiet --verify TG_STASH --)" &&
168 [ -n "$stashhash" ] &&
169 [ "$(git cat-file -t "$stashhash" 2>/dev/null)" = "tag" ] ||
170 die
"anonymous --stash failed"
173 # proceed with the update
175 git update-ref
-m "tg update --base $tgbranch $2" "refs/$topbases/$tgbranch" "$newrev" "$baserev" ||
176 die
"Unable to update base ref"
177 if [ -n "$alreadymerged" ]; then
178 [ -n "$quiet" ] ||
echo "Already contained in branch (base updated)"
181 git checkout
-q $iowopt "$tgbranch" || die
"git checkout failed"
183 # options validation guarantees that at most one of basemsg or basefile is set
184 [ -z "$basemsg" ] || msgopt
='-m "$basemsg"'
185 if [ -n "$basefile" ]; then
186 # git merge does not accept a -F <msgfile> option so we have to fake it
187 basefilemsg
="$(cat "$basefile")" || die
"could not read file '$basefile'"
188 msgopt
='-m "$basefilemsg"'
191 if [ -n "$editmode" ]; then
192 if [ "$editmode" = "0" ]; then
198 if [ -z "$basemsg$basefile" ]; then
199 [ -n "$editopt" ] || editopt
="--edit"
200 basemsg
="tg update --base $tgbranch $2"
201 msgopt
='-m "$basemsg"'
203 [ -n "$editopt" ] || editopt
="--no-edit"
206 [ -z "$basenc" ] || ncopt
="--no-commit"
207 eval git merge
--no-ff --no-log --no-stat $auhopt $ncopt $editopt "$msgopt" "refs/$topbases/$tgbranch" -- ||
exit
209 if [ -z "$basenc" ]; then
210 clear_matching_untracked
"$current"
211 checkout_symref_full
"$current" || ec
=$?
213 if [ "${ec:-0}" != "0" ]; then
214 info
"Unable to switch to ${current#refs/heads/}"
216 git rev-parse
-q --verify HEAD
>/dev
/null
2>&1 &&
217 ! git symbolic-ref
-q HEAD
>/dev
/null
2>&1
219 info
"HEAD is currently detached"
220 info
"Use 'git checkout ${current#refs/heads/}' to reattach"
227 state_dir
="$git_dir/tg-update"
236 [ -d "$state_dir" ] ||
return 1
237 [ -s "$state_dir/fullcmd" ] ||
return 1
238 [ -f "$state_dir/remote" ] ||
return 1
239 [ -f "$state_dir/skipms" ] ||
return 1
240 [ -f "$state_dir/all" ] ||
return 1
241 [ -s "$state_dir/current" ] ||
return 1
242 [ -s "$state_dir/stashhash" ] ||
return 1
243 [ -s "$state_dir/name" ] ||
return 1
244 [ -s "$state_dir/names" ] ||
return 1
245 [ -f "$state_dir/processed" ] ||
return 1
246 [ -f "$state_dir/no_auto" ] ||
return 1
247 [ -f "$state_dir/setautoupdate" ] ||
return 1
248 [ -f "$state_dir/merging_topfiles" ] ||
return 1
249 [ -f "$state_dir/mergeours" ] ||
return 1
250 [ -f "$state_dir/mergeours" ] ||
return 1
251 if [ -s "$state_dir/mergeours" ]; then
252 [ -s "$state_dir/mergetheirs" ] ||
return 1
254 ! [ -s "$state_dir/mergetheirs" ] ||
return 1
259 is_active || die
"programmer error"
260 IFS
= read -r fullcmd
<"$state_dir/fullcmd" && [ -n "$fullcmd" ]
261 IFS
= read -r base_remote
<"$state_dir/remote" ||
:
262 IFS
= read -r skipms
<"$state_dir/skipms" ||
:
263 IFS
= read -r all
<"$state_dir/all" ||
:
264 IFS
= read -r current
<"$state_dir/current" && [ -n "$current" ]
265 IFS
= read -r stashhash
<"$state_dir/stashhash" && [ -n "$stashhash" ]
266 IFS
= read -r name
<"$state_dir/name" && [ -n "$name" ]
267 IFS
= read -r names
<"$state_dir/names" && [ -n "$names" ]
268 IFS
= read -r processed
<"$state_dir/processed" ||
:
269 IFS
= read -r next_no_auto
<"$state_dir/no_auto" ||
:
270 IFS
= read -r setautoupdate
<"$state_dir/setautoupdate" ||
:
271 # merging_topfiles is for outside info but not to be restored
272 IFS
= read -r mergeours
<"$state_dir/mergeours" ||
:
273 IFS
= read -r mergetheirs
<"$state_dir/mergetheirs" ||
:
274 if [ -n "$mergeours" ] && [ -n "$mergetheirs" ]; then
275 headhash
="$(git rev-parse --quiet --verify HEAD --)" ||
:
276 if [ -n "$headhash" ]; then
277 parents
="$(git --no-pager log -n 1 --format='format:%P' "$headhash" -- 2>/dev/null)" ||
:
278 if [ "$parents" = "$mergeours $mergetheirs" ]; then
279 mergeresult
="$headhash"
282 if [ -z "$mergeresult" ]; then
291 ! [ -e "$state_dir" ] ||
rm -rf "$state_dir" >/dev
/null
2>&1 ||
:
296 ! is_active || isactive
=1
298 if [ -z "$isactive" ] && [ $# -eq 1 ]; then
299 case "$1" in --abort|
--stop|
--continue|
--skip) isactiveopt
=1; esac
301 if [ -n "$isactive" ] ||
[ -n "$isactiveopt" ]; then
302 [ $# -eq 1 ] && [ x
"$1" != x
"--status" ] ||
{ do_status
; exit 0; }
304 if [ -z "$isactive" ]; then
306 info
"No update is currently active"
313 IFS
= read -r current
<"$state_dir/current" ||
:
314 IFS
= read -r stashhash
<"$state_dir/stashhash" ||
:
316 if [ -n "$stashhash" ]; then
317 tg revert
-f -q -q --no-stash "$stashhash" >/dev
/null
2>&1 ||
:
319 if [ -n "$current" ]; then
320 info
"Ok, update aborted, returning to ${current#refs/heads/}"
321 checkout_symref_full
-f "$current"
323 info
"Ok, update aborted. Now, you just need to"
324 info
"switch back to some sane branch using \`git$gitcdopt checkout\`."
326 ! [ -f "$git_dir/TGMERGE_MSG" ] ||
[ -e "$git_dir/MERGE_MSG" ] ||
327 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" ||
:
332 info
"Ok, update stopped. Now, you just need to"
333 info
"switch back to some sane branch using \`git$gitcdopt checkout\`."
334 ! [ -f "$git_dir/TGMERGE_MSG" ] ||
[ -e "$git_dir/MERGE_MSG" ] ||
335 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" ||
:
340 if [ "$1" = "--skip" ]; then
341 info
"Ok, I will try to continue without updating this branch."
343 case " $processed " in *" $name "*);;*)
344 processed
="${processed:+$processed }$name"
347 # assume user fixed it
348 # we could be left on a detached HEAD if we were resolving
349 # a conflict while merging a base in, fix it with a checkout
350 v_strip_ref bname
"$name"
351 git checkout
-q $iowopt "$bname"
360 if [ -z "$restored" ]; then
362 [ "$(git config --get --bool topgit.setAutoUpdate 2>/dev/null)" != "false" ] ||
365 while [ -n "$1" ]; do
371 [ -z "$names$pattern" ] || usage
1
379 --auto|
--auto-update|
--set-auto|
--set-auto-update)
381 --no-auto|
--no-auto-update|
--no-set-auto|
--no-set-auto-update)
396 [ $# -gt 0 ] && [ -n "$1" ] || die
"option $arg requires an argument"
402 basemsg
="${1#--message=}";;
404 [ $# -gt 0 ] && [ -n "$1" ] || die
"option $arg requires an argument"
410 basefile
="${1#--file=}"
411 [ -n "$basefile" ] || die
"option --file= requires an argument"
420 if [ -z "$all" ]; then
421 namecnt
=$
(( $namecnt + 1 ))
422 [ "$namecnt" != "1" ] || name1
="$arg"
423 [ "$namecnt" != "2" ] || name2
="$arg"
424 names
="${names:+$names }$arg"
426 v_strip_ref arg
"$arg"
427 pattern
="${pattern:+$pattern }refs/$topbases/$arg"
433 while [ $# -gt 0 ]; do
434 if [ -z "$all" ]; then
435 namecnt
=$
(( $namecnt + 1 ))
436 [ "$namecnt" != "1" ] || name1
="$1"
437 [ "$namecnt" != "2" ] || name2
="$1"
438 names
="${names:+$names }$*"
441 pattern
="${pattern:+$pattern }refs/$topbases/$arg"
445 [ -n "$basemode" ] ||
[ -z "$editmode$basemsg$basefile$basenc$basefrc" ] || usage
1
446 [ -z "$basemode" ] ||
[ -z "$all$skipms" ] || usage
1
447 [ -z "$basemode" ] ||
[ -z "$basemsg" ] ||
[ -z "$basefile" ] || usage
1
448 [ -z "$basemode" ] ||
[ "$namecnt" -eq 2 ] || usage
1
450 current
="$(git symbolic-ref -q HEAD)" ||
:
451 if [ -n "$current" ]; then
452 [ -n "$(git rev-parse --verify --quiet HEAD --)" ] ||
453 die
"cannot return to unborn branch; switch to another branch"
455 current
="$(git rev-parse --verify --quiet HEAD)" ||
456 die
"cannot return to invalid HEAD; switch to another branch"
459 [ -z "$basemode" ] || do_base_mode
"$name1" "$name2"
461 origpattern
="$pattern"
462 [ -z "$pattern" ] && pattern
="refs/$topbases"
465 [ -n "$all$names" ] || names
="HEAD"
466 if [ -z "$all" ]; then
469 while [ $# -gt 0 ]; do
470 v_verify_topgit_branch name
"$1"
471 case " $names " in *" $name "*);;*)
472 names
="${names:+$names }$name"
478 if [ "$namecnt" -eq 1 ]; then
479 case "$fullcmd" in *" @"|
*" HEAD")
481 fullcmd
="${fullcmd% *}"
484 [ "$namecnt" -ne 0 ] || fullcmd
="$fullcmd $names"
490 mkdir
-p "$state_dir"
491 printf '%s\n' "$fullcmd" >"$state_dir/fullcmd"
492 printf '%s\n' "$base_remote" >"$state_dir/remote"
493 printf '%s\n' "$skipms" >"$state_dir/skipms"
494 printf '%s\n' "$all" >"$state_dir/all"
495 printf '%s\n' "$current" >"$state_dir/current"
496 printf '%s\n' "$stashhash" >"$state_dir/stashhash"
497 printf '%s\n' "$name" >"$state_dir/name"
498 printf '%s\n' "$names" >"$state_dir/names"
499 printf '%s\n' "$processed" >"$state_dir/processed"
500 printf '%s\n' "$no_auto" >"$state_dir/no_auto"
501 printf '%s\n' "$setautoupdate" >"$state_dir/setautoupdate"
502 # this one is an external flag and needs to be zero length for false
503 printf '%s' "$merging_topfiles" >"$state_dir/merging_topfiles"
504 printf '%s\n' "$1" >"$state_dir/mergeours"
505 printf '%s\n' "$2" >"$state_dir/mergetheirs"
508 stash_now_if_requested
() {
509 [ -z "$TG_RECURSIVE" ] ||
return 0
510 [ -z "$stashhash" ] ||
return 0
511 ensure_ident_available
512 msg
="tgupdate: autostash before update"
513 if [ -n "$all" ]; then
514 msg
="$msg --all${origpattern:+ $origpattern}"
519 if [ -n "$stash" ]; then
520 tg tag
-q -q -m "$msg" --stash "$@" &&
521 stashhash
="$(git rev-parse --quiet --verify refs/tgstash --)" &&
522 [ -n "$stashhash" ] &&
523 [ "$(git cat-file -t "$stashhash" 2>/dev/null)" = "tag" ] ||
524 die
"requested --stash failed"
526 tg tag
--anonymous "$@" &&
527 stashhash
="$(git rev-parse --quiet --verify TG_STASH --)" &&
528 [ -n "$stashhash" ] &&
529 [ "$(git cat-file -t "$stashhash" 2>/dev/null)" = "tag" ] ||
530 die
"anonymous --stash failed"
532 [ -z "$next_no_auto" ] || no_auto
="$next_no_auto"
540 if [ -n "$TG_RECURSIVE" ]; then
541 TG_RECURSIVE
="==> [$1]${TG_RECURSIVE#==>}"
543 TG_RECURSIVE
="==> [$1]$lf"
547 [ $_ret -eq 3 ] && exit 3
553 [ -n "$1" ] ||
return 0
555 [ "$1" != "$on_base" ] ||
556 [ "$(git symbolic-ref -q HEAD)" != "refs/$topbases/$1" ]
563 update_branch_internal
() {
564 # We are cacheable until the first change
568 ## First, take care of our base
570 _depcheck
="$(get_temp tg-depcheck)"
572 needs_update
"$_update_name" >"$_depcheck" ||
:
573 if [ -n "$missing_deps" ]; then
574 msg
="Some dependencies are missing: $missing_deps"
575 if [ -n "$skipms" ]; then
576 info
"$msg; skipping"
577 elif [ -z "$all" ]; then
580 info
"$msg; skipping branch $_update_name"
584 # allow automatic simple merges by default until a failure occurs
586 if [ -s "$_depcheck" ]; then
587 # (1) last word is $_update_name, remove it
588 # (2) keep only immediate dependencies of a chain adding a leading '+'
589 # (3) one-level deep dependencies get a '-' prefix instead
591 -e 's/ [^ ]* *$//; # (1)' \
592 -e 's/.* \([^ ]*\)$/+\1/; # (2)' \
593 -e 's/^\([^+]\)/-\1/; # (3)' |
594 # now each line is +branch or -branch (+ == recurse)
595 >"$_depcheck.ideps" \
596 uniq -s 1 # fold branch lines; + always comes before - and thus wins within uniq
598 stash_now_if_requested
600 while read -r depline
; do
602 action
="${depline%$dep}"
604 # We do not distinguish between dependencies out-of-date
605 # and base/remote out-of-date cases for $dep here,
606 # but thanks to needs_update returning : or :refs/remotes/...
607 # for the latter, we do correctly recurse here
610 if [ x
"$action" = x
+ ]; then
611 case " $missing_deps " in *" $dep "*)
612 info
"Skipping recursing to missing dependency: $dep"
615 info
"Recursing to $dep..."
616 recursive_update
"$dep" ||
exit 3
618 done <"$_depcheck.ideps"
620 # Create a list of all the fully qualified ref names that need
621 # to be merged into $_update_name's base. This will be done
622 # as an octopus merge if there are no conflicts.
626 while read -r dep
; do
635 deplist
="${deplist:+$deplist }$d"
636 deplines
="$deplines$d$lf"
640 deplist
="${deplist:+$deplist }$d"
641 deplines
="$deplines$d$lf"
646 set -- "$@" "refs/heads/$dep"
647 deplist
="${deplist:+$deplist }$dep"
648 deplines
="$deplines$dep$lf"
651 done <"$_depcheck.ideps"
653 # Make sure we end up on the correct base branch
655 if [ $# -ge 2 ]; then
656 info
"Updating $_update_name base with deps: $deplist"
658 msg
="tgupdate: octopus merge $# deps into $_update_name base$lf$lf$deplines"
659 if attempt_index_merge
--remove -m "$msg" "refs/$topbases/$_update_name" "$@"; then
662 info
"Octopus merge failed; falling back to multiple 3-way merges"
667 for fulldep
in "$@"; do
668 # This will be either a proper topic branch
669 # or a remote base. (branch_needs_update() is called
670 # only on the _dependencies_, not our branch itself!)
674 dep
="${fulldep#refs/heads/}";;
676 dep
="${fulldep#refs/}";;
678 dep
="$fulldep";; # this should be a programmer error
681 info
"Updating $_update_name base with $dep changes..."
683 msg
="tgupdate: merge $dep into $_update_name base"
685 ! attempt_index_merge
$no_auto --remove -m "$msg" "refs/$topbases/$_update_name" "$fulldep^0" &&
687 # We need to switch to the base branch
688 # ...but only if we aren't there yet (from failed previous merge)
689 do_base_switch
"$_update_name" || die
"do_base_switch failed" &&
690 git_merge
--remove --name "$_update_name base" --name "$dep" -m "$msg" "$fulldep^0"
696 info
"Please commit merge resolution and call \`$tgdisplayac update --continue\`"
697 info
"(use \`$tgdisplayac status\` to see more options)"
702 info
"The base is up-to-date."
706 ## Second, update our head with the remote branch
709 merge_with
="refs/$topbases/$_update_name"
711 if has_remote
"$_update_name"; then
712 _rname
="refs/remotes/$base_remote/$_update_name"
713 if branch_contains
"refs/heads/$_update_name" "$_rname"; then
714 info
"The $_update_name head is up-to-date wrt. its remote branch."
716 stash_now_if_requested
717 info
"Reconciling $_update_name base with remote branch updates..."
719 msg
="tgupdate: merge ${_rname#refs/} onto $_update_name base"
724 if [ -n "$mergeresult" ]; then
725 checkours
="$(git rev-parse --verify --quiet "refs
/$topbases/$_update_name^
0" --)" ||
:
726 checktheirs
="$(git rev-parse --verify --quiet "$_rname^
0" --)" ||
:
727 if [ "$mergeours" = "$checkours" ] && [ "$mergetheirs" = "$checktheirs" ]; then
728 got_merge_with
="$mergeresult"
732 [ -z "$got_merge_with" ] &&
733 ! v_attempt_index_merge
$no_auto --theirs "merge_with" -m "$msg" "refs/$topbases/$_update_name" "$_rname^0" &&
735 # *DETACH* our HEAD now!
737 git checkout
-q --detach $iowopt "refs/$topbases/$_update_name" || die
"git checkout failed" &&
738 git_merge
--theirs --name "$_update_name base content" --name "${_rname#refs/}" -m "$msg" "$_rname^0" &&
739 merge_with
="$(git rev-parse --verify HEAD --)"
743 "$(git rev-parse --verify --quiet "refs
/$topbases/$_update_name^
0" --)" \
744 "$(git rev-parse --verify --quiet "$_rname^
0" --)"
746 info
"Please commit merge resolution and call \`$tgdisplayac update --continue\`"
747 info
"(use \`$tgdisplayac status\` to see more options)"
750 # Go back but remember we want to merge with this, not base
751 [ -z "$got_merge_with" ] || merge_with
="$got_merge_with"
752 plusextra
="${_rname#refs/} + "
757 ## Third, update our head with the base
759 if branch_contains
"refs/heads/$_update_name" "$merge_with"; then
760 info
"The $_update_name head is up-to-date wrt. the base."
763 stash_now_if_requested
764 info
"Updating $_update_name against ${plusextra}new base..."
766 msg
="tgupdate: merge ${plusextra}$_update_name base into $_update_name"
768 if [ -n "$brmmode" ] && [ "$base_remote" ]; then
769 b4deps
="$(git rev-parse --verify --quiet "refs
/heads
/$_update_name:.topdeps
" --)" && [ -n "$b4deps" ] ||
770 b4deps
="$(git hash-object -t blob -w --stdin </dev/null)"
773 ! attempt_index_merge
$no_auto $brmmode -m "$msg" "refs/heads/$_update_name" "$merge_with^0" &&
775 # Home, sweet home...
776 # (We want to always switch back, in case we were
777 # on the base from failed previous merge.)
778 git checkout
-q $iowopt "$_update_name" || die
"git checkout failed" &&
779 git_merge
$brmmode --name "$_update_name" --name "${plusextra}$topbases/$_update_name" -m "$msg" "$merge_with^0"
783 merging_topfiles
="${brmmode:+1}"
786 info
"Please commit merge resolution and call \`$tgdisplayac update --continue\`"
787 info
"(use \`$tgdisplayac status\` to see more options)"
791 # Fourth, auto create locally any newly depended on branches we got from the remote
794 if [ -n "$b4deps" ] &&
795 l8rdeps
="$(git rev-parse --verify --quiet "refs
/heads
/$_update_name:.topdeps
" --)" &&
796 [ -n "$l8rdeps" ] && [ "$b4deps" != "$l8rdeps" ]
799 while read -r newdep
; do
800 if [ -n "$newdep" ]; then
801 if auto_create_local_remote
"$newdep"; then
804 if ref_exists
"refs/heads/$newdep"; then
805 # maybe the line just moved around
806 [ -n "$_olddeps" ] && [ -f "$_olddeps" ] ||
{
807 _olddeps
="$(get_temp b4deps)" &&
808 git cat-file blob
"$b4deps" >"$_olddeps"
810 if awk -v "newdep=$newdep" '$0 == newdep {exit 1}' <"$_olddeps"; then
811 # nope, it's a new head already existing locally
815 # helpfully check to see if there's such a remote branch
817 ! ref_exists
"refs/remotes/$base_remote/$newdep" || _rntgb
=1
818 # maybe a blocking local orphan base too
820 if [ -n "$_rntgb" ] &&
821 ref_exists
"refs/remotes/$base_remote/${topbases#heads/}/$newdep" &&
822 ref_exists
"refs/$topbases/$newdep"
826 # spew the flexibly adjustable warning
827 warn
"-------------------------------------"
828 warn
"MISSING DEPENDENCY MERGED FROM REMOTE"
829 warn
"-------------------------------------"
830 warn
"Local Branch: $_update_name"
831 warn
" Remote Name: $base_remote"
832 warn
" Dependency: $newdep"
833 if [ -n "$_blocked" ]; then
834 warn
"Blocking Ref: refs/$topbases/$newdep"
835 elif [ -n "$_rntgb" ]; then
836 warn
"Existing Ref: refs/remotes/$base_remote/$newdep"
839 if [ -n "$_blocked" ]; then
840 warn
"There is no local branch by that name, but"
841 warn
"there IS a remote TopGit branch available by"
842 warn
"that name, but creation of a local version has"
843 warn
"been blocked by existence of the ref shown above."
844 elif [ -n "$_rntgb" ]; then
845 warn
"There is no local branch or remote TopGit"
846 warn
"branch available by that name, but there is an"
847 warn
"existing non-TopGit remote branch ref shown above."
848 warn
"Non-TopGit branches are not set up automatically"
849 warn
"by TopGit and must be maintained manually."
851 warn
"There is no local branch or remote branch"
852 warn
"(TopGit or otherwise) available by that name."
854 warn
"-------------------------------------"
859 $(git diff --ignore-space-at-eol "$b4deps" "$l8rdeps" -- | diff_added_lines)
868 update_branch_internal
"$@" || _ubicode
=$?
869 while [ "$_maxdeploop" -gt 0 ] && [ "$_ubicode" = "75" ]; do
870 _maxdeploop
="$(( $maxdeploop - 1 ))"
871 info
"Updating $1 again with newly added dependencies..."
873 update_branch_internal
"$@" || _ubicode
=$?
878 # We are "read-only" and cacheable until the first change
882 do_non_annihilated_branches_patterns
() {
883 while read -r _pat
&& [ -n "$_pat" ]; do
886 non_annihilated_branches
"$@"
889 do_non_annihilated_branches
() {
890 if [ -z "$pattern" ]; then
891 non_annihilated_branches
893 do_non_annihilated_branches_patterns
<<-EOT
894 $(sed 'y/ /\n/' <<-LIST
902 if [ -n "$all" ] && [ -z "$restored" ]; then
904 while read name
&& [ -n "$name" ]; do
905 case " $names " in *" $name "*);;*)
906 names
="${names:+$names }$name"
909 $(do_non_annihilated_branches)
913 for name
in $names; do
914 case " $processed " in *" $name "*) continue; esac
915 [ -z "$all" ] && case "$names" in *" "*) ! :; esac || info
"Proccessing $name..."
916 update_branch
"$name" ||
exit
917 processed
="${processed:+$processed }$name"
920 [ -z "$all" ] && case "$names" in *" "*) ! :; esac ||
921 info
"Returning to ${current#refs/heads/}..."
923 clear_matching_untracked
"$current"
924 checkout_symref_full
"$current" || ec
=$?
925 ! [ -f "$git_dir/TGMERGE_MSG" ] ||
[ -e "$git_dir/MERGE_MSG" ] ||
926 mv -f "$git_dir/TGMERGE_MSG" "$git_dir/MERGE_MSG" ||
:
929 if [ "${ec:-0}" != "0" ]; then
930 info
"Unable to switch to ${current#refs/heads/}"
932 git rev-parse
-q --verify HEAD
>/dev
/null
2>&1 &&
933 ! git symbolic-ref
-q HEAD
>/dev
/null
2>&1
935 info
"HEAD is currently detached"
936 info
"Use 'git checkout ${current#refs/heads/}' to reattach"