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