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
8 name
= # Branch to update
9 all
= # Update all branches
10 pattern
= # Branch selection filter for -a
11 current
= # Branch we are currently on
12 skip
= # skip missing dependencies
13 stash
= # tgstash refs before changes
15 if [ "$(git config --get --bool topgit.autostash 2>/dev/null || :)" != "false" ]; then
16 # topgit.autostash is true
34 echo "Usage: ${tgname:-tg} [...] update [--[no-]stash] [--skip] ([<name>] | -a [<pattern>...])" >&2
37 if [ -z "$all" ]; then
38 [ -z "$name" ] || die
"name already specified ($name)"
41 pattern
="${pattern:+$pattern }refs/$topbases/$(strip_ref "$arg")"
46 origpattern
="$pattern"
47 [ -z "$pattern" ] && pattern
="refs/$topbases"
49 current
="$(strip_ref "$
(git symbolic-ref
-q HEAD ||
:)")"
50 if [ -z "$all" ]; then
51 name
="$(verify_topgit_branch "${name:-HEAD}")"
53 [ -n "$current" ] || die
"cannot return to detached tree; switch to another branch"
58 stash_now_if_requested
() {
59 [ -z "$TG_RECURSIVE" ] ||
return 0
60 ensure_ident_available
61 [ -n "$stash" ] ||
return 0
62 msg
="tgupdate: autostash before update"
63 if [ -n "$all" ]; then
64 msg
="$msg --all${origpattern:+ $origpattern}"
70 $tg tag
--quiet -m "$msg" --stash "$stashb" || die
"requested --stash failed"
75 $tg update
${skip:+--skip}
77 [ $_ret -eq 3 ] && exit 3
81 # run git merge with the passed in arguments AND --no-stat
82 # return the exit status of git merge
83 # if the returned exit status is no error show a shortstat before
84 # returning assuming the merge was done into the previous HEAD
86 _oldhead
="$(git rev-parse --verify HEAD^0)"
88 git merge
$auhopt --no-stat "$@" || _ret
=$?
89 [ "$_ret" != "0" ] || git
--no-pager diff --shortstat "$_oldhead" HEAD^
0 --
94 # We are cacheable until the first change
98 ## First, take care of our base
100 _depcheck
="$(get_temp tg-depcheck)"
102 needs_update
"$_update_name" >"$_depcheck" ||
:
103 if [ -n "$missing_deps" ]; then
104 msg
="Some dependencies are missing: $missing_deps"
105 if [ -n "$skip" ]; then
106 info
"$msg; skipping"
107 elif [ -z "$all" ]; then
110 info
"$msg; skipping branch $_update_name"
114 if [ -s "$_depcheck" ]; then
115 stash_now_if_requested
116 # We need to switch to the base branch
117 # ...but only if we aren't there yet (from failed previous merge)
118 _HEAD
="$(git symbolic-ref -q HEAD || :)"
119 if [ "$_HEAD" = "${_HEAD#refs/$topbases/}" ]; then
120 switch_to_base
"$_update_name"
124 sed 's/ [^ ]* *$//' |
# last is $_update_name
125 sed 's/.* \([^ ]*\)$/+\1/' |
# only immediate dependencies
126 sed 's/^\([^+]\)/-\1/' |
# now each line is +branch or -branch (+ == recurse)
127 uniq -s 1 |
# fold branch lines; + always comes before - and thus wins within uniq
128 while read depline
; do
130 action
="${depline%$dep}"
133 # We do not distinguish between dependencies out-of-date
134 # and base/remote out-of-date cases for $dep here,
135 # but thanks to needs_update returning : or refs/remotes/<remote>/<name>
136 # for the latter, we do correctly recurse here
139 if [ x
"$action" = x
+ ]; then
140 case " $missing_deps " in *" $dep "*)
141 info
"Skipping recursing to missing dependency: $dep"
144 info
"Recursing to $dep..."
145 git checkout
-q "$dep"
147 TG_RECURSIVE
="[$dep] $TG_RECURSIVE"
151 while ! recursive_update
; do
152 # The merge got stuck! Let the user fix it up.
153 info
"You are in a subshell. If you abort the merge,"
154 info
"use \`exit 1\` to abort the recursive update altogether."
155 info
"Use \`exit 2\` to skip updating this branch and continue."
156 if "${SHELL:-@SHELL_PATH@}" -i </dev
/tty
; then
157 # assume user fixed it
158 # we could be left on a detached HEAD if we were resolving
159 # a conflict while merging a base in, fix it with a checkout
160 git checkout
-q "$(strip_ref "$dep")"
164 if [ $ret -eq 2 ]; then
165 info
"Ok, I will try to continue without updating this branch."
168 info
"Ok, you aborted the merge. Now, you just need to"
169 info
"switch back to some sane branch using \`git$gitcdopt checkout\`."
176 switch_to_base
"$_update_name"
179 # This will be either a proper topic branch
180 # or a remote base. (branch_needs_update() is called
181 # only on the _dependencies_, not our branch itself!)
183 info
"Updating $_update_name base with $dep changes..."
184 case "$dep" in refs
/*) fulldep
="$dep";; *) fulldep
="refs/heads/$dep"; esac
185 if ! git_merge
-m "tgupdate: merge ${dep#refs/} into $topbases/$_update_name" "$fulldep^0"; then
186 if [ -z "$TG_RECURSIVE" ]; then
187 resume
="\`$tgdisplay update${skip:+ --skip} $_update_name\` again"
191 info
"Please commit merge resolution and call $resume."
192 info
"It is also safe to abort this operation using \`git$gitcdopt reset --hard\`,"
193 info
"but please remember that you are on the base branch now;"
194 info
"you will want to switch to some normal branch afterwards."
201 info
"The base is up-to-date."
204 # Home, sweet home...
205 # (We want to always switch back, in case we were on the base from failed
207 git checkout
-q "$_update_name"
209 merge_with
="refs/$topbases/$_update_name"
212 ## Second, update our head with the remote branch
215 if has_remote
"$_update_name"; then
216 _rname
="refs/remotes/$base_remote/$_update_name"
217 if branch_contains
"refs/heads/$_update_name" "$_rname"; then
218 info
"The $_update_name head is up-to-date wrt. its remote branch."
220 stash_now_if_requested
221 info
"Reconciling $_update_name base with remote branch updates..."
223 # *DETACH* our HEAD now!
224 git checkout
-q --detach "refs/$topbases/$_update_name"
225 if ! git_merge
-m "tgupdate: merge ${_rname#refs/} onto $topbases/$_update_name" "$_rname^0"; then
226 info
"Oops, you will need to help me out here a bit."
227 info
"Please commit merge resolution and call:"
228 info
"git$gitcdopt checkout $_update_name && git$gitcdopt merge <commitid>"
229 info
"It is also safe to abort this operation using: git$gitcdopt reset --hard $_update_name"
232 # Go back but remember we want to merge with this, not base
233 merge_with
="$(git rev-parse --verify HEAD --)"
234 plusextra
="${_rname#refs/}+"
235 git checkout
-q "$_update_name"
240 ## Third, update our head with the base
242 if branch_contains
"refs/heads/$_update_name" "$merge_with"; then
243 info
"The $_update_name head is up-to-date wrt. the base."
246 stash_now_if_requested
247 info
"Updating $_update_name against new base..."
249 if ! git_merge
-m "tgupdate: merge ${plusextra}$topbases/$_update_name into $_update_name" "$merge_with^0"; then
250 if [ -z "$TG_RECURSIVE" ]; then
251 info
"Please commit merge resolution. No need to do anything else"
252 info
"You can abort this operation using \`git$gitcdopt reset --hard\` now"
253 info
"and retry this merge later using \`$tgdisplay update${skip:+ --skip}\`."
255 info
"Please commit merge resolution and call exit."
256 info
"You can abort this operation using \`git$gitcdopt reset --hard\`."
262 # We are "read-only" and cacheable until the first change
266 [ -z "$all" ] && { update_branch
$name; exit; }
268 do_non_annihilated_branches_patterns
() {
269 while read -r _pat
&& [ -n "$_pat" ]; do
272 non_annihilated_branches
"$@"
275 do_non_annihilated_branches
() {
276 if [ -z "$pattern" ]; then
277 non_annihilated_branches
279 do_non_annihilated_branches_patterns
<<-EOT
280 $(sed 'y/ /\n/' <<-LIST
288 while read name
&& [ -n "$name" ]; do
289 info
"Proccessing $name..."
290 update_branch
"$name" ||
exit
292 $(do_non_annihilated_branches)
295 info
"Returning to $current..."
296 git checkout
-q "$current"