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