README: mention export --collapse + git format-patch
[topgit/pro.git] / tg-update.sh
blobb8da932a3df9b10fb5e5e903d64bc4faed9a6a88
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 export TG_RECURSIVE="[$dep] $TG_RECURSIVE"
102 export PS1="[$dep] $PS1"
103 while ! recursive_update; do
104 # The merge got stuck! Let the user fix it up.
105 info "You are in a subshell. If you abort the merge,"
106 info "use \`exit 1\` to abort the recursive update altogether."
107 info "Use \`exit 2\` to skip updating this branch and continue."
108 if "${SHELL:-/bin/sh}" -i </dev/tty; then
109 # assume user fixed it
110 # we could be left on a detached HEAD if we were resolving
111 # a conflict while merging a base in, fix it with a checkout
112 git checkout -q "$(strip_ref "$dep")"
113 continue
114 else
115 ret=$?
116 if [ $ret -eq 2 ]; then
117 info "Ok, I will try to continue without updating this branch."
118 break
119 else
120 info "Ok, you aborted the merge. Now, you just need to"
121 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
122 exit 3
125 done
127 switch_to_base "$_update_name"
130 # This will be either a proper topic branch
131 # or a remote base. (branch_needs_update() is called
132 # only on the _dependencies_, not our branch itself!)
134 info "Updating base with $dep changes..."
135 if ! git merge "$dep"; then
136 if [ -z "$TG_RECURSIVE" ]; then
137 resume="\`$tgdisplay update${skip:+ --skip} $_update_name\` again"
138 else # subshell
139 resume='exit'
141 info "Please commit merge resolution and call $resume."
142 info "It is also safe to abort this operation using \`git$gitcdopt reset --hard\`,"
143 info "but please remember that you are on the base branch now;"
144 info "you will want to switch to some normal branch afterwards."
145 rm "$_depcheck"
146 exit 2
148 done
149 else
150 info "The base is up-to-date."
153 # Home, sweet home...
154 # (We want to always switch back, in case we were on the base from failed
155 # previous merge.)
156 git checkout -q "$_update_name"
158 merge_with="refs/top-bases/$_update_name"
161 ## Second, update our head with the remote branch
163 if has_remote "$_update_name"; then
164 _rname="refs/remotes/$base_remote/$_update_name"
165 if branch_contains "$_update_name" "$_rname"; then
166 info "The $_update_name head is up-to-date wrt. its remote branch."
167 else
168 info "Reconciling remote branch updates with $_update_name base..."
169 # *DETACH* our HEAD now!
170 git checkout -q "refs/top-bases/$_update_name"
171 if ! git merge "$_rname"; then
172 info "Oops, you will need to help me out here a bit."
173 info "Please commit merge resolution and call:"
174 info "git$gitcdopt checkout $_update_name && git$gitcdopt merge <commitid>"
175 info "It is also safe to abort this operation using: git$gitcdopt reset --hard $_update_name"
176 exit 4
178 # Go back but remember we want to merge with this, not base
179 merge_with="$(git rev-parse HEAD)"
180 git checkout -q "$_update_name"
185 ## Third, update our head with the base
187 if branch_contains "$_update_name" "$merge_with"; then
188 info "The $_update_name head is up-to-date wrt. the base."
189 return 0
191 info "Updating $_update_name against new base..."
192 if ! git merge "$merge_with"; then
193 if [ -z "$TG_RECURSIVE" ]; then
194 info "Please commit merge resolution. No need to do anything else"
195 info "You can abort this operation using \`git$gitcdopt reset --hard\` now"
196 info "and retry this merge later using \`$tgdisplay update${skip:+ --skip}\`."
197 else # subshell
198 info "Please commit merge resolution and call exit."
199 info "You can abort this operation using \`git$gitcdopt reset --hard\`."
201 exit 4
205 [ -z "$all" ] && { update_branch $name; exit; }
207 non_annihilated_branches $pattern |
208 while read name; do
209 info "Proccessing $name..."
210 update_branch "$name" || exit
211 done
213 info "Returning to $current..."
214 git checkout -q "$current"
215 # vim:noet