scripts: use more portable export
[topgit/pro.git] / tg-update.sh
blob1285b7b53523b50842a2b161318c9ddf7b4082f6
1 #!/bin/sh
2 # TopGit - A different patch queue manager
3 # (c) Petr Baudis <pasky@suse.cz> 2008
4 # GPLv2
6 name= # Branch to update
7 all= # Update all branches
8 pattern= # Branch selection filter for -a
9 current= # Branch we are currently on
10 skip= # skip missing dependencies
12 ## Parse options
14 while [ -n "$1" ]; do
15 arg="$1"; shift
16 case "$arg" in
17 -a)
18 all=1;;
19 --skip)
20 skip=1;;
21 -*)
22 echo "Usage: ${tgname:-tg} [...] update [--skip] ([<name>] | -a [<pattern>...])" >&2
23 exit 1;;
25 if [ -z "$all" ]; then
26 [ -z "$name" ] || die "name already specified ($name)"
27 name="$arg"
28 else
29 pattern="$pattern refs/top-bases/${arg#refs/top-bases/}"
32 esac
33 done
34 [ -z "$pattern" ] && pattern=refs/top-bases
36 current="$(strip_ref "$(git symbolic-ref HEAD 2>/dev/null)")"
37 if [ -z "$all" ]; then
38 name="$(verify_topgit_branch "${name:-HEAD}")"
39 else
40 [ -n "$current" ] || die "cannot return to detached tree; switch to another branch"
43 ensure_clean_tree
45 recursive_update() {
46 $tg update ${skip:+--skip}
47 _ret=$?
48 [ $_ret -eq 3 ] && exit 3
49 return $_ret
52 update_branch() {
53 _update_name="$1"
54 ## First, take care of our base
56 _depcheck="$(get_temp tg-depcheck)"
57 missing_deps=
58 needs_update "$_update_name" >"$_depcheck" || :
59 if [ -n "$missing_deps" ]; then
60 msg="Some dependencies are missing: $missing_deps"
61 if [ -n "$skip" ]; then
62 info "$msg; skipping"
63 elif [ -z "$all" ]; then
64 die "$msg"
65 else
66 info "$msg; skipping branch $_update_name"
67 return
70 if [ -s "$_depcheck" ]; then
71 # We need to switch to the base branch
72 # ...but only if we aren't there yet (from failed previous merge)
73 _HEAD="$(git symbolic-ref HEAD)"
74 if [ "$_HEAD" = "${_HEAD#refs/top-bases/}" ]; then
75 switch_to_base "$_update_name"
78 cat "$_depcheck" |
79 sed 's/ [^ ]* *$//' | # last is $_update_name
80 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
81 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
82 uniq -s 1 | # fold branch lines; + always comes before - and thus wins within uniq
83 while read depline; do
84 action="$(echo "$depline" | cut -c 1)"
85 dep="$(echo "$depline" | cut -c 2-)"
87 # We do not distinguish between dependencies out-of-date
88 # and base/remote out-of-date cases for $dep here,
89 # but thanks to needs_update returning : or %
90 # for the latter, we do correctly recurse here
91 # in both cases.
93 if [ x"$action" = x+ ]; then
94 case " $missing_deps " in *" $dep "*)
95 info "Skipping recursing to missing dependency: $dep"
96 continue
97 esac
98 info "Recursing to $dep..."
99 git checkout -q "$dep"
101 TG_RECURSIVE="[$dep] $TG_RECURSIVE"
102 PS1="[$dep] $PS1"
103 export TG_RECURSIVE
104 export PS1
105 while ! recursive_update; do
106 # The merge got stuck! Let the user fix it up.
107 info "You are in a subshell. If you abort the merge,"
108 info "use \`exit 1\` to abort the recursive update altogether."
109 info "Use \`exit 2\` to skip updating this branch and continue."
110 if "${SHELL:-/bin/sh}" -i </dev/tty; then
111 # assume user fixed it
112 # we could be left on a detached HEAD if we were resolving
113 # a conflict while merging a base in, fix it with a checkout
114 git checkout -q "$(strip_ref "$dep")"
115 continue
116 else
117 ret=$?
118 if [ $ret -eq 2 ]; then
119 info "Ok, I will try to continue without updating this branch."
120 break
121 else
122 info "Ok, you aborted the merge. Now, you just need to"
123 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
124 exit 3
127 done
129 switch_to_base "$_update_name"
132 # This will be either a proper topic branch
133 # or a remote base. (branch_needs_update() is called
134 # only on the _dependencies_, not our branch itself!)
136 info "Updating base with $dep changes..."
137 if ! git merge "$dep"; then
138 if [ -z "$TG_RECURSIVE" ]; then
139 resume="\`$tgdisplay update${skip:+ --skip} $_update_name\` again"
140 else # subshell
141 resume='exit'
143 info "Please commit merge resolution and call $resume."
144 info "It is also safe to abort this operation using \`git$gitcdopt reset --hard\`,"
145 info "but please remember that you are on the base branch now;"
146 info "you will want to switch to some normal branch afterwards."
147 rm "$_depcheck"
148 exit 2
150 done
151 else
152 info "The base is up-to-date."
155 # Home, sweet home...
156 # (We want to always switch back, in case we were on the base from failed
157 # previous merge.)
158 git checkout -q "$_update_name"
160 merge_with="refs/top-bases/$_update_name"
163 ## Second, update our head with the remote branch
165 if has_remote "$_update_name"; then
166 _rname="refs/remotes/$base_remote/$_update_name"
167 if branch_contains "$_update_name" "$_rname"; then
168 info "The $_update_name head is up-to-date wrt. its remote branch."
169 else
170 info "Reconciling remote branch updates with $_update_name base..."
171 # *DETACH* our HEAD now!
172 git checkout -q "refs/top-bases/$_update_name"
173 if ! git merge "$_rname"; then
174 info "Oops, you will need to help me out here a bit."
175 info "Please commit merge resolution and call:"
176 info "git$gitcdopt checkout $_update_name && git$gitcdopt merge <commitid>"
177 info "It is also safe to abort this operation using: git$gitcdopt reset --hard $_update_name"
178 exit 4
180 # Go back but remember we want to merge with this, not base
181 merge_with="$(git rev-parse HEAD)"
182 git checkout -q "$_update_name"
187 ## Third, update our head with the base
189 if branch_contains "$_update_name" "$merge_with"; then
190 info "The $_update_name head is up-to-date wrt. the base."
191 return 0
193 info "Updating $_update_name against new base..."
194 if ! git merge "$merge_with"; then
195 if [ -z "$TG_RECURSIVE" ]; then
196 info "Please commit merge resolution. No need to do anything else"
197 info "You can abort this operation using \`git$gitcdopt reset --hard\` now"
198 info "and retry this merge later using \`$tgdisplay update${skip:+ --skip}\`."
199 else # subshell
200 info "Please commit merge resolution and call exit."
201 info "You can abort this operation using \`git$gitcdopt reset --hard\`."
203 exit 4
207 [ -z "$all" ] && { update_branch $name; exit; }
209 non_annihilated_branches $pattern |
210 while read name; do
211 info "Proccessing $name..."
212 update_branch "$name" || exit
213 done
215 info "Returning to $current..."
216 git checkout -q "$current"
217 # vim:noet