mercurial/destutil.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43114 8197b395710e
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    74             # and the usual case (len = 1)
    74             # and the usual case (len = 1)
    75             successors = [n for sub in successors for n in sub]
    75             successors = [n for sub in successors for n in sub]
    76 
    76 
    77             # get the max revision for the given successors set,
    77             # get the max revision for the given successors set,
    78             # i.e. the 'tip' of a set
    78             # i.e. the 'tip' of a set
    79             node = repo.revs('max(%ln)', successors).first()
    79             node = repo.revs(b'max(%ln)', successors).first()
    80             if bookmarks.isactivewdirparent(repo):
    80             if bookmarks.isactivewdirparent(repo):
    81                 movemark = repo['.'].node()
    81                 movemark = repo[b'.'].node()
    82     return node, movemark, None
    82     return node, movemark, None
    83 
    83 
    84 
    84 
    85 def _destupdatebook(repo, clean):
    85 def _destupdatebook(repo, clean):
    86     """decide on an update destination from active bookmark"""
    86     """decide on an update destination from active bookmark"""
   100     wc = repo[None]
   100     wc = repo[None]
   101     movemark = node = None
   101     movemark = node = None
   102     currentbranch = wc.branch()
   102     currentbranch = wc.branch()
   103 
   103 
   104     if clean:
   104     if clean:
   105         currentbranch = repo['.'].branch()
   105         currentbranch = repo[b'.'].branch()
   106 
   106 
   107     if currentbranch in repo.branchmap():
   107     if currentbranch in repo.branchmap():
   108         heads = repo.branchheads(currentbranch)
   108         heads = repo.branchheads(currentbranch)
   109         if heads:
   109         if heads:
   110             node = repo.revs('max(.::(%ln))', heads).first()
   110             node = repo.revs(b'max(.::(%ln))', heads).first()
   111         if bookmarks.isactivewdirparent(repo):
   111         if bookmarks.isactivewdirparent(repo):
   112             movemark = repo['.'].node()
   112             movemark = repo[b'.'].node()
   113     elif currentbranch == 'default' and not wc.p1():
   113     elif currentbranch == b'default' and not wc.p1():
   114         # "null" parent belongs to "default" branch, but it doesn't exist, so
   114         # "null" parent belongs to "default" branch, but it doesn't exist, so
   115         # update to the tipmost non-closed branch head
   115         # update to the tipmost non-closed branch head
   116         node = repo.revs('max(head() and not closed())').first()
   116         node = repo.revs(b'max(head() and not closed())').first()
   117     else:
   117     else:
   118         node = repo['.'].node()
   118         node = repo[b'.'].node()
   119     return node, movemark, None
   119     return node, movemark, None
   120 
   120 
   121 
   121 
   122 def _destupdatebranchfallback(repo, clean):
   122 def _destupdatebranchfallback(repo, clean):
   123     """decide on an update destination from closed heads in current branch"""
   123     """decide on an update destination from closed heads in current branch"""
   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 
   396     return repo[node].rev()
   396     return repo[node].rev()
   397 
   397 
   398 
   398 
   399 def desthistedit(ui, repo):
   399 def desthistedit(ui, repo):
   400     """Default base revision to edit for `hg histedit`."""
   400     """Default base revision to edit for `hg histedit`."""
   401     default = ui.config('histedit', 'defaultrev')
   401     default = ui.config(b'histedit', b'defaultrev')
   402 
   402 
   403     if default is None:
   403     if default is None:
   404         revs = stack.getstack(repo)
   404         revs = stack.getstack(repo)
   405     elif default:
   405     elif default:
   406         revs = scmutil.revrange(repo, [default])
   406         revs = scmutil.revrange(repo, [default])
   407     else:
   407     else:
   408         raise error.Abort(_("config option histedit.defaultrev can't be empty"))
   408         raise error.Abort(
       
   409             _(b"config option histedit.defaultrev can't be empty")
       
   410         )
   409 
   411 
   410     if revs:
   412     if revs:
   411         # Take the first revision of the revset as the root
   413         # Take the first revision of the revset as the root
   412         return revs.min()
   414         return revs.min()
   413 
   415 
   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):