tg-update: correctly return error for missing deps
[topgit.git] / tg-update.sh
blobecbc0d56283e91468d248e5da4e9dd2734bc2527
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
12 ## Parse options
14 while [ -n "$1" ]; do
15 arg="$1"; shift
16 case "$arg" in
17 -a)
18 all=1;;
19 -*)
20 echo "Usage: tg [...] update [-a] [NAME|PATTERN ...]" >&2
21 exit 1;;
23 if [ -z "$all" ]; then
24 [ -z "$name" ] || die "name already specified ($name)"
25 name="$arg"
26 else
27 pattern="$pattern refs/top-bases/${arg#refs/top-bases/}"
30 esac
31 done
32 [ -z "$pattern" ] && pattern=refs/top-bases
34 current="$(git symbolic-ref HEAD | sed 's#^refs/\(heads\|top-bases\)/##')"
35 if [ -z "$all" -a -z "$name" ]; then
36 name="$current"
37 base_rev="$(git rev-parse --short --verify "refs/top-bases/$name" 2>/dev/null)" ||
38 die "not a TopGit-controlled branch"
41 update_branch() {
42 local name="$1" base_rev depcheck missing_deps HEAD
43 ## First, take care of our base
45 depcheck="$(get_temp tg-depcheck)"
46 missing_deps=
47 needs_update "$name" >"$depcheck" || :
48 if [ -n "$missing_deps" ]; then
49 if [ -z "$all" ]; then
50 die "some dependencies are missing: $missing_deps"
51 else
52 info "some dependencies are missing: $missing_deps; skipping"
53 return
56 if [ -s "$depcheck" ]; then
57 # We need to switch to the base branch
58 # ...but only if we aren't there yet (from failed previous merge)
59 HEAD="$(git symbolic-ref HEAD)"
60 if [ "$HEAD" = "${HEAD#refs/top-bases/}" ]; then
61 switch_to_base "$name"
64 cat "$depcheck" |
65 sed 's/ [^ ]* *$//' | # last is $name
66 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
67 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
68 uniq -s 1 | # fold branch lines; + always comes before - and thus wins within uniq
69 while read depline; do
70 action="$(echo "$depline" | cut -c 1)"
71 dep="$(echo "$depline" | cut -c 2-)"
73 # We do not distinguish between dependencies out-of-date
74 # and base/remote out-of-date cases for $dep here,
75 # but thanks to needs_update returning : or %
76 # for the latter, we do correctly recurse here
77 # in both cases.
79 if [ x"$action" = x+ ]; then
80 info "Recursing to $dep..."
81 git checkout -q "$dep"
83 export TG_RECURSIVE="[$dep] $TG_RECURSIVE"
84 export PS1="[$dep] $PS1"
85 while ! $tg update; do
86 # The merge got stuck! Let the user fix it up.
87 info "You are in a subshell. If you abort the merge,"
88 info "use \`exit 1\` to abort the recursive update altogether."
89 info "Use \`exit 2\` to skip updating this branch and continue."
90 if sh -i </dev/tty; then
91 # assume user fixed it
92 continue
93 else
94 ret=$?
95 if [ $ret -eq 2 ]; then
96 info "Ok, I will try to continue without updating this branch."
97 break
98 else
99 info "Ok, you aborted the merge. Now, you just need to"
100 info "switch back to some sane branch using \`git checkout\`."
101 exit 3
104 done
106 switch_to_base "$name"
109 # This will be either a proper topic branch
110 # or a remote base. (branch_needs_update() is called
111 # only on the _dependencies_, not our branch itself!)
113 info "Updating base with $dep changes..."
114 if ! git merge "$dep"; then
115 if [ -z "$TG_RECURSIVE" ]; then
116 resume="\`git checkout $name && $tg update\` again"
117 else # subshell
118 resume='exit'
120 info "Please commit merge resolution and call $resume."
121 info "It is also safe to abort this operation using \`git reset --hard\`,"
122 info "but please remember that you are on the base branch now;"
123 info "you will want to switch to some normal branch afterwards."
124 rm "$depcheck"
125 exit 2
127 done
128 else
129 info "The base is up-to-date."
132 # Home, sweet home...
133 # (We want to always switch back, in case we were on the base from failed
134 # previous merge.)
135 git checkout -q "$name"
137 merge_with="refs/top-bases/$name"
140 ## Second, update our head with the remote branch
142 if has_remote "$name"; then
143 rname="refs/remotes/$base_remote/$name"
144 if branch_contains "$name" "$rname"; then
145 info "The $name head is up-to-date wrt. its remote branch."
146 else
147 info "Reconciling remote branch updates with $name base..."
148 # *DETACH* our HEAD now!
149 git checkout -q "refs/top-bases/$name"
150 if ! git merge "$rname"; then
151 info "Oops, you will need to help me out here a bit."
152 info "Please commit merge resolution and call:"
153 info "git checkout $name && git merge <commitid>"
154 info "It is also safe to abort this operation using: git reset --hard $name"
155 exit 3
157 # Go back but remember we want to merge with this, not base
158 merge_with="$(git rev-parse HEAD)"
159 git checkout -q "$name"
164 ## Third, update our head with the base
166 if branch_contains "$name" "$merge_with"; then
167 info "The $name head is up-to-date wrt. the base."
168 return 0
170 info "Updating $name against new base..."
171 if ! git merge "$merge_with"; then
172 if [ -z "$TG_RECURSIVE" ]; then
173 info "Please commit merge resolution. No need to do anything else"
174 info "You can abort this operation using \`git reset --hard\` now"
175 info "and retry this merge later using \`$tg update\`."
176 else # subshell
177 info "Please commit merge resolution and call exit."
178 info "You can abort this operation using \`git reset --hard\`."
180 exit 3
184 [ -z "$all" ] && { update_branch $name; exit; }
186 git for-each-ref $pattern |
187 while read rev type ref; do
188 name="${ref#refs/top-bases/}"
189 if branch_annihilated "$name"; then
190 continue;
192 info "Procesing $name..."
193 update_branch "$name" || exit
194 done
196 info "Returning to $current..."
197 git checkout -q "$current"
198 # vim:noet