Bumped version to 0.9; last minute doc changes
[topgit.git] / tg-update.sh
bloba6494f9f365d19f8bed7b3e772f32ff0f68ef645
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 ([<name>] | -a [<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 2>/dev/null | sed 's#^refs/\(heads\|top-bases\)/##')"
35 if [ -z "$all" ]; then
36 if [ -z "$name" ]; then
37 name="$current"
38 base_rev="$(git rev-parse --short --verify "refs/top-bases/$name" 2>/dev/null)" ||
39 die "not a TopGit-controlled branch"
41 else
42 [ -n "$current" ] || die "cannot return to detached tree; switch to another branch"
45 update_branch() {
46 local name="$1" base_rev depcheck missing_deps HEAD
47 ## First, take care of our base
49 depcheck="$(get_temp tg-depcheck)"
50 missing_deps=
51 needs_update "$name" >"$depcheck" || :
52 if [ -n "$missing_deps" ]; then
53 if [ -z "$all" ]; then
54 die "some dependencies are missing: $missing_deps"
55 else
56 info "some dependencies are missing: $missing_deps; skipping"
57 return
60 if [ -s "$depcheck" ]; then
61 # We need to switch to the base branch
62 # ...but only if we aren't there yet (from failed previous merge)
63 HEAD="$(git symbolic-ref HEAD)"
64 if [ "$HEAD" = "${HEAD#refs/top-bases/}" ]; then
65 switch_to_base "$name"
68 cat "$depcheck" |
69 sed 's/ [^ ]* *$//' | # last is $name
70 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
71 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
72 uniq -s 1 | # fold branch lines; + always comes before - and thus wins within uniq
73 while read depline; do
74 action="$(echo "$depline" | cut -c 1)"
75 dep="$(echo "$depline" | cut -c 2-)"
77 # We do not distinguish between dependencies out-of-date
78 # and base/remote out-of-date cases for $dep here,
79 # but thanks to needs_update returning : or %
80 # for the latter, we do correctly recurse here
81 # in both cases.
83 if [ x"$action" = x+ ]; then
84 info "Recursing to $dep..."
85 git checkout -q "$dep"
87 export TG_RECURSIVE="[$dep] $TG_RECURSIVE"
88 export PS1="[$dep] $PS1"
89 while ! $tg update; do
90 # The merge got stuck! Let the user fix it up.
91 info "You are in a subshell. If you abort the merge,"
92 info "use \`exit 1\` to abort the recursive update altogether."
93 info "Use \`exit 2\` to skip updating this branch and continue."
94 if sh -i </dev/tty; then
95 # assume user fixed it
96 continue
97 else
98 ret=$?
99 if [ $ret -eq 2 ]; then
100 info "Ok, I will try to continue without updating this branch."
101 break
102 else
103 info "Ok, you aborted the merge. Now, you just need to"
104 info "switch back to some sane branch using \`git checkout\`."
105 exit 3
108 done
110 switch_to_base "$name"
113 # This will be either a proper topic branch
114 # or a remote base. (branch_needs_update() is called
115 # only on the _dependencies_, not our branch itself!)
117 info "Updating base with $dep changes..."
118 if ! git merge "$dep"; then
119 if [ -z "$TG_RECURSIVE" ]; then
120 resume="\`git checkout $name && $tg update\` again"
121 else # subshell
122 resume='exit'
124 info "Please commit merge resolution and call $resume."
125 info "It is also safe to abort this operation using \`git reset --hard\`,"
126 info "but please remember that you are on the base branch now;"
127 info "you will want to switch to some normal branch afterwards."
128 rm "$depcheck"
129 exit 2
131 done
132 else
133 info "The base is up-to-date."
136 # Home, sweet home...
137 # (We want to always switch back, in case we were on the base from failed
138 # previous merge.)
139 git checkout -q "$name"
141 merge_with="refs/top-bases/$name"
144 ## Second, update our head with the remote branch
146 if has_remote "$name"; then
147 rname="refs/remotes/$base_remote/$name"
148 if branch_contains "$name" "$rname"; then
149 info "The $name head is up-to-date wrt. its remote branch."
150 else
151 info "Reconciling remote branch updates with $name base..."
152 # *DETACH* our HEAD now!
153 git checkout -q "refs/top-bases/$name"
154 if ! git merge "$rname"; then
155 info "Oops, you will need to help me out here a bit."
156 info "Please commit merge resolution and call:"
157 info "git checkout $name && git merge <commitid>"
158 info "It is also safe to abort this operation using: git reset --hard $name"
159 exit 3
161 # Go back but remember we want to merge with this, not base
162 merge_with="$(git rev-parse HEAD)"
163 git checkout -q "$name"
168 ## Third, update our head with the base
170 if branch_contains "$name" "$merge_with"; then
171 info "The $name head is up-to-date wrt. the base."
172 return 0
174 info "Updating $name against new base..."
175 if ! git merge "$merge_with"; then
176 if [ -z "$TG_RECURSIVE" ]; then
177 info "Please commit merge resolution. No need to do anything else"
178 info "You can abort this operation using \`git reset --hard\` now"
179 info "and retry this merge later using \`$tg update\`."
180 else # subshell
181 info "Please commit merge resolution and call exit."
182 info "You can abort this operation using \`git reset --hard\`."
184 exit 3
188 [ -z "$all" ] && { update_branch $name; exit; }
190 git for-each-ref $pattern |
191 while read rev type ref; do
192 name="${ref#refs/top-bases/}"
193 if branch_annihilated "$name"; then
194 continue;
196 info "Procesing $name..."
197 update_branch "$name" || exit
198 done
200 info "Returning to $current..."
201 git checkout -q "$current"
202 # vim:noet