##// END OF EJS Templates
show: use the new stack definition for show stack...
Boris Feld -
r37020:a7219879 default
parent child Browse files
Show More
@@ -1,414 +1,413
1 # destutil.py - Mercurial utility function for command destination
1 # destutil.py - Mercurial utility function for command destination
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com> and other
3 # Copyright Matt Mackall <mpm@selenic.com> and other
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .i18n import _
10 from .i18n import _
11 from . import (
11 from . import (
12 bookmarks,
12 bookmarks,
13 error,
13 error,
14 obsutil,
14 obsutil,
15 scmutil,
15 scmutil,
16 stack
16 )
17 )
17
18
18 def _destupdateobs(repo, clean):
19 def _destupdateobs(repo, clean):
19 """decide of an update destination from obsolescence markers"""
20 """decide of an update destination from obsolescence markers"""
20 node = None
21 node = None
21 wc = repo[None]
22 wc = repo[None]
22 p1 = wc.p1()
23 p1 = wc.p1()
23 movemark = None
24 movemark = None
24
25
25 if p1.obsolete() and not p1.children():
26 if p1.obsolete() and not p1.children():
26 # allow updating to successors
27 # allow updating to successors
27 successors = obsutil.successorssets(repo, p1.node())
28 successors = obsutil.successorssets(repo, p1.node())
28
29
29 # behavior of certain cases is as follows,
30 # behavior of certain cases is as follows,
30 #
31 #
31 # divergent changesets: update to highest rev, similar to what
32 # divergent changesets: update to highest rev, similar to what
32 # is currently done when there are more than one head
33 # is currently done when there are more than one head
33 # (i.e. 'tip')
34 # (i.e. 'tip')
34 #
35 #
35 # replaced changesets: same as divergent except we know there
36 # replaced changesets: same as divergent except we know there
36 # is no conflict
37 # is no conflict
37 #
38 #
38 # pruned changeset: no update is done; though, we could
39 # pruned changeset: no update is done; though, we could
39 # consider updating to the first non-obsolete parent,
40 # consider updating to the first non-obsolete parent,
40 # similar to what is current done for 'hg prune'
41 # similar to what is current done for 'hg prune'
41
42
42 if successors:
43 if successors:
43 # flatten the list here handles both divergent (len > 1)
44 # flatten the list here handles both divergent (len > 1)
44 # and the usual case (len = 1)
45 # and the usual case (len = 1)
45 successors = [n for sub in successors for n in sub]
46 successors = [n for sub in successors for n in sub]
46
47
47 # get the max revision for the given successors set,
48 # get the max revision for the given successors set,
48 # i.e. the 'tip' of a set
49 # i.e. the 'tip' of a set
49 node = repo.revs('max(%ln)', successors).first()
50 node = repo.revs('max(%ln)', successors).first()
50 if bookmarks.isactivewdirparent(repo):
51 if bookmarks.isactivewdirparent(repo):
51 movemark = repo['.'].node()
52 movemark = repo['.'].node()
52 return node, movemark, None
53 return node, movemark, None
53
54
54 def _destupdatebook(repo, clean):
55 def _destupdatebook(repo, clean):
55 """decide on an update destination from active bookmark"""
56 """decide on an update destination from active bookmark"""
56 # we also move the active bookmark, if any
57 # we also move the active bookmark, if any
57 activemark = None
58 activemark = None
58 node, movemark = bookmarks.calculateupdate(repo.ui, repo, None)
59 node, movemark = bookmarks.calculateupdate(repo.ui, repo, None)
59 if node is not None:
60 if node is not None:
60 activemark = node
61 activemark = node
61 return node, movemark, activemark
62 return node, movemark, activemark
62
63
63 def _destupdatebranch(repo, clean):
64 def _destupdatebranch(repo, clean):
64 """decide on an update destination from current branch
65 """decide on an update destination from current branch
65
66
66 This ignores closed branch heads.
67 This ignores closed branch heads.
67 """
68 """
68 wc = repo[None]
69 wc = repo[None]
69 movemark = node = None
70 movemark = node = None
70 currentbranch = wc.branch()
71 currentbranch = wc.branch()
71
72
72 if clean:
73 if clean:
73 currentbranch = repo['.'].branch()
74 currentbranch = repo['.'].branch()
74
75
75 if currentbranch in repo.branchmap():
76 if currentbranch in repo.branchmap():
76 heads = repo.branchheads(currentbranch)
77 heads = repo.branchheads(currentbranch)
77 if heads:
78 if heads:
78 node = repo.revs('max(.::(%ln))', heads).first()
79 node = repo.revs('max(.::(%ln))', heads).first()
79 if bookmarks.isactivewdirparent(repo):
80 if bookmarks.isactivewdirparent(repo):
80 movemark = repo['.'].node()
81 movemark = repo['.'].node()
81 elif currentbranch == 'default' and not wc.p1():
82 elif currentbranch == 'default' and not wc.p1():
82 # "null" parent belongs to "default" branch, but it doesn't exist, so
83 # "null" parent belongs to "default" branch, but it doesn't exist, so
83 # update to the tipmost non-closed branch head
84 # update to the tipmost non-closed branch head
84 node = repo.revs('max(head() and not closed())').first()
85 node = repo.revs('max(head() and not closed())').first()
85 else:
86 else:
86 node = repo['.'].node()
87 node = repo['.'].node()
87 return node, movemark, None
88 return node, movemark, None
88
89
89 def _destupdatebranchfallback(repo, clean):
90 def _destupdatebranchfallback(repo, clean):
90 """decide on an update destination from closed heads in current branch"""
91 """decide on an update destination from closed heads in current branch"""
91 wc = repo[None]
92 wc = repo[None]
92 currentbranch = wc.branch()
93 currentbranch = wc.branch()
93 movemark = None
94 movemark = None
94 if currentbranch in repo.branchmap():
95 if currentbranch in repo.branchmap():
95 # here, all descendant branch heads are closed
96 # here, all descendant branch heads are closed
96 heads = repo.branchheads(currentbranch, closed=True)
97 heads = repo.branchheads(currentbranch, closed=True)
97 assert heads, "any branch has at least one head"
98 assert heads, "any branch has at least one head"
98 node = repo.revs('max(.::(%ln))', heads).first()
99 node = repo.revs('max(.::(%ln))', heads).first()
99 assert node is not None, ("any revision has at least "
100 assert node is not None, ("any revision has at least "
100 "one descendant branch head")
101 "one descendant branch head")
101 if bookmarks.isactivewdirparent(repo):
102 if bookmarks.isactivewdirparent(repo):
102 movemark = repo['.'].node()
103 movemark = repo['.'].node()
103 else:
104 else:
104 # here, no "default" branch, and all branches are closed
105 # here, no "default" branch, and all branches are closed
105 node = repo.lookup('tip')
106 node = repo.lookup('tip')
106 assert node is not None, "'tip' exists even in empty repository"
107 assert node is not None, "'tip' exists even in empty repository"
107 return node, movemark, None
108 return node, movemark, None
108
109
109 # order in which each step should be evaluated
110 # order in which each step should be evaluated
110 # steps are run until one finds a destination
111 # steps are run until one finds a destination
111 destupdatesteps = ['evolution', 'bookmark', 'branch', 'branchfallback']
112 destupdatesteps = ['evolution', 'bookmark', 'branch', 'branchfallback']
112 # mapping to ease extension overriding steps.
113 # mapping to ease extension overriding steps.
113 destupdatestepmap = {'evolution': _destupdateobs,
114 destupdatestepmap = {'evolution': _destupdateobs,
114 'bookmark': _destupdatebook,
115 'bookmark': _destupdatebook,
115 'branch': _destupdatebranch,
116 'branch': _destupdatebranch,
116 'branchfallback': _destupdatebranchfallback,
117 'branchfallback': _destupdatebranchfallback,
117 }
118 }
118
119
119 def destupdate(repo, clean=False):
120 def destupdate(repo, clean=False):
120 """destination for bare update operation
121 """destination for bare update operation
121
122
122 return (rev, movemark, activemark)
123 return (rev, movemark, activemark)
123
124
124 - rev: the revision to update to,
125 - rev: the revision to update to,
125 - movemark: node to move the active bookmark from
126 - movemark: node to move the active bookmark from
126 (cf bookmark.calculate update),
127 (cf bookmark.calculate update),
127 - activemark: a bookmark to activate at the end of the update.
128 - activemark: a bookmark to activate at the end of the update.
128 """
129 """
129 node = movemark = activemark = None
130 node = movemark = activemark = None
130
131
131 for step in destupdatesteps:
132 for step in destupdatesteps:
132 node, movemark, activemark = destupdatestepmap[step](repo, clean)
133 node, movemark, activemark = destupdatestepmap[step](repo, clean)
133 if node is not None:
134 if node is not None:
134 break
135 break
135 rev = repo[node].rev()
136 rev = repo[node].rev()
136
137
137 return rev, movemark, activemark
138 return rev, movemark, activemark
138
139
139 msgdestmerge = {
140 msgdestmerge = {
140 # too many matching divergent bookmark
141 # too many matching divergent bookmark
141 'toomanybookmarks':
142 'toomanybookmarks':
142 {'merge':
143 {'merge':
143 (_("multiple matching bookmarks to merge -"
144 (_("multiple matching bookmarks to merge -"
144 " please merge with an explicit rev or bookmark"),
145 " please merge with an explicit rev or bookmark"),
145 _("run 'hg heads' to see all heads")),
146 _("run 'hg heads' to see all heads")),
146 'rebase':
147 'rebase':
147 (_("multiple matching bookmarks to rebase -"
148 (_("multiple matching bookmarks to rebase -"
148 " please rebase to an explicit rev or bookmark"),
149 " please rebase to an explicit rev or bookmark"),
149 _("run 'hg heads' to see all heads")),
150 _("run 'hg heads' to see all heads")),
150 },
151 },
151 # no other matching divergent bookmark
152 # no other matching divergent bookmark
152 'nootherbookmarks':
153 'nootherbookmarks':
153 {'merge':
154 {'merge':
154 (_("no matching bookmark to merge - "
155 (_("no matching bookmark to merge - "
155 "please merge with an explicit rev or bookmark"),
156 "please merge with an explicit rev or bookmark"),
156 _("run 'hg heads' to see all heads")),
157 _("run 'hg heads' to see all heads")),
157 'rebase':
158 'rebase':
158 (_("no matching bookmark to rebase - "
159 (_("no matching bookmark to rebase - "
159 "please rebase to an explicit rev or bookmark"),
160 "please rebase to an explicit rev or bookmark"),
160 _("run 'hg heads' to see all heads")),
161 _("run 'hg heads' to see all heads")),
161 },
162 },
162 # branch have too many unbookmarked heads, no obvious destination
163 # branch have too many unbookmarked heads, no obvious destination
163 'toomanyheads':
164 'toomanyheads':
164 {'merge':
165 {'merge':
165 (_("branch '%s' has %d heads - please merge with an explicit rev"),
166 (_("branch '%s' has %d heads - please merge with an explicit rev"),
166 _("run 'hg heads .' to see heads")),
167 _("run 'hg heads .' to see heads")),
167 'rebase':
168 'rebase':
168 (_("branch '%s' has %d heads - please rebase to an explicit rev"),
169 (_("branch '%s' has %d heads - please rebase to an explicit rev"),
169 _("run 'hg heads .' to see heads")),
170 _("run 'hg heads .' to see heads")),
170 },
171 },
171 # branch have no other unbookmarked heads
172 # branch have no other unbookmarked heads
172 'bookmarkedheads':
173 'bookmarkedheads':
173 {'merge':
174 {'merge':
174 (_("heads are bookmarked - please merge with an explicit rev"),
175 (_("heads are bookmarked - please merge with an explicit rev"),
175 _("run 'hg heads' to see all heads")),
176 _("run 'hg heads' to see all heads")),
176 'rebase':
177 'rebase':
177 (_("heads are bookmarked - please rebase to an explicit rev"),
178 (_("heads are bookmarked - please rebase to an explicit rev"),
178 _("run 'hg heads' to see all heads")),
179 _("run 'hg heads' to see all heads")),
179 },
180 },
180 # branch have just a single heads, but there is other branches
181 # branch have just a single heads, but there is other branches
181 'nootherbranchheads':
182 'nootherbranchheads':
182 {'merge':
183 {'merge':
183 (_("branch '%s' has one head - please merge with an explicit rev"),
184 (_("branch '%s' has one head - please merge with an explicit rev"),
184 _("run 'hg heads' to see all heads")),
185 _("run 'hg heads' to see all heads")),
185 'rebase':
186 'rebase':
186 (_("branch '%s' has one head - please rebase to an explicit rev"),
187 (_("branch '%s' has one head - please rebase to an explicit rev"),
187 _("run 'hg heads' to see all heads")),
188 _("run 'hg heads' to see all heads")),
188 },
189 },
189 # repository have a single head
190 # repository have a single head
190 'nootherheads':
191 'nootherheads':
191 {'merge':
192 {'merge':
192 (_('nothing to merge'),
193 (_('nothing to merge'),
193 None),
194 None),
194 'rebase':
195 'rebase':
195 (_('nothing to rebase'),
196 (_('nothing to rebase'),
196 None),
197 None),
197 },
198 },
198 # repository have a single head and we are not on it
199 # repository have a single head and we are not on it
199 'nootherheadsbehind':
200 'nootherheadsbehind':
200 {'merge':
201 {'merge':
201 (_('nothing to merge'),
202 (_('nothing to merge'),
202 _("use 'hg update' instead")),
203 _("use 'hg update' instead")),
203 'rebase':
204 'rebase':
204 (_('nothing to rebase'),
205 (_('nothing to rebase'),
205 _("use 'hg update' instead")),
206 _("use 'hg update' instead")),
206 },
207 },
207 # We are not on a head
208 # We are not on a head
208 'notatheads':
209 'notatheads':
209 {'merge':
210 {'merge':
210 (_('working directory not at a head revision'),
211 (_('working directory not at a head revision'),
211 _("use 'hg update' or merge with an explicit revision")),
212 _("use 'hg update' or merge with an explicit revision")),
212 'rebase':
213 'rebase':
213 (_('working directory not at a head revision'),
214 (_('working directory not at a head revision'),
214 _("use 'hg update' or rebase to an explicit revision"))
215 _("use 'hg update' or rebase to an explicit revision"))
215 },
216 },
216 'emptysourceset':
217 'emptysourceset':
217 {'merge':
218 {'merge':
218 (_('source set is empty'),
219 (_('source set is empty'),
219 None),
220 None),
220 'rebase':
221 'rebase':
221 (_('source set is empty'),
222 (_('source set is empty'),
222 None),
223 None),
223 },
224 },
224 'multiplebranchessourceset':
225 'multiplebranchessourceset':
225 {'merge':
226 {'merge':
226 (_('source set is rooted in multiple branches'),
227 (_('source set is rooted in multiple branches'),
227 None),
228 None),
228 'rebase':
229 'rebase':
229 (_('rebaseset is rooted in multiple named branches'),
230 (_('rebaseset is rooted in multiple named branches'),
230 _('specify an explicit destination with --dest')),
231 _('specify an explicit destination with --dest')),
231 },
232 },
232 }
233 }
233
234
234 def _destmergebook(repo, action='merge', sourceset=None, destspace=None):
235 def _destmergebook(repo, action='merge', sourceset=None, destspace=None):
235 """find merge destination in the active bookmark case"""
236 """find merge destination in the active bookmark case"""
236 node = None
237 node = None
237 bmheads = bookmarks.headsforactive(repo)
238 bmheads = bookmarks.headsforactive(repo)
238 curhead = repo[repo._activebookmark].node()
239 curhead = repo[repo._activebookmark].node()
239 if len(bmheads) == 2:
240 if len(bmheads) == 2:
240 if curhead == bmheads[0]:
241 if curhead == bmheads[0]:
241 node = bmheads[1]
242 node = bmheads[1]
242 else:
243 else:
243 node = bmheads[0]
244 node = bmheads[0]
244 elif len(bmheads) > 2:
245 elif len(bmheads) > 2:
245 msg, hint = msgdestmerge['toomanybookmarks'][action]
246 msg, hint = msgdestmerge['toomanybookmarks'][action]
246 raise error.ManyMergeDestAbort(msg, hint=hint)
247 raise error.ManyMergeDestAbort(msg, hint=hint)
247 elif len(bmheads) <= 1:
248 elif len(bmheads) <= 1:
248 msg, hint = msgdestmerge['nootherbookmarks'][action]
249 msg, hint = msgdestmerge['nootherbookmarks'][action]
249 raise error.NoMergeDestAbort(msg, hint=hint)
250 raise error.NoMergeDestAbort(msg, hint=hint)
250 assert node is not None
251 assert node is not None
251 return node
252 return node
252
253
253 def _destmergebranch(repo, action='merge', sourceset=None, onheadcheck=True,
254 def _destmergebranch(repo, action='merge', sourceset=None, onheadcheck=True,
254 destspace=None):
255 destspace=None):
255 """find merge destination based on branch heads"""
256 """find merge destination based on branch heads"""
256 node = None
257 node = None
257
258
258 if sourceset is None:
259 if sourceset is None:
259 sourceset = [repo[repo.dirstate.p1()].rev()]
260 sourceset = [repo[repo.dirstate.p1()].rev()]
260 branch = repo.dirstate.branch()
261 branch = repo.dirstate.branch()
261 elif not sourceset:
262 elif not sourceset:
262 msg, hint = msgdestmerge['emptysourceset'][action]
263 msg, hint = msgdestmerge['emptysourceset'][action]
263 raise error.NoMergeDestAbort(msg, hint=hint)
264 raise error.NoMergeDestAbort(msg, hint=hint)
264 else:
265 else:
265 branch = None
266 branch = None
266 for ctx in repo.set('roots(%ld::%ld)', sourceset, sourceset):
267 for ctx in repo.set('roots(%ld::%ld)', sourceset, sourceset):
267 if branch is not None and ctx.branch() != branch:
268 if branch is not None and ctx.branch() != branch:
268 msg, hint = msgdestmerge['multiplebranchessourceset'][action]
269 msg, hint = msgdestmerge['multiplebranchessourceset'][action]
269 raise error.ManyMergeDestAbort(msg, hint=hint)
270 raise error.ManyMergeDestAbort(msg, hint=hint)
270 branch = ctx.branch()
271 branch = ctx.branch()
271
272
272 bheads = repo.branchheads(branch)
273 bheads = repo.branchheads(branch)
273 onhead = repo.revs('%ld and %ln', sourceset, bheads)
274 onhead = repo.revs('%ld and %ln', sourceset, bheads)
274 if onheadcheck and not onhead:
275 if onheadcheck and not onhead:
275 # Case A: working copy if not on a head. (merge only)
276 # Case A: working copy if not on a head. (merge only)
276 #
277 #
277 # This is probably a user mistake We bailout pointing at 'hg update'
278 # This is probably a user mistake We bailout pointing at 'hg update'
278 if len(repo.heads()) <= 1:
279 if len(repo.heads()) <= 1:
279 msg, hint = msgdestmerge['nootherheadsbehind'][action]
280 msg, hint = msgdestmerge['nootherheadsbehind'][action]
280 else:
281 else:
281 msg, hint = msgdestmerge['notatheads'][action]
282 msg, hint = msgdestmerge['notatheads'][action]
282 raise error.Abort(msg, hint=hint)
283 raise error.Abort(msg, hint=hint)
283 # remove heads descendants of source from the set
284 # remove heads descendants of source from the set
284 bheads = list(repo.revs('%ln - (%ld::)', bheads, sourceset))
285 bheads = list(repo.revs('%ln - (%ld::)', bheads, sourceset))
285 # filters out bookmarked heads
286 # filters out bookmarked heads
286 nbhs = list(repo.revs('%ld - bookmark()', bheads))
287 nbhs = list(repo.revs('%ld - bookmark()', bheads))
287
288
288 if destspace is not None:
289 if destspace is not None:
289 # restrict search space
290 # restrict search space
290 # used in the 'hg pull --rebase' case, see issue 5214.
291 # used in the 'hg pull --rebase' case, see issue 5214.
291 nbhs = list(repo.revs('%ld and %ld', destspace, nbhs))
292 nbhs = list(repo.revs('%ld and %ld', destspace, nbhs))
292
293
293 if len(nbhs) > 1:
294 if len(nbhs) > 1:
294 # Case B: There is more than 1 other anonymous heads
295 # Case B: There is more than 1 other anonymous heads
295 #
296 #
296 # This means that there will be more than 1 candidate. This is
297 # This means that there will be more than 1 candidate. This is
297 # ambiguous. We abort asking the user to pick as explicit destination
298 # ambiguous. We abort asking the user to pick as explicit destination
298 # instead.
299 # instead.
299 msg, hint = msgdestmerge['toomanyheads'][action]
300 msg, hint = msgdestmerge['toomanyheads'][action]
300 msg %= (branch, len(bheads) + 1)
301 msg %= (branch, len(bheads) + 1)
301 raise error.ManyMergeDestAbort(msg, hint=hint)
302 raise error.ManyMergeDestAbort(msg, hint=hint)
302 elif not nbhs:
303 elif not nbhs:
303 # Case B: There is no other anonymous heads
304 # Case B: There is no other anonymous heads
304 #
305 #
305 # This means that there is no natural candidate to merge with.
306 # This means that there is no natural candidate to merge with.
306 # We abort, with various messages for various cases.
307 # We abort, with various messages for various cases.
307 if bheads:
308 if bheads:
308 msg, hint = msgdestmerge['bookmarkedheads'][action]
309 msg, hint = msgdestmerge['bookmarkedheads'][action]
309 elif len(repo.heads()) > 1:
310 elif len(repo.heads()) > 1:
310 msg, hint = msgdestmerge['nootherbranchheads'][action]
311 msg, hint = msgdestmerge['nootherbranchheads'][action]
311 msg %= branch
312 msg %= branch
312 elif not onhead:
313 elif not onhead:
313 # if 'onheadcheck == False' (rebase case),
314 # if 'onheadcheck == False' (rebase case),
314 # this was not caught in Case A.
315 # this was not caught in Case A.
315 msg, hint = msgdestmerge['nootherheadsbehind'][action]
316 msg, hint = msgdestmerge['nootherheadsbehind'][action]
316 else:
317 else:
317 msg, hint = msgdestmerge['nootherheads'][action]
318 msg, hint = msgdestmerge['nootherheads'][action]
318 raise error.NoMergeDestAbort(msg, hint=hint)
319 raise error.NoMergeDestAbort(msg, hint=hint)
319 else:
320 else:
320 node = nbhs[0]
321 node = nbhs[0]
321 assert node is not None
322 assert node is not None
322 return node
323 return node
323
324
324 def destmerge(repo, action='merge', sourceset=None, onheadcheck=True,
325 def destmerge(repo, action='merge', sourceset=None, onheadcheck=True,
325 destspace=None):
326 destspace=None):
326 """return the default destination for a merge
327 """return the default destination for a merge
327
328
328 (or raise exception about why it can't pick one)
329 (or raise exception about why it can't pick one)
329
330
330 :action: the action being performed, controls emitted error message
331 :action: the action being performed, controls emitted error message
331 """
332 """
332 # destspace is here to work around issues with `hg pull --rebase` see
333 # destspace is here to work around issues with `hg pull --rebase` see
333 # issue5214 for details
334 # issue5214 for details
334 if repo._activebookmark:
335 if repo._activebookmark:
335 node = _destmergebook(repo, action=action, sourceset=sourceset,
336 node = _destmergebook(repo, action=action, sourceset=sourceset,
336 destspace=destspace)
337 destspace=destspace)
337 else:
338 else:
338 node = _destmergebranch(repo, action=action, sourceset=sourceset,
339 node = _destmergebranch(repo, action=action, sourceset=sourceset,
339 onheadcheck=onheadcheck, destspace=destspace)
340 onheadcheck=onheadcheck, destspace=destspace)
340 return repo[node].rev()
341 return repo[node].rev()
341
342
342 histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
343 histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
343
344
344 def desthistedit(ui, repo):
345 def desthistedit(ui, repo):
345 """Default base revision to edit for `hg histedit`."""
346 """Default base revision to edit for `hg histedit`."""
346 default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
347 default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
347 if default:
348 if default:
348 revs = scmutil.revrange(repo, [default])
349 revs = scmutil.revrange(repo, [default])
349 if revs:
350 if revs:
350 # The revset supplied by the user may not be in ascending order nor
351 # The revset supplied by the user may not be in ascending order nor
351 # take the first revision. So do this manually.
352 # take the first revision. So do this manually.
352 revs.sort()
353 revs.sort()
353 return revs.first()
354 return revs.first()
354
355
355 return None
356 return None
356
357
357 def stackbase(ui, repo):
358 def stackbase(ui, repo):
358 # The histedit default base stops at public changesets, branchpoints,
359 revs = stack.getstack(repo)
359 # and merges, which is exactly what we want for a stack.
360 revs = scmutil.revrange(repo, [histeditdefaultrevset])
361 return revs.last() if revs else None
360 return revs.last() if revs else None
362
361
363 def _statusotherbook(ui, repo):
362 def _statusotherbook(ui, repo):
364 bmheads = bookmarks.headsforactive(repo)
363 bmheads = bookmarks.headsforactive(repo)
365 curhead = repo[repo._activebookmark].node()
364 curhead = repo[repo._activebookmark].node()
366 if repo.revs('%n and parents()', curhead):
365 if repo.revs('%n and parents()', curhead):
367 # we are on the active bookmark
366 # we are on the active bookmark
368 bmheads = [b for b in bmheads if curhead != b]
367 bmheads = [b for b in bmheads if curhead != b]
369 if bmheads:
368 if bmheads:
370 msg = _('%i other divergent bookmarks for "%s"\n')
369 msg = _('%i other divergent bookmarks for "%s"\n')
371 ui.status(msg % (len(bmheads), repo._activebookmark))
370 ui.status(msg % (len(bmheads), repo._activebookmark))
372
371
373 def _statusotherbranchheads(ui, repo):
372 def _statusotherbranchheads(ui, repo):
374 currentbranch = repo.dirstate.branch()
373 currentbranch = repo.dirstate.branch()
375 allheads = repo.branchheads(currentbranch, closed=True)
374 allheads = repo.branchheads(currentbranch, closed=True)
376 heads = repo.branchheads(currentbranch)
375 heads = repo.branchheads(currentbranch)
377 if repo.revs('%ln and parents()', allheads):
376 if repo.revs('%ln and parents()', allheads):
378 # we are on a head, even though it might be closed
377 # we are on a head, even though it might be closed
379 #
378 #
380 # on closed otherheads
379 # on closed otherheads
381 # ========= ==========
380 # ========= ==========
382 # o 0 all heads for current branch are closed
381 # o 0 all heads for current branch are closed
383 # N only descendant branch heads are closed
382 # N only descendant branch heads are closed
384 # x 0 there is only one non-closed branch head
383 # x 0 there is only one non-closed branch head
385 # N there are some non-closed branch heads
384 # N there are some non-closed branch heads
386 # ========= ==========
385 # ========= ==========
387 otherheads = repo.revs('%ln - parents()', heads)
386 otherheads = repo.revs('%ln - parents()', heads)
388 if repo['.'].closesbranch():
387 if repo['.'].closesbranch():
389 ui.warn(_('no open descendant heads on branch "%s", '
388 ui.warn(_('no open descendant heads on branch "%s", '
390 'updating to a closed head\n') %
389 'updating to a closed head\n') %
391 (currentbranch))
390 (currentbranch))
392 if otherheads:
391 if otherheads:
393 ui.warn(_("(committing will reopen the head, "
392 ui.warn(_("(committing will reopen the head, "
394 "use 'hg heads .' to see %i other heads)\n") %
393 "use 'hg heads .' to see %i other heads)\n") %
395 (len(otherheads)))
394 (len(otherheads)))
396 else:
395 else:
397 ui.warn(_('(committing will reopen branch "%s")\n') %
396 ui.warn(_('(committing will reopen branch "%s")\n') %
398 (currentbranch))
397 (currentbranch))
399 elif otherheads:
398 elif otherheads:
400 curhead = repo['.']
399 curhead = repo['.']
401 ui.status(_('updated to "%s: %s"\n') % (curhead,
400 ui.status(_('updated to "%s: %s"\n') % (curhead,
402 curhead.description().split('\n')[0]))
401 curhead.description().split('\n')[0]))
403 ui.status(_('%i other heads for branch "%s"\n') %
402 ui.status(_('%i other heads for branch "%s"\n') %
404 (len(otherheads), currentbranch))
403 (len(otherheads), currentbranch))
405
404
406 def statusotherdests(ui, repo):
405 def statusotherdests(ui, repo):
407 """Print message about other head"""
406 """Print message about other head"""
408 # XXX we should probably include a hint:
407 # XXX we should probably include a hint:
409 # - about what to do
408 # - about what to do
410 # - how to see such heads
409 # - how to see such heads
411 if repo._activebookmark:
410 if repo._activebookmark:
412 _statusotherbook(ui, repo)
411 _statusotherbook(ui, repo)
413 else:
412 else:
414 _statusotherbranchheads(ui, repo)
413 _statusotherbranchheads(ui, repo)
General Comments 0
You need to be logged in to leave comments. Login now