125 currentbranch = wc.branch() |
125 currentbranch = wc.branch() |
126 movemark = None |
126 movemark = None |
127 if currentbranch in repo.branchmap(): |
127 if currentbranch in repo.branchmap(): |
128 # here, all descendant branch heads are closed |
128 # here, all descendant branch heads are closed |
129 heads = repo.branchheads(currentbranch, closed=True) |
129 heads = repo.branchheads(currentbranch, closed=True) |
130 assert heads, "any branch has at least one head" |
130 assert heads, b"any branch has at least one head" |
131 node = repo.revs('max(.::(%ln))', heads).first() |
131 node = repo.revs(b'max(.::(%ln))', heads).first() |
132 assert node is not None, ( |
132 assert node is not None, ( |
133 "any revision has at least " "one descendant branch head" |
133 b"any revision has at least " b"one descendant branch head" |
134 ) |
134 ) |
135 if bookmarks.isactivewdirparent(repo): |
135 if bookmarks.isactivewdirparent(repo): |
136 movemark = repo['.'].node() |
136 movemark = repo[b'.'].node() |
137 else: |
137 else: |
138 # here, no "default" branch, and all branches are closed |
138 # here, no "default" branch, and all branches are closed |
139 node = repo.lookup('tip') |
139 node = repo.lookup(b'tip') |
140 assert node is not None, "'tip' exists even in empty repository" |
140 assert node is not None, b"'tip' exists even in empty repository" |
141 return node, movemark, None |
141 return node, movemark, None |
142 |
142 |
143 |
143 |
144 # order in which each step should be evaluated |
144 # order in which each step should be evaluated |
145 # steps are run until one finds a destination |
145 # steps are run until one finds a destination |
146 destupdatesteps = ['evolution', 'bookmark', 'branch', 'branchfallback'] |
146 destupdatesteps = [b'evolution', b'bookmark', b'branch', b'branchfallback'] |
147 # mapping to ease extension overriding steps. |
147 # mapping to ease extension overriding steps. |
148 destupdatestepmap = { |
148 destupdatestepmap = { |
149 'evolution': _destupdateobs, |
149 b'evolution': _destupdateobs, |
150 'bookmark': _destupdatebook, |
150 b'bookmark': _destupdatebook, |
151 'branch': _destupdatebranch, |
151 b'branch': _destupdatebranch, |
152 'branchfallback': _destupdatebranchfallback, |
152 b'branchfallback': _destupdatebranchfallback, |
153 } |
153 } |
154 |
154 |
155 |
155 |
156 def destupdate(repo, clean=False): |
156 def destupdate(repo, clean=False): |
157 """destination for bare update operation |
157 """destination for bare update operation |
174 return rev, movemark, activemark |
174 return rev, movemark, activemark |
175 |
175 |
176 |
176 |
177 msgdestmerge = { |
177 msgdestmerge = { |
178 # too many matching divergent bookmark |
178 # too many matching divergent bookmark |
179 'toomanybookmarks': { |
179 b'toomanybookmarks': { |
180 'merge': ( |
180 b'merge': ( |
181 _( |
181 _( |
182 "multiple matching bookmarks to merge -" |
182 b"multiple matching bookmarks to merge -" |
183 " please merge with an explicit rev or bookmark" |
183 b" please merge with an explicit rev or bookmark" |
184 ), |
184 ), |
185 _("run 'hg heads' to see all heads"), |
185 _(b"run 'hg heads' to see all heads"), |
186 ), |
186 ), |
187 'rebase': ( |
187 b'rebase': ( |
188 _( |
188 _( |
189 "multiple matching bookmarks to rebase -" |
189 b"multiple matching bookmarks to rebase -" |
190 " please rebase to an explicit rev or bookmark" |
190 b" please rebase to an explicit rev or bookmark" |
191 ), |
191 ), |
192 _("run 'hg heads' to see all heads"), |
192 _(b"run 'hg heads' to see all heads"), |
193 ), |
193 ), |
194 }, |
194 }, |
195 # no other matching divergent bookmark |
195 # no other matching divergent bookmark |
196 'nootherbookmarks': { |
196 b'nootherbookmarks': { |
197 'merge': ( |
197 b'merge': ( |
198 _( |
198 _( |
199 "no matching bookmark to merge - " |
199 b"no matching bookmark to merge - " |
200 "please merge with an explicit rev or bookmark" |
200 b"please merge with an explicit rev or bookmark" |
201 ), |
201 ), |
202 _("run 'hg heads' to see all heads"), |
202 _(b"run 'hg heads' to see all heads"), |
203 ), |
203 ), |
204 'rebase': ( |
204 b'rebase': ( |
205 _( |
205 _( |
206 "no matching bookmark to rebase - " |
206 b"no matching bookmark to rebase - " |
207 "please rebase to an explicit rev or bookmark" |
207 b"please rebase to an explicit rev or bookmark" |
208 ), |
208 ), |
209 _("run 'hg heads' to see all heads"), |
209 _(b"run 'hg heads' to see all heads"), |
210 ), |
210 ), |
211 }, |
211 }, |
212 # branch have too many unbookmarked heads, no obvious destination |
212 # branch have too many unbookmarked heads, no obvious destination |
213 'toomanyheads': { |
213 b'toomanyheads': { |
214 'merge': ( |
214 b'merge': ( |
215 _("branch '%s' has %d heads - please merge with an explicit rev"), |
215 _(b"branch '%s' has %d heads - please merge with an explicit rev"), |
216 _("run 'hg heads .' to see heads"), |
216 _(b"run 'hg heads .' to see heads"), |
217 ), |
217 ), |
218 'rebase': ( |
218 b'rebase': ( |
219 _("branch '%s' has %d heads - please rebase to an explicit rev"), |
219 _(b"branch '%s' has %d heads - please rebase to an explicit rev"), |
220 _("run 'hg heads .' to see heads"), |
220 _(b"run 'hg heads .' to see heads"), |
221 ), |
221 ), |
222 }, |
222 }, |
223 # branch have no other unbookmarked heads |
223 # branch have no other unbookmarked heads |
224 'bookmarkedheads': { |
224 b'bookmarkedheads': { |
225 'merge': ( |
225 b'merge': ( |
226 _("heads are bookmarked - please merge with an explicit rev"), |
226 _(b"heads are bookmarked - please merge with an explicit rev"), |
227 _("run 'hg heads' to see all heads"), |
227 _(b"run 'hg heads' to see all heads"), |
228 ), |
228 ), |
229 'rebase': ( |
229 b'rebase': ( |
230 _("heads are bookmarked - please rebase to an explicit rev"), |
230 _(b"heads are bookmarked - please rebase to an explicit rev"), |
231 _("run 'hg heads' to see all heads"), |
231 _(b"run 'hg heads' to see all heads"), |
232 ), |
232 ), |
233 }, |
233 }, |
234 # branch have just a single heads, but there is other branches |
234 # branch have just a single heads, but there is other branches |
235 'nootherbranchheads': { |
235 b'nootherbranchheads': { |
236 'merge': ( |
236 b'merge': ( |
237 _("branch '%s' has one head - please merge with an explicit rev"), |
237 _(b"branch '%s' has one head - please merge with an explicit rev"), |
238 _("run 'hg heads' to see all heads"), |
238 _(b"run 'hg heads' to see all heads"), |
239 ), |
239 ), |
240 'rebase': ( |
240 b'rebase': ( |
241 _("branch '%s' has one head - please rebase to an explicit rev"), |
241 _(b"branch '%s' has one head - please rebase to an explicit rev"), |
242 _("run 'hg heads' to see all heads"), |
242 _(b"run 'hg heads' to see all heads"), |
243 ), |
243 ), |
244 }, |
244 }, |
245 # repository have a single head |
245 # repository have a single head |
246 'nootherheads': { |
246 b'nootherheads': { |
247 'merge': (_('nothing to merge'), None), |
247 b'merge': (_(b'nothing to merge'), None), |
248 'rebase': (_('nothing to rebase'), None), |
248 b'rebase': (_(b'nothing to rebase'), None), |
249 }, |
249 }, |
250 # repository have a single head and we are not on it |
250 # repository have a single head and we are not on it |
251 'nootherheadsbehind': { |
251 b'nootherheadsbehind': { |
252 'merge': (_('nothing to merge'), _("use 'hg update' instead")), |
252 b'merge': (_(b'nothing to merge'), _(b"use 'hg update' instead")), |
253 'rebase': (_('nothing to rebase'), _("use 'hg update' instead")), |
253 b'rebase': (_(b'nothing to rebase'), _(b"use 'hg update' instead")), |
254 }, |
254 }, |
255 # We are not on a head |
255 # We are not on a head |
256 'notatheads': { |
256 b'notatheads': { |
257 'merge': ( |
257 b'merge': ( |
258 _('working directory not at a head revision'), |
258 _(b'working directory not at a head revision'), |
259 _("use 'hg update' or merge with an explicit revision"), |
259 _(b"use 'hg update' or merge with an explicit revision"), |
260 ), |
260 ), |
261 'rebase': ( |
261 b'rebase': ( |
262 _('working directory not at a head revision'), |
262 _(b'working directory not at a head revision'), |
263 _("use 'hg update' or rebase to an explicit revision"), |
263 _(b"use 'hg update' or rebase to an explicit revision"), |
264 ), |
264 ), |
265 }, |
265 }, |
266 'emptysourceset': { |
266 b'emptysourceset': { |
267 'merge': (_('source set is empty'), None), |
267 b'merge': (_(b'source set is empty'), None), |
268 'rebase': (_('source set is empty'), None), |
268 b'rebase': (_(b'source set is empty'), None), |
269 }, |
269 }, |
270 'multiplebranchessourceset': { |
270 b'multiplebranchessourceset': { |
271 'merge': (_('source set is rooted in multiple branches'), None), |
271 b'merge': (_(b'source set is rooted in multiple branches'), None), |
272 'rebase': ( |
272 b'rebase': ( |
273 _('rebaseset is rooted in multiple named branches'), |
273 _(b'rebaseset is rooted in multiple named branches'), |
274 _('specify an explicit destination with --dest'), |
274 _(b'specify an explicit destination with --dest'), |
275 ), |
275 ), |
276 }, |
276 }, |
277 } |
277 } |
278 |
278 |
279 |
279 |
280 def _destmergebook(repo, action='merge', sourceset=None, destspace=None): |
280 def _destmergebook(repo, action=b'merge', sourceset=None, destspace=None): |
281 """find merge destination in the active bookmark case""" |
281 """find merge destination in the active bookmark case""" |
282 node = None |
282 node = None |
283 bmheads = bookmarks.headsforactive(repo) |
283 bmheads = bookmarks.headsforactive(repo) |
284 curhead = repo._bookmarks[repo._activebookmark] |
284 curhead = repo._bookmarks[repo._activebookmark] |
285 if len(bmheads) == 2: |
285 if len(bmheads) == 2: |
286 if curhead == bmheads[0]: |
286 if curhead == bmheads[0]: |
287 node = bmheads[1] |
287 node = bmheads[1] |
288 else: |
288 else: |
289 node = bmheads[0] |
289 node = bmheads[0] |
290 elif len(bmheads) > 2: |
290 elif len(bmheads) > 2: |
291 msg, hint = msgdestmerge['toomanybookmarks'][action] |
291 msg, hint = msgdestmerge[b'toomanybookmarks'][action] |
292 raise error.ManyMergeDestAbort(msg, hint=hint) |
292 raise error.ManyMergeDestAbort(msg, hint=hint) |
293 elif len(bmheads) <= 1: |
293 elif len(bmheads) <= 1: |
294 msg, hint = msgdestmerge['nootherbookmarks'][action] |
294 msg, hint = msgdestmerge[b'nootherbookmarks'][action] |
295 raise error.NoMergeDestAbort(msg, hint=hint) |
295 raise error.NoMergeDestAbort(msg, hint=hint) |
296 assert node is not None |
296 assert node is not None |
297 return node |
297 return node |
298 |
298 |
299 |
299 |
300 def _destmergebranch( |
300 def _destmergebranch( |
301 repo, action='merge', sourceset=None, onheadcheck=True, destspace=None |
301 repo, action=b'merge', sourceset=None, onheadcheck=True, destspace=None |
302 ): |
302 ): |
303 """find merge destination based on branch heads""" |
303 """find merge destination based on branch heads""" |
304 node = None |
304 node = None |
305 |
305 |
306 if sourceset is None: |
306 if sourceset is None: |
307 sourceset = [repo[repo.dirstate.p1()].rev()] |
307 sourceset = [repo[repo.dirstate.p1()].rev()] |
308 branch = repo.dirstate.branch() |
308 branch = repo.dirstate.branch() |
309 elif not sourceset: |
309 elif not sourceset: |
310 msg, hint = msgdestmerge['emptysourceset'][action] |
310 msg, hint = msgdestmerge[b'emptysourceset'][action] |
311 raise error.NoMergeDestAbort(msg, hint=hint) |
311 raise error.NoMergeDestAbort(msg, hint=hint) |
312 else: |
312 else: |
313 branch = None |
313 branch = None |
314 for ctx in repo.set('roots(%ld::%ld)', sourceset, sourceset): |
314 for ctx in repo.set(b'roots(%ld::%ld)', sourceset, sourceset): |
315 if branch is not None and ctx.branch() != branch: |
315 if branch is not None and ctx.branch() != branch: |
316 msg, hint = msgdestmerge['multiplebranchessourceset'][action] |
316 msg, hint = msgdestmerge[b'multiplebranchessourceset'][action] |
317 raise error.ManyMergeDestAbort(msg, hint=hint) |
317 raise error.ManyMergeDestAbort(msg, hint=hint) |
318 branch = ctx.branch() |
318 branch = ctx.branch() |
319 |
319 |
320 bheads = repo.branchheads(branch) |
320 bheads = repo.branchheads(branch) |
321 onhead = repo.revs('%ld and %ln', sourceset, bheads) |
321 onhead = repo.revs(b'%ld and %ln', sourceset, bheads) |
322 if onheadcheck and not onhead: |
322 if onheadcheck and not onhead: |
323 # Case A: working copy if not on a head. (merge only) |
323 # Case A: working copy if not on a head. (merge only) |
324 # |
324 # |
325 # This is probably a user mistake We bailout pointing at 'hg update' |
325 # This is probably a user mistake We bailout pointing at 'hg update' |
326 if len(repo.heads()) <= 1: |
326 if len(repo.heads()) <= 1: |
327 msg, hint = msgdestmerge['nootherheadsbehind'][action] |
327 msg, hint = msgdestmerge[b'nootherheadsbehind'][action] |
328 else: |
328 else: |
329 msg, hint = msgdestmerge['notatheads'][action] |
329 msg, hint = msgdestmerge[b'notatheads'][action] |
330 raise error.Abort(msg, hint=hint) |
330 raise error.Abort(msg, hint=hint) |
331 # remove heads descendants of source from the set |
331 # remove heads descendants of source from the set |
332 bheads = list(repo.revs('%ln - (%ld::)', bheads, sourceset)) |
332 bheads = list(repo.revs(b'%ln - (%ld::)', bheads, sourceset)) |
333 # filters out bookmarked heads |
333 # filters out bookmarked heads |
334 nbhs = list(repo.revs('%ld - bookmark()', bheads)) |
334 nbhs = list(repo.revs(b'%ld - bookmark()', bheads)) |
335 |
335 |
336 if destspace is not None: |
336 if destspace is not None: |
337 # restrict search space |
337 # restrict search space |
338 # used in the 'hg pull --rebase' case, see issue 5214. |
338 # used in the 'hg pull --rebase' case, see issue 5214. |
339 nbhs = list(repo.revs('%ld and %ld', destspace, nbhs)) |
339 nbhs = list(repo.revs(b'%ld and %ld', destspace, nbhs)) |
340 |
340 |
341 if len(nbhs) > 1: |
341 if len(nbhs) > 1: |
342 # Case B: There is more than 1 other anonymous heads |
342 # Case B: There is more than 1 other anonymous heads |
343 # |
343 # |
344 # This means that there will be more than 1 candidate. This is |
344 # This means that there will be more than 1 candidate. This is |
345 # ambiguous. We abort asking the user to pick as explicit destination |
345 # ambiguous. We abort asking the user to pick as explicit destination |
346 # instead. |
346 # instead. |
347 msg, hint = msgdestmerge['toomanyheads'][action] |
347 msg, hint = msgdestmerge[b'toomanyheads'][action] |
348 msg %= (branch, len(bheads) + 1) |
348 msg %= (branch, len(bheads) + 1) |
349 raise error.ManyMergeDestAbort(msg, hint=hint) |
349 raise error.ManyMergeDestAbort(msg, hint=hint) |
350 elif not nbhs: |
350 elif not nbhs: |
351 # Case B: There is no other anonymous heads |
351 # Case B: There is no other anonymous heads |
352 # |
352 # |
353 # This means that there is no natural candidate to merge with. |
353 # This means that there is no natural candidate to merge with. |
354 # We abort, with various messages for various cases. |
354 # We abort, with various messages for various cases. |
355 if bheads: |
355 if bheads: |
356 msg, hint = msgdestmerge['bookmarkedheads'][action] |
356 msg, hint = msgdestmerge[b'bookmarkedheads'][action] |
357 elif len(repo.heads()) > 1: |
357 elif len(repo.heads()) > 1: |
358 msg, hint = msgdestmerge['nootherbranchheads'][action] |
358 msg, hint = msgdestmerge[b'nootherbranchheads'][action] |
359 msg %= branch |
359 msg %= branch |
360 elif not onhead: |
360 elif not onhead: |
361 # if 'onheadcheck == False' (rebase case), |
361 # if 'onheadcheck == False' (rebase case), |
362 # this was not caught in Case A. |
362 # this was not caught in Case A. |
363 msg, hint = msgdestmerge['nootherheadsbehind'][action] |
363 msg, hint = msgdestmerge[b'nootherheadsbehind'][action] |
364 else: |
364 else: |
365 msg, hint = msgdestmerge['nootherheads'][action] |
365 msg, hint = msgdestmerge[b'nootherheads'][action] |
366 raise error.NoMergeDestAbort(msg, hint=hint) |
366 raise error.NoMergeDestAbort(msg, hint=hint) |
367 else: |
367 else: |
368 node = nbhs[0] |
368 node = nbhs[0] |
369 assert node is not None |
369 assert node is not None |
370 return node |
370 return node |
371 |
371 |
372 |
372 |
373 def destmerge( |
373 def destmerge( |
374 repo, action='merge', sourceset=None, onheadcheck=True, destspace=None |
374 repo, action=b'merge', sourceset=None, onheadcheck=True, destspace=None |
375 ): |
375 ): |
376 """return the default destination for a merge |
376 """return the default destination for a merge |
377 |
377 |
378 (or raise exception about why it can't pick one) |
378 (or raise exception about why it can't pick one) |
379 |
379 |
420 |
422 |
421 |
423 |
422 def _statusotherbook(ui, repo): |
424 def _statusotherbook(ui, repo): |
423 bmheads = bookmarks.headsforactive(repo) |
425 bmheads = bookmarks.headsforactive(repo) |
424 curhead = repo._bookmarks[repo._activebookmark] |
426 curhead = repo._bookmarks[repo._activebookmark] |
425 if repo.revs('%n and parents()', curhead): |
427 if repo.revs(b'%n and parents()', curhead): |
426 # we are on the active bookmark |
428 # we are on the active bookmark |
427 bmheads = [b for b in bmheads if curhead != b] |
429 bmheads = [b for b in bmheads if curhead != b] |
428 if bmheads: |
430 if bmheads: |
429 msg = _('%i other divergent bookmarks for "%s"\n') |
431 msg = _(b'%i other divergent bookmarks for "%s"\n') |
430 ui.status(msg % (len(bmheads), repo._activebookmark)) |
432 ui.status(msg % (len(bmheads), repo._activebookmark)) |
431 |
433 |
432 |
434 |
433 def _statusotherbranchheads(ui, repo): |
435 def _statusotherbranchheads(ui, repo): |
434 currentbranch = repo.dirstate.branch() |
436 currentbranch = repo.dirstate.branch() |
435 allheads = repo.branchheads(currentbranch, closed=True) |
437 allheads = repo.branchheads(currentbranch, closed=True) |
436 heads = repo.branchheads(currentbranch) |
438 heads = repo.branchheads(currentbranch) |
437 if repo.revs('%ln and parents()', allheads): |
439 if repo.revs(b'%ln and parents()', allheads): |
438 # we are on a head, even though it might be closed |
440 # we are on a head, even though it might be closed |
439 # |
441 # |
440 # on closed otherheads |
442 # on closed otherheads |
441 # ========= ========== |
443 # ========= ========== |
442 # o 0 all heads for current branch are closed |
444 # o 0 all heads for current branch are closed |
443 # N only descendant branch heads are closed |
445 # N only descendant branch heads are closed |
444 # x 0 there is only one non-closed branch head |
446 # x 0 there is only one non-closed branch head |
445 # N there are some non-closed branch heads |
447 # N there are some non-closed branch heads |
446 # ========= ========== |
448 # ========= ========== |
447 otherheads = repo.revs('%ln - parents()', heads) |
449 otherheads = repo.revs(b'%ln - parents()', heads) |
448 if repo['.'].closesbranch(): |
450 if repo[b'.'].closesbranch(): |
449 ui.warn( |
451 ui.warn( |
450 _( |
452 _( |
451 'no open descendant heads on branch "%s", ' |
453 b'no open descendant heads on branch "%s", ' |
452 'updating to a closed head\n' |
454 b'updating to a closed head\n' |
453 ) |
455 ) |
454 % currentbranch |
456 % currentbranch |
455 ) |
457 ) |
456 if otherheads: |
458 if otherheads: |
457 ui.warn( |
459 ui.warn( |
458 _( |
460 _( |
459 "(committing will reopen the head, " |
461 b"(committing will reopen the head, " |
460 "use 'hg heads .' to see %i other heads)\n" |
462 b"use 'hg heads .' to see %i other heads)\n" |
461 ) |
463 ) |
462 % (len(otherheads)) |
464 % (len(otherheads)) |
463 ) |
465 ) |
464 else: |
466 else: |
465 ui.warn( |
467 ui.warn( |
466 _('(committing will reopen branch "%s")\n') % currentbranch |
468 _(b'(committing will reopen branch "%s")\n') % currentbranch |
467 ) |
469 ) |
468 elif otherheads: |
470 elif otherheads: |
469 curhead = repo['.'] |
471 curhead = repo[b'.'] |
470 ui.status( |
472 ui.status( |
471 _('updated to "%s: %s"\n') |
473 _(b'updated to "%s: %s"\n') |
472 % (curhead, curhead.description().split('\n')[0]) |
474 % (curhead, curhead.description().split(b'\n')[0]) |
473 ) |
475 ) |
474 ui.status( |
476 ui.status( |
475 _('%i other heads for branch "%s"\n') |
477 _(b'%i other heads for branch "%s"\n') |
476 % (len(otherheads), currentbranch) |
478 % (len(otherheads), currentbranch) |
477 ) |
479 ) |
478 |
480 |
479 |
481 |
480 def statusotherdests(ui, repo): |
482 def statusotherdests(ui, repo): |