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