Set version to 0.12
[topgit/pro.git] / tg-update.sh
blob79a1d4ff87e5ec6afc4997b00d47499f96c6ea2b
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="$(strip_ref "$(git symbolic-ref HEAD 2>/dev/null)")"
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 ensure_clean_tree
47 recursive_update() {
48 $tg update
49 _ret=$?
50 [ $_ret -eq 3 ] && exit 3
51 return $_ret
54 update_branch() {
55 _update_name="$1"
56 ## First, take care of our base
58 _depcheck="$(get_temp tg-depcheck)"
59 missing_deps=
60 needs_update "$_update_name" >"$_depcheck" || :
61 if [ -n "$missing_deps" ]; then
62 if [ -z "$all" ]; then
63 die "some dependencies are missing: $missing_deps"
64 else
65 info "some dependencies are missing: $missing_deps; skipping"
66 return
69 if [ -s "$_depcheck" ]; then
70 # We need to switch to the base branch
71 # ...but only if we aren't there yet (from failed previous merge)
72 _HEAD="$(git symbolic-ref HEAD)"
73 if [ "$_HEAD" = "${_HEAD#refs/top-bases/}" ]; then
74 switch_to_base "$_update_name"
77 cat "$_depcheck" |
78 sed 's/ [^ ]* *$//' | # last is $_update_name
79 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
80 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
81 uniq -s 1 | # fold branch lines; + always comes before - and thus wins within uniq
82 while read depline; do
83 action="$(echo "$depline" | cut -c 1)"
84 dep="$(echo "$depline" | cut -c 2-)"
86 # We do not distinguish between dependencies out-of-date
87 # and base/remote out-of-date cases for $dep here,
88 # but thanks to needs_update returning : or %
89 # for the latter, we do correctly recurse here
90 # in both cases.
92 if [ x"$action" = x+ ]; then
93 info "Recursing to $dep..."
94 git checkout -q "$dep"
96 export TG_RECURSIVE="[$dep] $TG_RECURSIVE"
97 export PS1="[$dep] $PS1"
98 while ! recursive_update; do
99 # The merge got stuck! Let the user fix it up.
100 info "You are in a subshell. If you abort the merge,"
101 info "use \`exit 1\` to abort the recursive update altogether."
102 info "Use \`exit 2\` to skip updating this branch and continue."
103 if sh -i </dev/tty; then
104 # assume user fixed it
105 continue
106 else
107 ret=$?
108 if [ $ret -eq 2 ]; then
109 info "Ok, I will try to continue without updating this branch."
110 break
111 else
112 info "Ok, you aborted the merge. Now, you just need to"
113 info "switch back to some sane branch using \`git checkout\`."
114 exit 3
117 done
119 switch_to_base "$_update_name"
122 # This will be either a proper topic branch
123 # or a remote base. (branch_needs_update() is called
124 # only on the _dependencies_, not our branch itself!)
126 info "Updating base with $dep changes..."
127 if ! git merge "$dep"; then
128 if [ -z "$TG_RECURSIVE" ]; then
129 resume="\`git checkout $_update_name && $tg update\` again"
130 else # subshell
131 resume='exit'
133 info "Please commit merge resolution and call $resume."
134 info "It is also safe to abort this operation using \`git reset --hard\`,"
135 info "but please remember that you are on the base branch now;"
136 info "you will want to switch to some normal branch afterwards."
137 rm "$_depcheck"
138 exit 2
140 done
141 else
142 info "The base is up-to-date."
145 # Home, sweet home...
146 # (We want to always switch back, in case we were on the base from failed
147 # previous merge.)
148 git checkout -q "$_update_name"
150 merge_with="refs/top-bases/$_update_name"
153 ## Second, update our head with the remote branch
155 if has_remote "$_update_name"; then
156 _rname="refs/remotes/$base_remote/$_update_name"
157 if branch_contains "$_update_name" "$_rname"; then
158 info "The $_update_name head is up-to-date wrt. its remote branch."
159 else
160 info "Reconciling remote branch updates with $_update_name base..."
161 # *DETACH* our HEAD now!
162 git checkout -q "refs/top-bases/$_update_name"
163 if ! git merge "$_rname"; then
164 info "Oops, you will need to help me out here a bit."
165 info "Please commit merge resolution and call:"
166 info "git checkout $_update_name && git merge <commitid>"
167 info "It is also safe to abort this operation using: git reset --hard $_update_name"
168 exit 4
170 # Go back but remember we want to merge with this, not base
171 merge_with="$(git rev-parse HEAD)"
172 git checkout -q "$_update_name"
177 ## Third, update our head with the base
179 if branch_contains "$_update_name" "$merge_with"; then
180 info "The $_update_name head is up-to-date wrt. the base."
181 return 0
183 info "Updating $_update_name against new base..."
184 if ! git merge "$merge_with"; then
185 if [ -z "$TG_RECURSIVE" ]; then
186 info "Please commit merge resolution. No need to do anything else"
187 info "You can abort this operation using \`git reset --hard\` now"
188 info "and retry this merge later using \`$tg update\`."
189 else # subshell
190 info "Please commit merge resolution and call exit."
191 info "You can abort this operation using \`git reset --hard\`."
193 exit 4
197 [ -z "$all" ] && { update_branch $name; exit; }
199 non_annihilated_branches $pattern |
200 while read name; do
201 info "Proccessing $name..."
202 update_branch "$name" || exit
203 done
205 info "Returning to $current..."
206 git checkout -q "$current"
207 # vim:noet