tg.sh: set LESS to -FRX by default instead of -FRSX
[topgit/pro.git] / tg-update.sh
blob6f58c3e04e67ea09fdbfeea1cb59067398013886
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 # We are cacheable until the first change
54 become_cacheable
56 _update_name="$1"
57 ## First, take care of our base
59 _depcheck="$(get_temp tg-depcheck)"
60 missing_deps=
61 needs_update "$_update_name" >"$_depcheck" || :
62 if [ -n "$missing_deps" ]; then
63 msg="Some dependencies are missing: $missing_deps"
64 if [ -n "$skip" ]; then
65 info "$msg; skipping"
66 elif [ -z "$all" ]; then
67 die "$msg"
68 else
69 info "$msg; skipping branch $_update_name"
70 return
73 if [ -s "$_depcheck" ]; then
74 # We need to switch to the base branch
75 # ...but only if we aren't there yet (from failed previous merge)
76 _HEAD="$(git symbolic-ref HEAD)"
77 if [ "$_HEAD" = "${_HEAD#refs/top-bases/}" ]; then
78 switch_to_base "$_update_name"
81 cat "$_depcheck" |
82 sed 's/ [^ ]* *$//' | # last is $_update_name
83 sed 's/.* \([^ ]*\)$/+\1/' | # only immediate dependencies
84 sed 's/^\([^+]\)/-\1/' | # now each line is +branch or -branch (+ == recurse)
85 uniq -s 1 | # fold branch lines; + always comes before - and thus wins within uniq
86 while read depline; do
87 action="$(echo "$depline" | cut -c 1)"
88 dep="$(echo "$depline" | cut -c 2-)"
89 become_non_cacheable
91 # We do not distinguish between dependencies out-of-date
92 # and base/remote out-of-date cases for $dep here,
93 # but thanks to needs_update returning : or %
94 # for the latter, we do correctly recurse here
95 # in both cases.
97 if [ x"$action" = x+ ]; then
98 case " $missing_deps " in *" $dep "*)
99 info "Skipping recursing to missing dependency: $dep"
100 continue
101 esac
102 info "Recursing to $dep..."
103 git checkout -q "$dep"
105 TG_RECURSIVE="[$dep] $TG_RECURSIVE"
106 PS1="[$dep] $PS1"
107 export TG_RECURSIVE
108 export PS1
109 while ! recursive_update; do
110 # The merge got stuck! Let the user fix it up.
111 info "You are in a subshell. If you abort the merge,"
112 info "use \`exit 1\` to abort the recursive update altogether."
113 info "Use \`exit 2\` to skip updating this branch and continue."
114 if "${SHELL:-/bin/sh}" -i </dev/tty; then
115 # assume user fixed it
116 # we could be left on a detached HEAD if we were resolving
117 # a conflict while merging a base in, fix it with a checkout
118 git checkout -q "$(strip_ref "$dep")"
119 continue
120 else
121 ret=$?
122 if [ $ret -eq 2 ]; then
123 info "Ok, I will try to continue without updating this branch."
124 break
125 else
126 info "Ok, you aborted the merge. Now, you just need to"
127 info "switch back to some sane branch using \`git$gitcdopt checkout\`."
128 exit 3
131 done
133 switch_to_base "$_update_name"
136 # This will be either a proper topic branch
137 # or a remote base. (branch_needs_update() is called
138 # only on the _dependencies_, not our branch itself!)
140 info "Updating base with $dep changes..."
141 if ! git merge "$dep"; then
142 if [ -z "$TG_RECURSIVE" ]; then
143 resume="\`$tgdisplay update${skip:+ --skip} $_update_name\` again"
144 else # subshell
145 resume='exit'
147 info "Please commit merge resolution and call $resume."
148 info "It is also safe to abort this operation using \`git$gitcdopt reset --hard\`,"
149 info "but please remember that you are on the base branch now;"
150 info "you will want to switch to some normal branch afterwards."
151 rm "$_depcheck"
152 exit 2
154 done
155 else
156 info "The base is up-to-date."
159 # Home, sweet home...
160 # (We want to always switch back, in case we were on the base from failed
161 # previous merge.)
162 git checkout -q "$_update_name"
164 merge_with="refs/top-bases/$_update_name"
167 ## Second, update our head with the remote branch
169 if has_remote "$_update_name"; then
170 _rname="refs/remotes/$base_remote/$_update_name"
171 if branch_contains "refs/heads/$_update_name" "$_rname"; then
172 info "The $_update_name head is up-to-date wrt. its remote branch."
173 else
174 info "Reconciling remote branch updates with $_update_name base..."
175 become_non_cacheable
176 # *DETACH* our HEAD now!
177 git checkout -q "refs/top-bases/$_update_name"
178 if ! git merge "$_rname"; then
179 info "Oops, you will need to help me out here a bit."
180 info "Please commit merge resolution and call:"
181 info "git$gitcdopt checkout $_update_name && git$gitcdopt merge <commitid>"
182 info "It is also safe to abort this operation using: git$gitcdopt reset --hard $_update_name"
183 exit 4
185 # Go back but remember we want to merge with this, not base
186 merge_with="$(git rev-parse HEAD)"
187 git checkout -q "$_update_name"
192 ## Third, update our head with the base
194 if branch_contains "refs/heads/$_update_name" "$merge_with"; then
195 info "The $_update_name head is up-to-date wrt. the base."
196 return 0
198 info "Updating $_update_name against new base..."
199 become_non_cacheable
200 if ! git merge "$merge_with"; then
201 if [ -z "$TG_RECURSIVE" ]; then
202 info "Please commit merge resolution. No need to do anything else"
203 info "You can abort this operation using \`git$gitcdopt reset --hard\` now"
204 info "and retry this merge later using \`$tgdisplay update${skip:+ --skip}\`."
205 else # subshell
206 info "Please commit merge resolution and call exit."
207 info "You can abort this operation using \`git$gitcdopt reset --hard\`."
209 exit 4
213 # We are "read-only" and cacheable until the first change
214 tg_read_only=1
215 create_ref_cache
217 [ -z "$all" ] && { update_branch $name; exit; }
219 non_annihilated_branches $pattern |
220 while read name; do
221 info "Proccessing $name..."
222 update_branch "$name" || exit
223 done
225 info "Returning to $current..."
226 git checkout -q "$current"
227 # vim:noet