hgext/rebase.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43085 eef9a2d67051
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    54 # The following constants are used throughout the rebase module. The ordering of
    54 # The following constants are used throughout the rebase module. The ordering of
    55 # their values must be maintained.
    55 # their values must be maintained.
    56 
    56 
    57 # Indicates that a revision needs to be rebased
    57 # Indicates that a revision needs to be rebased
    58 revtodo = -1
    58 revtodo = -1
    59 revtodostr = '-1'
    59 revtodostr = b'-1'
    60 
    60 
    61 # legacy revstates no longer needed in current code
    61 # legacy revstates no longer needed in current code
    62 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
    62 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
    63 legacystates = {'-2', '-3', '-4', '-5'}
    63 legacystates = {b'-2', b'-3', b'-4', b'-5'}
    64 
    64 
    65 cmdtable = {}
    65 cmdtable = {}
    66 command = registrar.command(cmdtable)
    66 command = registrar.command(cmdtable)
    67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    69 # be specifying the version(s) of Mercurial they are tested with, or
    69 # be specifying the version(s) of Mercurial they are tested with, or
    70 # leave the attribute unspecified.
    70 # leave the attribute unspecified.
    71 testedwith = 'ships-with-hg-core'
    71 testedwith = b'ships-with-hg-core'
    72 
    72 
    73 
    73 
    74 def _nothingtorebase():
    74 def _nothingtorebase():
    75     return 1
    75     return 1
    76 
    76 
    77 
    77 
    78 def _savegraft(ctx, extra):
    78 def _savegraft(ctx, extra):
    79     s = ctx.extra().get('source', None)
    79     s = ctx.extra().get(b'source', None)
    80     if s is not None:
    80     if s is not None:
    81         extra['source'] = s
    81         extra[b'source'] = s
    82     s = ctx.extra().get('intermediate-source', None)
    82     s = ctx.extra().get(b'intermediate-source', None)
    83     if s is not None:
    83     if s is not None:
    84         extra['intermediate-source'] = s
    84         extra[b'intermediate-source'] = s
    85 
    85 
    86 
    86 
    87 def _savebranch(ctx, extra):
    87 def _savebranch(ctx, extra):
    88     extra['branch'] = ctx.branch()
    88     extra[b'branch'] = ctx.branch()
    89 
    89 
    90 
    90 
    91 def _destrebase(repo, sourceset, destspace=None):
    91 def _destrebase(repo, sourceset, destspace=None):
    92     """small wrapper around destmerge to pass the right extra args
    92     """small wrapper around destmerge to pass the right extra args
    93 
    93 
    94     Please wrap destutil.destmerge instead."""
    94     Please wrap destutil.destmerge instead."""
    95     return destutil.destmerge(
    95     return destutil.destmerge(
    96         repo,
    96         repo,
    97         action='rebase',
    97         action=b'rebase',
    98         sourceset=sourceset,
    98         sourceset=sourceset,
    99         onheadcheck=False,
    99         onheadcheck=False,
   100         destspace=destspace,
   100         destspace=destspace,
   101     )
   101     )
   102 
   102 
   103 
   103 
   104 revsetpredicate = registrar.revsetpredicate()
   104 revsetpredicate = registrar.revsetpredicate()
   105 
   105 
   106 
   106 
   107 @revsetpredicate('_destrebase')
   107 @revsetpredicate(b'_destrebase')
   108 def _revsetdestrebase(repo, subset, x):
   108 def _revsetdestrebase(repo, subset, x):
   109     # ``_rebasedefaultdest()``
   109     # ``_rebasedefaultdest()``
   110 
   110 
   111     # default destination for rebase.
   111     # default destination for rebase.
   112     # # XXX: Currently private because I expect the signature to change.
   112     # # XXX: Currently private because I expect the signature to change.
   116     if x is not None:
   116     if x is not None:
   117         sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
   117         sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
   118     return subset & smartset.baseset([_destrebase(repo, sourceset)])
   118     return subset & smartset.baseset([_destrebase(repo, sourceset)])
   119 
   119 
   120 
   120 
   121 @revsetpredicate('_destautoorphanrebase')
   121 @revsetpredicate(b'_destautoorphanrebase')
   122 def _revsetdestautoorphanrebase(repo, subset, x):
   122 def _revsetdestautoorphanrebase(repo, subset, x):
   123     # ``_destautoorphanrebase()``
   123     # ``_destautoorphanrebase()``
   124 
   124 
   125     # automatic rebase destination for a single orphan revision.
   125     # automatic rebase destination for a single orphan revision.
   126     unfi = repo.unfiltered()
   126     unfi = repo.unfiltered()
   127     obsoleted = unfi.revs('obsolete()')
   127     obsoleted = unfi.revs(b'obsolete()')
   128 
   128 
   129     src = revset.getset(repo, subset, x).first()
   129     src = revset.getset(repo, subset, x).first()
   130 
   130 
   131     # Empty src or already obsoleted - Do not return a destination
   131     # Empty src or already obsoleted - Do not return a destination
   132     if not src or src in obsoleted:
   132     if not src or src in obsoleted:
   133         return smartset.baseset()
   133         return smartset.baseset()
   134     dests = destutil.orphanpossibledestination(repo, src)
   134     dests = destutil.orphanpossibledestination(repo, src)
   135     if len(dests) > 1:
   135     if len(dests) > 1:
   136         raise error.Abort(
   136         raise error.Abort(
   137             _("ambiguous automatic rebase: %r could end up on any of %r")
   137             _(b"ambiguous automatic rebase: %r could end up on any of %r")
   138             % (src, dests)
   138             % (src, dests)
   139         )
   139         )
   140     # We have zero or one destination, so we can just return here.
   140     # We have zero or one destination, so we can just return here.
   141     return smartset.baseset(dests)
   141     return smartset.baseset(dests)
   142 
   142 
   143 
   143 
   144 def _ctxdesc(ctx):
   144 def _ctxdesc(ctx):
   145     """short description for a context"""
   145     """short description for a context"""
   146     desc = '%d:%s "%s"' % (ctx.rev(), ctx, ctx.description().split('\n', 1)[0])
   146     desc = b'%d:%s "%s"' % (
       
   147         ctx.rev(),
       
   148         ctx,
       
   149         ctx.description().split(b'\n', 1)[0],
       
   150     )
   147     repo = ctx.repo()
   151     repo = ctx.repo()
   148     names = []
   152     names = []
   149     for nsname, ns in repo.names.iteritems():
   153     for nsname, ns in repo.names.iteritems():
   150         if nsname == 'branches':
   154         if nsname == b'branches':
   151             continue
   155             continue
   152         names.extend(ns.names(repo, ctx.node()))
   156         names.extend(ns.names(repo, ctx.node()))
   153     if names:
   157     if names:
   154         desc += ' (%s)' % ' '.join(names)
   158         desc += b' (%s)' % b' '.join(names)
   155     return desc
   159     return desc
   156 
   160 
   157 
   161 
   158 class rebaseruntime(object):
   162 class rebaseruntime(object):
   159     """This class is a container for rebase runtime state"""
   163     """This class is a container for rebase runtime state"""
   183         self.state = {}
   187         self.state = {}
   184         self.activebookmark = None
   188         self.activebookmark = None
   185         self.destmap = {}
   189         self.destmap = {}
   186         self.skipped = set()
   190         self.skipped = set()
   187 
   191 
   188         self.collapsef = opts.get('collapse', False)
   192         self.collapsef = opts.get(b'collapse', False)
   189         self.collapsemsg = cmdutil.logmessage(ui, opts)
   193         self.collapsemsg = cmdutil.logmessage(ui, opts)
   190         self.date = opts.get('date', None)
   194         self.date = opts.get(b'date', None)
   191 
   195 
   192         e = opts.get('extrafn')  # internal, used by e.g. hgsubversion
   196         e = opts.get(b'extrafn')  # internal, used by e.g. hgsubversion
   193         self.extrafns = [_savegraft]
   197         self.extrafns = [_savegraft]
   194         if e:
   198         if e:
   195             self.extrafns = [e]
   199             self.extrafns = [e]
   196 
   200 
   197         self.backupf = ui.configbool('rewrite', 'backup-bundle')
   201         self.backupf = ui.configbool(b'rewrite', b'backup-bundle')
   198         self.keepf = opts.get('keep', False)
   202         self.keepf = opts.get(b'keep', False)
   199         self.keepbranchesf = opts.get('keepbranches', False)
   203         self.keepbranchesf = opts.get(b'keepbranches', False)
   200         self.obsoletenotrebased = {}
   204         self.obsoletenotrebased = {}
   201         self.obsoletewithoutsuccessorindestination = set()
   205         self.obsoletewithoutsuccessorindestination = set()
   202         self.inmemory = inmemory
   206         self.inmemory = inmemory
   203         self.stateobj = statemod.cmdstate(repo, 'rebasestate')
   207         self.stateobj = statemod.cmdstate(repo, b'rebasestate')
   204 
   208 
   205     @property
   209     @property
   206     def repo(self):
   210     def repo(self):
   207         if self.prepared:
   211         if self.prepared:
   208             return self._repo.unfiltered()
   212             return self._repo.unfiltered()
   211 
   215 
   212     def storestatus(self, tr=None):
   216     def storestatus(self, tr=None):
   213         """Store the current status to allow recovery"""
   217         """Store the current status to allow recovery"""
   214         if tr:
   218         if tr:
   215             tr.addfilegenerator(
   219             tr.addfilegenerator(
   216                 'rebasestate',
   220                 b'rebasestate',
   217                 ('rebasestate',),
   221                 (b'rebasestate',),
   218                 self._writestatus,
   222                 self._writestatus,
   219                 location='plain',
   223                 location=b'plain',
   220             )
   224             )
   221         else:
   225         else:
   222             with self.repo.vfs("rebasestate", "w") as f:
   226             with self.repo.vfs(b"rebasestate", b"w") as f:
   223                 self._writestatus(f)
   227                 self._writestatus(f)
   224 
   228 
   225     def _writestatus(self, f):
   229     def _writestatus(self, f):
   226         repo = self.repo
   230         repo = self.repo
   227         assert repo.filtername is None
   231         assert repo.filtername is None
   228         f.write(repo[self.originalwd].hex() + '\n')
   232         f.write(repo[self.originalwd].hex() + b'\n')
   229         # was "dest". we now write dest per src root below.
   233         # was "dest". we now write dest per src root below.
   230         f.write('\n')
   234         f.write(b'\n')
   231         f.write(repo[self.external].hex() + '\n')
   235         f.write(repo[self.external].hex() + b'\n')
   232         f.write('%d\n' % int(self.collapsef))
   236         f.write(b'%d\n' % int(self.collapsef))
   233         f.write('%d\n' % int(self.keepf))
   237         f.write(b'%d\n' % int(self.keepf))
   234         f.write('%d\n' % int(self.keepbranchesf))
   238         f.write(b'%d\n' % int(self.keepbranchesf))
   235         f.write('%s\n' % (self.activebookmark or ''))
   239         f.write(b'%s\n' % (self.activebookmark or b''))
   236         destmap = self.destmap
   240         destmap = self.destmap
   237         for d, v in self.state.iteritems():
   241         for d, v in self.state.iteritems():
   238             oldrev = repo[d].hex()
   242             oldrev = repo[d].hex()
   239             if v >= 0:
   243             if v >= 0:
   240                 newrev = repo[v].hex()
   244                 newrev = repo[v].hex()
   241             else:
   245             else:
   242                 newrev = "%d" % v
   246                 newrev = b"%d" % v
   243             destnode = repo[destmap[d]].hex()
   247             destnode = repo[destmap[d]].hex()
   244             f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
   248             f.write(b"%s:%s:%s\n" % (oldrev, newrev, destnode))
   245         repo.ui.debug('rebase status stored\n')
   249         repo.ui.debug(b'rebase status stored\n')
   246 
   250 
   247     def restorestatus(self):
   251     def restorestatus(self):
   248         """Restore a previously stored status"""
   252         """Restore a previously stored status"""
   249         if not self.stateobj.exists():
   253         if not self.stateobj.exists():
   250             cmdutil.wrongtooltocontinue(self.repo, _('rebase'))
   254             cmdutil.wrongtooltocontinue(self.repo, _(b'rebase'))
   251 
   255 
   252         data = self._read()
   256         data = self._read()
   253         self.repo.ui.debug('rebase status resumed\n')
   257         self.repo.ui.debug(b'rebase status resumed\n')
   254 
   258 
   255         self.originalwd = data['originalwd']
   259         self.originalwd = data[b'originalwd']
   256         self.destmap = data['destmap']
   260         self.destmap = data[b'destmap']
   257         self.state = data['state']
   261         self.state = data[b'state']
   258         self.skipped = data['skipped']
   262         self.skipped = data[b'skipped']
   259         self.collapsef = data['collapse']
   263         self.collapsef = data[b'collapse']
   260         self.keepf = data['keep']
   264         self.keepf = data[b'keep']
   261         self.keepbranchesf = data['keepbranches']
   265         self.keepbranchesf = data[b'keepbranches']
   262         self.external = data['external']
   266         self.external = data[b'external']
   263         self.activebookmark = data['activebookmark']
   267         self.activebookmark = data[b'activebookmark']
   264 
   268 
   265     def _read(self):
   269     def _read(self):
   266         self.prepared = True
   270         self.prepared = True
   267         repo = self.repo
   271         repo = self.repo
   268         assert repo.filtername is None
   272         assert repo.filtername is None
   269         data = {
   273         data = {
   270             'keepbranches': None,
   274             b'keepbranches': None,
   271             'collapse': None,
   275             b'collapse': None,
   272             'activebookmark': None,
   276             b'activebookmark': None,
   273             'external': nullrev,
   277             b'external': nullrev,
   274             'keep': None,
   278             b'keep': None,
   275             'originalwd': None,
   279             b'originalwd': None,
   276         }
   280         }
   277         legacydest = None
   281         legacydest = None
   278         state = {}
   282         state = {}
   279         destmap = {}
   283         destmap = {}
   280 
   284 
   281         if True:
   285         if True:
   282             f = repo.vfs("rebasestate")
   286             f = repo.vfs(b"rebasestate")
   283             for i, l in enumerate(f.read().splitlines()):
   287             for i, l in enumerate(f.read().splitlines()):
   284                 if i == 0:
   288                 if i == 0:
   285                     data['originalwd'] = repo[l].rev()
   289                     data[b'originalwd'] = repo[l].rev()
   286                 elif i == 1:
   290                 elif i == 1:
   287                     # this line should be empty in newer version. but legacy
   291                     # this line should be empty in newer version. but legacy
   288                     # clients may still use it
   292                     # clients may still use it
   289                     if l:
   293                     if l:
   290                         legacydest = repo[l].rev()
   294                         legacydest = repo[l].rev()
   291                 elif i == 2:
   295                 elif i == 2:
   292                     data['external'] = repo[l].rev()
   296                     data[b'external'] = repo[l].rev()
   293                 elif i == 3:
   297                 elif i == 3:
   294                     data['collapse'] = bool(int(l))
   298                     data[b'collapse'] = bool(int(l))
   295                 elif i == 4:
   299                 elif i == 4:
   296                     data['keep'] = bool(int(l))
   300                     data[b'keep'] = bool(int(l))
   297                 elif i == 5:
   301                 elif i == 5:
   298                     data['keepbranches'] = bool(int(l))
   302                     data[b'keepbranches'] = bool(int(l))
   299                 elif i == 6 and not (len(l) == 81 and ':' in l):
   303                 elif i == 6 and not (len(l) == 81 and b':' in l):
   300                     # line 6 is a recent addition, so for backwards
   304                     # line 6 is a recent addition, so for backwards
   301                     # compatibility check that the line doesn't look like the
   305                     # compatibility check that the line doesn't look like the
   302                     # oldrev:newrev lines
   306                     # oldrev:newrev lines
   303                     data['activebookmark'] = l
   307                     data[b'activebookmark'] = l
   304                 else:
   308                 else:
   305                     args = l.split(':')
   309                     args = l.split(b':')
   306                     oldrev = repo[args[0]].rev()
   310                     oldrev = repo[args[0]].rev()
   307                     newrev = args[1]
   311                     newrev = args[1]
   308                     if newrev in legacystates:
   312                     if newrev in legacystates:
   309                         continue
   313                         continue
   310                     if len(args) > 2:
   314                     if len(args) > 2:
   316                         state[oldrev] = revtodo
   320                         state[oldrev] = revtodo
   317                         # Legacy compat special case
   321                         # Legacy compat special case
   318                     else:
   322                     else:
   319                         state[oldrev] = repo[newrev].rev()
   323                         state[oldrev] = repo[newrev].rev()
   320 
   324 
   321         if data['keepbranches'] is None:
   325         if data[b'keepbranches'] is None:
   322             raise error.Abort(_('.hg/rebasestate is incomplete'))
   326             raise error.Abort(_(b'.hg/rebasestate is incomplete'))
   323 
   327 
   324         data['destmap'] = destmap
   328         data[b'destmap'] = destmap
   325         data['state'] = state
   329         data[b'state'] = state
   326         skipped = set()
   330         skipped = set()
   327         # recompute the set of skipped revs
   331         # recompute the set of skipped revs
   328         if not data['collapse']:
   332         if not data[b'collapse']:
   329             seen = set(destmap.values())
   333             seen = set(destmap.values())
   330             for old, new in sorted(state.items()):
   334             for old, new in sorted(state.items()):
   331                 if new != revtodo and new in seen:
   335                 if new != revtodo and new in seen:
   332                     skipped.add(old)
   336                     skipped.add(old)
   333                 seen.add(new)
   337                 seen.add(new)
   334         data['skipped'] = skipped
   338         data[b'skipped'] = skipped
   335         repo.ui.debug(
   339         repo.ui.debug(
   336             'computed skipped revs: %s\n'
   340             b'computed skipped revs: %s\n'
   337             % (' '.join('%d' % r for r in sorted(skipped)) or '')
   341             % (b' '.join(b'%d' % r for r in sorted(skipped)) or b'')
   338         )
   342         )
   339 
   343 
   340         return data
   344         return data
   341 
   345 
   342     def _handleskippingobsolete(self, obsoleterevs, destmap):
   346     def _handleskippingobsolete(self, obsoleterevs, destmap):
   344 
   348 
   345         obsoleterevs:   iterable of all obsolete revisions in rebaseset
   349         obsoleterevs:   iterable of all obsolete revisions in rebaseset
   346         destmap:        {srcrev: destrev} destination revisions
   350         destmap:        {srcrev: destrev} destination revisions
   347         """
   351         """
   348         self.obsoletenotrebased = {}
   352         self.obsoletenotrebased = {}
   349         if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
   353         if not self.ui.configbool(b'experimental', b'rebaseskipobsolete'):
   350             return
   354             return
   351         obsoleteset = set(obsoleterevs)
   355         obsoleteset = set(obsoleterevs)
   352         (
   356         (
   353             self.obsoletenotrebased,
   357             self.obsoletenotrebased,
   354             self.obsoletewithoutsuccessorindestination,
   358             self.obsoletewithoutsuccessorindestination,
   367             if isabort:
   371             if isabort:
   368                 clearstatus(self.repo)
   372                 clearstatus(self.repo)
   369                 clearcollapsemsg(self.repo)
   373                 clearcollapsemsg(self.repo)
   370                 self.repo.ui.warn(
   374                 self.repo.ui.warn(
   371                     _(
   375                     _(
   372                         'rebase aborted (no revision is removed,'
   376                         b'rebase aborted (no revision is removed,'
   373                         ' only broken state is cleared)\n'
   377                         b' only broken state is cleared)\n'
   374                     )
   378                     )
   375                 )
   379                 )
   376                 return 0
   380                 return 0
   377             else:
   381             else:
   378                 msg = _('cannot continue inconsistent rebase')
   382                 msg = _(b'cannot continue inconsistent rebase')
   379                 hint = _('use "hg rebase --abort" to clear broken state')
   383                 hint = _(b'use "hg rebase --abort" to clear broken state')
   380                 raise error.Abort(msg, hint=hint)
   384                 raise error.Abort(msg, hint=hint)
   381 
   385 
   382         if isabort:
   386         if isabort:
   383             backup = backup and self.backupf
   387             backup = backup and self.backupf
   384             return self._abort(backup=backup, suppwarns=suppwarns)
   388             return self._abort(backup=backup, suppwarns=suppwarns)
   388             return _nothingtorebase()
   392             return _nothingtorebase()
   389 
   393 
   390         rebaseset = destmap.keys()
   394         rebaseset = destmap.keys()
   391         allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
   395         allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
   392         if not (self.keepf or allowunstable) and self.repo.revs(
   396         if not (self.keepf or allowunstable) and self.repo.revs(
   393             'first(children(%ld) - %ld)', rebaseset, rebaseset
   397             b'first(children(%ld) - %ld)', rebaseset, rebaseset
   394         ):
   398         ):
   395             raise error.Abort(
   399             raise error.Abort(
   396                 _(
   400                 _(
   397                     "can't remove original changesets with"
   401                     b"can't remove original changesets with"
   398                     " unrebased descendants"
   402                     b" unrebased descendants"
   399                 ),
   403                 ),
   400                 hint=_('use --keep to keep original changesets'),
   404                 hint=_(b'use --keep to keep original changesets'),
   401             )
   405             )
   402 
   406 
   403         result = buildstate(self.repo, destmap, self.collapsef)
   407         result = buildstate(self.repo, destmap, self.collapsef)
   404 
   408 
   405         if not result:
   409         if not result:
   406             # Empty state built, nothing to rebase
   410             # Empty state built, nothing to rebase
   407             self.ui.status(_('nothing to rebase\n'))
   411             self.ui.status(_(b'nothing to rebase\n'))
   408             return _nothingtorebase()
   412             return _nothingtorebase()
   409 
   413 
   410         for root in self.repo.set('roots(%ld)', rebaseset):
   414         for root in self.repo.set(b'roots(%ld)', rebaseset):
   411             if not self.keepf and not root.mutable():
   415             if not self.keepf and not root.mutable():
   412                 raise error.Abort(
   416                 raise error.Abort(
   413                     _("can't rebase public changeset %s") % root,
   417                     _(b"can't rebase public changeset %s") % root,
   414                     hint=_("see 'hg help phases' for details"),
   418                     hint=_(b"see 'hg help phases' for details"),
   415                 )
   419                 )
   416 
   420 
   417         (self.originalwd, self.destmap, self.state) = result
   421         (self.originalwd, self.destmap, self.state) = result
   418         if self.collapsef:
   422         if self.collapsef:
   419             dests = set(self.destmap.values())
   423             dests = set(self.destmap.values())
   420             if len(dests) != 1:
   424             if len(dests) != 1:
   421                 raise error.Abort(
   425                 raise error.Abort(
   422                     _('--collapse does not work with multiple destinations')
   426                     _(b'--collapse does not work with multiple destinations')
   423                 )
   427                 )
   424             destrev = next(iter(dests))
   428             destrev = next(iter(dests))
   425             destancestors = self.repo.changelog.ancestors(
   429             destancestors = self.repo.changelog.ancestors(
   426                 [destrev], inclusive=True
   430                 [destrev], inclusive=True
   427             )
   431             )
   428             self.external = externalparent(self.repo, self.state, destancestors)
   432             self.external = externalparent(self.repo, self.state, destancestors)
   429 
   433 
   430         for destrev in sorted(set(destmap.values())):
   434         for destrev in sorted(set(destmap.values())):
   431             dest = self.repo[destrev]
   435             dest = self.repo[destrev]
   432             if dest.closesbranch() and not self.keepbranchesf:
   436             if dest.closesbranch() and not self.keepbranchesf:
   433                 self.ui.status(_('reopening closed branch head %s\n') % dest)
   437                 self.ui.status(_(b'reopening closed branch head %s\n') % dest)
   434 
   438 
   435         self.prepared = True
   439         self.prepared = True
   436 
   440 
   437     def _assignworkingcopy(self):
   441     def _assignworkingcopy(self):
   438         if self.inmemory:
   442         if self.inmemory:
   439             from mercurial.context import overlayworkingctx
   443             from mercurial.context import overlayworkingctx
   440 
   444 
   441             self.wctx = overlayworkingctx(self.repo)
   445             self.wctx = overlayworkingctx(self.repo)
   442             self.repo.ui.debug("rebasing in-memory\n")
   446             self.repo.ui.debug(b"rebasing in-memory\n")
   443         else:
   447         else:
   444             self.wctx = self.repo[None]
   448             self.wctx = self.repo[None]
   445             self.repo.ui.debug("rebasing on disk\n")
   449             self.repo.ui.debug(b"rebasing on disk\n")
   446         self.repo.ui.log(
   450         self.repo.ui.log(
   447             "rebase",
   451             b"rebase",
   448             "using in-memory rebase: %r\n",
   452             b"using in-memory rebase: %r\n",
   449             self.inmemory,
   453             self.inmemory,
   450             rebase_imm_used=self.inmemory,
   454             rebase_imm_used=self.inmemory,
   451         )
   455         )
   452 
   456 
   453     def _performrebase(self, tr):
   457     def _performrebase(self, tr):
   462                 branches = set()
   466                 branches = set()
   463                 for rev in self.state:
   467                 for rev in self.state:
   464                     branches.add(repo[rev].branch())
   468                     branches.add(repo[rev].branch())
   465                     if len(branches) > 1:
   469                     if len(branches) > 1:
   466                         raise error.Abort(
   470                         raise error.Abort(
   467                             _('cannot collapse multiple named ' 'branches')
   471                             _(b'cannot collapse multiple named ' b'branches')
   468                         )
   472                         )
   469 
   473 
   470         # Calculate self.obsoletenotrebased
   474         # Calculate self.obsoletenotrebased
   471         obsrevs = _filterobsoleterevs(self.repo, self.state)
   475         obsrevs = _filterobsoleterevs(self.repo, self.state)
   472         self._handleskippingobsolete(obsrevs, self.destmap)
   476         self._handleskippingobsolete(obsrevs, self.destmap)
   484             # commits.
   488             # commits.
   485             self.storestatus(tr)
   489             self.storestatus(tr)
   486 
   490 
   487         cands = [k for k, v in self.state.iteritems() if v == revtodo]
   491         cands = [k for k, v in self.state.iteritems() if v == revtodo]
   488         p = repo.ui.makeprogress(
   492         p = repo.ui.makeprogress(
   489             _("rebasing"), unit=_('changesets'), total=len(cands)
   493             _(b"rebasing"), unit=_(b'changesets'), total=len(cands)
   490         )
   494         )
   491 
   495 
   492         def progress(ctx):
   496         def progress(ctx):
   493             p.increment(item=("%d:%s" % (ctx.rev(), ctx)))
   497             p.increment(item=(b"%d:%s" % (ctx.rev(), ctx)))
   494 
   498 
   495         allowdivergence = self.ui.configbool(
   499         allowdivergence = self.ui.configbool(
   496             'experimental', 'evolution.allowdivergence'
   500             b'experimental', b'evolution.allowdivergence'
   497         )
   501         )
   498         for subset in sortsource(self.destmap):
   502         for subset in sortsource(self.destmap):
   499             sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
   503             sortedrevs = self.repo.revs(b'sort(%ld, -topo)', subset)
   500             if not allowdivergence:
   504             if not allowdivergence:
   501                 sortedrevs -= self.repo.revs(
   505                 sortedrevs -= self.repo.revs(
   502                     'descendants(%ld) and not %ld',
   506                     b'descendants(%ld) and not %ld',
   503                     self.obsoletewithoutsuccessorindestination,
   507                     self.obsoletewithoutsuccessorindestination,
   504                     self.obsoletewithoutsuccessorindestination,
   508                     self.obsoletewithoutsuccessorindestination,
   505                 )
   509                 )
   506             for rev in sortedrevs:
   510             for rev in sortedrevs:
   507                 self._rebasenode(tr, rev, allowdivergence, progress)
   511                 self._rebasenode(tr, rev, allowdivergence, progress)
   508         p.complete()
   512         p.complete()
   509         ui.note(_('rebase merging completed\n'))
   513         ui.note(_(b'rebase merging completed\n'))
   510 
   514 
   511     def _concludenode(self, rev, p1, p2, editor, commitmsg=None):
   515     def _concludenode(self, rev, p1, p2, editor, commitmsg=None):
   512         '''Commit the wd changes with parents p1 and p2.
   516         '''Commit the wd changes with parents p1 and p2.
   513 
   517 
   514         Reuse commit info from rev but also store useful information in extra.
   518         Reuse commit info from rev but also store useful information in extra.
   518         if commitmsg is None:
   522         if commitmsg is None:
   519             commitmsg = ctx.description()
   523             commitmsg = ctx.description()
   520         date = self.date
   524         date = self.date
   521         if date is None:
   525         if date is None:
   522             date = ctx.date()
   526             date = ctx.date()
   523         extra = {'rebase_source': ctx.hex()}
   527         extra = {b'rebase_source': ctx.hex()}
   524         for c in self.extrafns:
   528         for c in self.extrafns:
   525             c(ctx, extra)
   529             c(ctx, extra)
   526         keepbranch = self.keepbranchesf and repo[p1].branch() != ctx.branch()
   530         keepbranch = self.keepbranchesf and repo[p1].branch() != ctx.branch()
   527         destphase = max(ctx.phase(), phases.draft)
   531         destphase = max(ctx.phase(), phases.draft)
   528         overrides = {('phases', 'new-commit'): destphase}
   532         overrides = {(b'phases', b'new-commit'): destphase}
   529         if keepbranch:
   533         if keepbranch:
   530             overrides[('ui', 'allowemptycommit')] = True
   534             overrides[(b'ui', b'allowemptycommit')] = True
   531         with repo.ui.configoverride(overrides, 'rebase'):
   535         with repo.ui.configoverride(overrides, b'rebase'):
   532             if self.inmemory:
   536             if self.inmemory:
   533                 newnode = commitmemorynode(
   537                 newnode = commitmemorynode(
   534                     repo,
   538                     repo,
   535                     p1,
   539                     p1,
   536                     p2,
   540                     p2,
   565         repo, ui, opts = self.repo, self.ui, self.opts
   569         repo, ui, opts = self.repo, self.ui, self.opts
   566         dest = self.destmap[rev]
   570         dest = self.destmap[rev]
   567         ctx = repo[rev]
   571         ctx = repo[rev]
   568         desc = _ctxdesc(ctx)
   572         desc = _ctxdesc(ctx)
   569         if self.state[rev] == rev:
   573         if self.state[rev] == rev:
   570             ui.status(_('already rebased %s\n') % desc)
   574             ui.status(_(b'already rebased %s\n') % desc)
   571         elif (
   575         elif (
   572             not allowdivergence
   576             not allowdivergence
   573             and rev in self.obsoletewithoutsuccessorindestination
   577             and rev in self.obsoletewithoutsuccessorindestination
   574         ):
   578         ):
   575             msg = (
   579             msg = (
   576                 _(
   580                 _(
   577                     'note: not rebasing %s and its descendants as '
   581                     b'note: not rebasing %s and its descendants as '
   578                     'this would cause divergence\n'
   582                     b'this would cause divergence\n'
   579                 )
   583                 )
   580                 % desc
   584                 % desc
   581             )
   585             )
   582             repo.ui.status(msg)
   586             repo.ui.status(msg)
   583             self.skipped.add(rev)
   587             self.skipped.add(rev)
   584         elif rev in self.obsoletenotrebased:
   588         elif rev in self.obsoletenotrebased:
   585             succ = self.obsoletenotrebased[rev]
   589             succ = self.obsoletenotrebased[rev]
   586             if succ is None:
   590             if succ is None:
   587                 msg = (
   591                 msg = (
   588                     _('note: not rebasing %s, it has no ' 'successor\n') % desc
   592                     _(b'note: not rebasing %s, it has no ' b'successor\n')
       
   593                     % desc
   589                 )
   594                 )
   590             else:
   595             else:
   591                 succdesc = _ctxdesc(repo[succ])
   596                 succdesc = _ctxdesc(repo[succ])
   592                 msg = _(
   597                 msg = _(
   593                     'note: not rebasing %s, already in ' 'destination as %s\n'
   598                     b'note: not rebasing %s, already in ' b'destination as %s\n'
   594                 ) % (desc, succdesc)
   599                 ) % (desc, succdesc)
   595             repo.ui.status(msg)
   600             repo.ui.status(msg)
   596             # Make clearrebased aware state[rev] is not a true successor
   601             # Make clearrebased aware state[rev] is not a true successor
   597             self.skipped.add(rev)
   602             self.skipped.add(rev)
   598             # Record rev as moved to its desired destination in self.state.
   603             # Record rev as moved to its desired destination in self.state.
   600             dest = max(
   605             dest = max(
   601                 adjustdest(repo, rev, self.destmap, self.state, self.skipped)
   606                 adjustdest(repo, rev, self.destmap, self.state, self.skipped)
   602             )
   607             )
   603             self.state[rev] = dest
   608             self.state[rev] = dest
   604         elif self.state[rev] == revtodo:
   609         elif self.state[rev] == revtodo:
   605             ui.status(_('rebasing %s\n') % desc)
   610             ui.status(_(b'rebasing %s\n') % desc)
   606             progressfn(ctx)
   611             progressfn(ctx)
   607             p1, p2, base = defineparents(
   612             p1, p2, base = defineparents(
   608                 repo,
   613                 repo,
   609                 rev,
   614                 rev,
   610                 self.destmap,
   615                 self.destmap,
   611                 self.state,
   616                 self.state,
   612                 self.skipped,
   617                 self.skipped,
   613                 self.obsoletenotrebased,
   618                 self.obsoletenotrebased,
   614             )
   619             )
   615             if not self.inmemory and len(repo[None].parents()) == 2:
   620             if not self.inmemory and len(repo[None].parents()) == 2:
   616                 repo.ui.debug('resuming interrupted rebase\n')
   621                 repo.ui.debug(b'resuming interrupted rebase\n')
   617             else:
   622             else:
   618                 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
   623                 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
   619                 with ui.configoverride(overrides, 'rebase'):
   624                 with ui.configoverride(overrides, b'rebase'):
   620                     stats = rebasenode(
   625                     stats = rebasenode(
   621                         repo,
   626                         repo,
   622                         rev,
   627                         rev,
   623                         p1,
   628                         p1,
   624                         base,
   629                         base,
   630                         if self.inmemory:
   635                         if self.inmemory:
   631                             raise error.InMemoryMergeConflictsError()
   636                             raise error.InMemoryMergeConflictsError()
   632                         else:
   637                         else:
   633                             raise error.InterventionRequired(
   638                             raise error.InterventionRequired(
   634                                 _(
   639                                 _(
   635                                     'unresolved conflicts (see hg '
   640                                     b'unresolved conflicts (see hg '
   636                                     'resolve, then hg rebase --continue)'
   641                                     b'resolve, then hg rebase --continue)'
   637                                 )
   642                                 )
   638                             )
   643                             )
   639             if not self.collapsef:
   644             if not self.collapsef:
   640                 merging = p2 != nullrev
   645                 merging = p2 != nullrev
   641                 editform = cmdutil.mergeeditform(merging, 'rebase')
   646                 editform = cmdutil.mergeeditform(merging, b'rebase')
   642                 editor = cmdutil.getcommiteditor(
   647                 editor = cmdutil.getcommiteditor(
   643                     editform=editform, **pycompat.strkwargs(opts)
   648                     editform=editform, **pycompat.strkwargs(opts)
   644                 )
   649                 )
   645                 newnode = self._concludenode(rev, p1, p2, editor)
   650                 newnode = self._concludenode(rev, p1, p2, editor)
   646             else:
   651             else:
   651                     repo.setparents(repo[p1].node())
   656                     repo.setparents(repo[p1].node())
   652                 newnode = None
   657                 newnode = None
   653             # Update the state
   658             # Update the state
   654             if newnode is not None:
   659             if newnode is not None:
   655                 self.state[rev] = repo[newnode].rev()
   660                 self.state[rev] = repo[newnode].rev()
   656                 ui.debug('rebased as %s\n' % short(newnode))
   661                 ui.debug(b'rebased as %s\n' % short(newnode))
   657             else:
   662             else:
   658                 if not self.collapsef:
   663                 if not self.collapsef:
   659                     ui.warn(
   664                     ui.warn(
   660                         _(
   665                         _(
   661                             'note: not rebasing %s, its destination already '
   666                             b'note: not rebasing %s, its destination already '
   662                             'has all its changes\n'
   667                             b'has all its changes\n'
   663                         )
   668                         )
   664                         % desc
   669                         % desc
   665                     )
   670                     )
   666                     self.skipped.add(rev)
   671                     self.skipped.add(rev)
   667                 self.state[rev] = p1
   672                 self.state[rev] = p1
   668                 ui.debug('next revision set to %d\n' % p1)
   673                 ui.debug(b'next revision set to %d\n' % p1)
   669         else:
   674         else:
   670             ui.status(
   675             ui.status(
   671                 _('already rebased %s as %s\n') % (desc, repo[self.state[rev]])
   676                 _(b'already rebased %s as %s\n') % (desc, repo[self.state[rev]])
   672             )
   677             )
   673         if not tr:
   678         if not tr:
   674             # When not using single transaction, store state after each
   679             # When not using single transaction, store state after each
   675             # commit is completely done. On InterventionRequired, we thus
   680             # commit is completely done. On InterventionRequired, we thus
   676             # won't store the status. Instead, we'll hit the "len(parents) == 2"
   681             # won't store the status. Instead, we'll hit the "len(parents) == 2"
   677             # case and realize that the commit was in progress.
   682             # case and realize that the commit was in progress.
   678             self.storestatus()
   683             self.storestatus()
   679 
   684 
   680     def _finishrebase(self):
   685     def _finishrebase(self):
   681         repo, ui, opts = self.repo, self.ui, self.opts
   686         repo, ui, opts = self.repo, self.ui, self.opts
   682         fm = ui.formatter('rebase', opts)
   687         fm = ui.formatter(b'rebase', opts)
   683         fm.startitem()
   688         fm.startitem()
   684         if self.collapsef:
   689         if self.collapsef:
   685             p1, p2, _base = defineparents(
   690             p1, p2, _base = defineparents(
   686                 repo,
   691                 repo,
   687                 min(self.state),
   692                 min(self.state),
   688                 self.destmap,
   693                 self.destmap,
   689                 self.state,
   694                 self.state,
   690                 self.skipped,
   695                 self.skipped,
   691                 self.obsoletenotrebased,
   696                 self.obsoletenotrebased,
   692             )
   697             )
   693             editopt = opts.get('edit')
   698             editopt = opts.get(b'edit')
   694             editform = 'rebase.collapse'
   699             editform = b'rebase.collapse'
   695             if self.collapsemsg:
   700             if self.collapsemsg:
   696                 commitmsg = self.collapsemsg
   701                 commitmsg = self.collapsemsg
   697             else:
   702             else:
   698                 commitmsg = 'Collapsed revision'
   703                 commitmsg = b'Collapsed revision'
   699                 for rebased in sorted(self.state):
   704                 for rebased in sorted(self.state):
   700                     if rebased not in self.skipped:
   705                     if rebased not in self.skipped:
   701                         commitmsg += '\n* %s' % repo[rebased].description()
   706                         commitmsg += b'\n* %s' % repo[rebased].description()
   702                 editopt = True
   707                 editopt = True
   703             editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
   708             editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
   704             revtoreuse = max(self.state)
   709             revtoreuse = max(self.state)
   705 
   710 
   706             newnode = self._concludenode(
   711             newnode = self._concludenode(
   710             if newnode is not None:
   715             if newnode is not None:
   711                 newrev = repo[newnode].rev()
   716                 newrev = repo[newnode].rev()
   712                 for oldrev in self.state:
   717                 for oldrev in self.state:
   713                     self.state[oldrev] = newrev
   718                     self.state[oldrev] = newrev
   714 
   719 
   715         if 'qtip' in repo.tags():
   720         if b'qtip' in repo.tags():
   716             updatemq(repo, self.state, self.skipped, **pycompat.strkwargs(opts))
   721             updatemq(repo, self.state, self.skipped, **pycompat.strkwargs(opts))
   717 
   722 
   718         # restore original working directory
   723         # restore original working directory
   719         # (we do this before stripping)
   724         # (we do this before stripping)
   720         newwd = self.state.get(self.originalwd, self.originalwd)
   725         newwd = self.state.get(self.originalwd, self.originalwd)
   721         if newwd < 0:
   726         if newwd < 0:
   722             # original directory is a parent of rebase set root or ignored
   727             # original directory is a parent of rebase set root or ignored
   723             newwd = self.originalwd
   728             newwd = self.originalwd
   724         if newwd not in [c.rev() for c in repo[None].parents()]:
   729         if newwd not in [c.rev() for c in repo[None].parents()]:
   725             ui.note(_("update back to initial working directory parent\n"))
   730             ui.note(_(b"update back to initial working directory parent\n"))
   726             hg.updaterepo(repo, newwd, overwrite=False)
   731             hg.updaterepo(repo, newwd, overwrite=False)
   727 
   732 
   728         collapsedas = None
   733         collapsedas = None
   729         if self.collapsef and not self.keepf:
   734         if self.collapsef and not self.keepf:
   730             collapsedas = newnode
   735             collapsedas = newnode
   741         )
   746         )
   742 
   747 
   743         clearstatus(repo)
   748         clearstatus(repo)
   744         clearcollapsemsg(repo)
   749         clearcollapsemsg(repo)
   745 
   750 
   746         ui.note(_("rebase completed\n"))
   751         ui.note(_(b"rebase completed\n"))
   747         util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
   752         util.unlinkpath(repo.sjoin(b'undo'), ignoremissing=True)
   748         if self.skipped:
   753         if self.skipped:
   749             skippedlen = len(self.skipped)
   754             skippedlen = len(self.skipped)
   750             ui.note(_("%d revisions have been skipped\n") % skippedlen)
   755             ui.note(_(b"%d revisions have been skipped\n") % skippedlen)
   751         fm.end()
   756         fm.end()
   752 
   757 
   753         if (
   758         if (
   754             self.activebookmark
   759             self.activebookmark
   755             and self.activebookmark in repo._bookmarks
   760             and self.activebookmark in repo._bookmarks
   756             and repo['.'].node() == repo._bookmarks[self.activebookmark]
   761             and repo[b'.'].node() == repo._bookmarks[self.activebookmark]
   757         ):
   762         ):
   758             bookmarks.activate(repo, self.activebookmark)
   763             bookmarks.activate(repo, self.activebookmark)
   759 
   764 
   760     def _abort(self, backup=True, suppwarns=False):
   765     def _abort(self, backup=True, suppwarns=False):
   761         '''Restore the repository to its original state.'''
   766         '''Restore the repository to its original state.'''
   773             ]
   778             ]
   774             immutable = [d for d in rebased if not repo[d].mutable()]
   779             immutable = [d for d in rebased if not repo[d].mutable()]
   775             cleanup = True
   780             cleanup = True
   776             if immutable:
   781             if immutable:
   777                 repo.ui.warn(
   782                 repo.ui.warn(
   778                     _("warning: can't clean up public changesets %s\n")
   783                     _(b"warning: can't clean up public changesets %s\n")
   779                     % ', '.join(bytes(repo[r]) for r in immutable),
   784                     % b', '.join(bytes(repo[r]) for r in immutable),
   780                     hint=_("see 'hg help phases' for details"),
   785                     hint=_(b"see 'hg help phases' for details"),
   781                 )
   786                 )
   782                 cleanup = False
   787                 cleanup = False
   783 
   788 
   784             descendants = set()
   789             descendants = set()
   785             if rebased:
   790             if rebased:
   786                 descendants = set(repo.changelog.descendants(rebased))
   791                 descendants = set(repo.changelog.descendants(rebased))
   787             if descendants - set(rebased):
   792             if descendants - set(rebased):
   788                 repo.ui.warn(
   793                 repo.ui.warn(
   789                     _(
   794                     _(
   790                         "warning: new changesets detected on "
   795                         b"warning: new changesets detected on "
   791                         "destination branch, can't strip\n"
   796                         b"destination branch, can't strip\n"
   792                     )
   797                     )
   793                 )
   798                 )
   794                 cleanup = False
   799                 cleanup = False
   795 
   800 
   796             if cleanup:
   801             if cleanup:
   797                 shouldupdate = False
   802                 shouldupdate = False
   798                 if rebased:
   803                 if rebased:
   799                     strippoints = [
   804                     strippoints = [
   800                         c.node() for c in repo.set('roots(%ld)', rebased)
   805                         c.node() for c in repo.set(b'roots(%ld)', rebased)
   801                     ]
   806                     ]
   802 
   807 
   803                 updateifonnodes = set(rebased)
   808                 updateifonnodes = set(rebased)
   804                 updateifonnodes.update(self.destmap.values())
   809                 updateifonnodes.update(self.destmap.values())
   805                 updateifonnodes.add(self.originalwd)
   810                 updateifonnodes.add(self.originalwd)
   806                 shouldupdate = repo['.'].rev() in updateifonnodes
   811                 shouldupdate = repo[b'.'].rev() in updateifonnodes
   807 
   812 
   808                 # Update away from the rebase if necessary
   813                 # Update away from the rebase if necessary
   809                 if shouldupdate or needupdate(repo, self.state):
   814                 if shouldupdate or needupdate(repo, self.state):
   810                     mergemod.update(
   815                     mergemod.update(
   811                         repo, self.originalwd, branchmerge=False, force=True
   816                         repo, self.originalwd, branchmerge=False, force=True
   820 
   825 
   821         finally:
   826         finally:
   822             clearstatus(repo)
   827             clearstatus(repo)
   823             clearcollapsemsg(repo)
   828             clearcollapsemsg(repo)
   824             if not suppwarns:
   829             if not suppwarns:
   825                 repo.ui.warn(_('rebase aborted\n'))
   830                 repo.ui.warn(_(b'rebase aborted\n'))
   826         return 0
   831         return 0
   827 
   832 
   828 
   833 
   829 @command(
   834 @command(
   830     'rebase',
   835     b'rebase',
   831     [
   836     [
   832         (
   837         (
   833             's',
   838             b's',
   834             'source',
   839             b'source',
   835             '',
   840             b'',
   836             _('rebase the specified changeset and descendants'),
   841             _(b'rebase the specified changeset and descendants'),
   837             _('REV'),
   842             _(b'REV'),
   838         ),
   843         ),
   839         (
   844         (
   840             'b',
   845             b'b',
   841             'base',
   846             b'base',
   842             '',
   847             b'',
   843             _('rebase everything from branching point of specified changeset'),
   848             _(b'rebase everything from branching point of specified changeset'),
   844             _('REV'),
   849             _(b'REV'),
   845         ),
   850         ),
   846         ('r', 'rev', [], _('rebase these revisions'), _('REV')),
   851         (b'r', b'rev', [], _(b'rebase these revisions'), _(b'REV')),
   847         ('d', 'dest', '', _('rebase onto the specified changeset'), _('REV')),
       
   848         ('', 'collapse', False, _('collapse the rebased changesets')),
       
   849         (
   852         (
   850             'm',
   853             b'd',
   851             'message',
   854             b'dest',
   852             '',
   855             b'',
   853             _('use text as collapse commit message'),
   856             _(b'rebase onto the specified changeset'),
   854             _('TEXT'),
   857             _(b'REV'),
   855         ),
   858         ),
   856         ('e', 'edit', False, _('invoke editor on commit messages')),
   859         (b'', b'collapse', False, _(b'collapse the rebased changesets')),
   857         (
   860         (
   858             'l',
   861             b'm',
   859             'logfile',
   862             b'message',
   860             '',
   863             b'',
   861             _('read collapse commit message from file'),
   864             _(b'use text as collapse commit message'),
   862             _('FILE'),
   865             _(b'TEXT'),
   863         ),
   866         ),
   864         ('k', 'keep', False, _('keep original changesets')),
   867         (b'e', b'edit', False, _(b'invoke editor on commit messages')),
   865         ('', 'keepbranches', False, _('keep original branch names')),
       
   866         ('D', 'detach', False, _('(DEPRECATED)')),
       
   867         ('i', 'interactive', False, _('(DEPRECATED)')),
       
   868         ('t', 'tool', '', _('specify merge tool')),
       
   869         ('', 'stop', False, _('stop interrupted rebase')),
       
   870         ('c', 'continue', False, _('continue an interrupted rebase')),
       
   871         ('a', 'abort', False, _('abort an interrupted rebase')),
       
   872         (
   868         (
   873             '',
   869             b'l',
   874             'auto-orphans',
   870             b'logfile',
   875             '',
   871             b'',
       
   872             _(b'read collapse commit message from file'),
       
   873             _(b'FILE'),
       
   874         ),
       
   875         (b'k', b'keep', False, _(b'keep original changesets')),
       
   876         (b'', b'keepbranches', False, _(b'keep original branch names')),
       
   877         (b'D', b'detach', False, _(b'(DEPRECATED)')),
       
   878         (b'i', b'interactive', False, _(b'(DEPRECATED)')),
       
   879         (b't', b'tool', b'', _(b'specify merge tool')),
       
   880         (b'', b'stop', False, _(b'stop interrupted rebase')),
       
   881         (b'c', b'continue', False, _(b'continue an interrupted rebase')),
       
   882         (b'a', b'abort', False, _(b'abort an interrupted rebase')),
       
   883         (
       
   884             b'',
       
   885             b'auto-orphans',
       
   886             b'',
   876             _(
   887             _(
   877                 'automatically rebase orphan revisions '
   888                 b'automatically rebase orphan revisions '
   878                 'in the specified revset (EXPERIMENTAL)'
   889                 b'in the specified revset (EXPERIMENTAL)'
   879             ),
   890             ),
   880         ),
   891         ),
   881     ]
   892     ]
   882     + cmdutil.dryrunopts
   893     + cmdutil.dryrunopts
   883     + cmdutil.formatteropts
   894     + cmdutil.formatteropts
   884     + cmdutil.confirmopts,
   895     + cmdutil.confirmopts,
   885     _('[-s REV | -b REV] [-d REV] [OPTION]'),
   896     _(b'[-s REV | -b REV] [-d REV] [OPTION]'),
   886     helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
   897     helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
   887 )
   898 )
   888 def rebase(ui, repo, **opts):
   899 def rebase(ui, repo, **opts):
   889     """move changeset (and descendants) to a different branch
   900     """move changeset (and descendants) to a different branch
   890 
   901 
  1007     Returns 0 on success, 1 if nothing to rebase or there are
  1018     Returns 0 on success, 1 if nothing to rebase or there are
  1008     unresolved conflicts.
  1019     unresolved conflicts.
  1009 
  1020 
  1010     """
  1021     """
  1011     opts = pycompat.byteskwargs(opts)
  1022     opts = pycompat.byteskwargs(opts)
  1012     inmemory = ui.configbool('rebase', 'experimental.inmemory')
  1023     inmemory = ui.configbool(b'rebase', b'experimental.inmemory')
  1013     dryrun = opts.get('dry_run')
  1024     dryrun = opts.get(b'dry_run')
  1014     confirm = opts.get('confirm')
  1025     confirm = opts.get(b'confirm')
  1015     selactions = [k for k in ['abort', 'stop', 'continue'] if opts.get(k)]
  1026     selactions = [k for k in [b'abort', b'stop', b'continue'] if opts.get(k)]
  1016     if len(selactions) > 1:
  1027     if len(selactions) > 1:
  1017         raise error.Abort(
  1028         raise error.Abort(
  1018             _('cannot use --%s with --%s') % tuple(selactions[:2])
  1029             _(b'cannot use --%s with --%s') % tuple(selactions[:2])
  1019         )
  1030         )
  1020     action = selactions[0] if selactions else None
  1031     action = selactions[0] if selactions else None
  1021     if dryrun and action:
  1032     if dryrun and action:
  1022         raise error.Abort(_('cannot specify both --dry-run and --%s') % action)
  1033         raise error.Abort(_(b'cannot specify both --dry-run and --%s') % action)
  1023     if confirm and action:
  1034     if confirm and action:
  1024         raise error.Abort(_('cannot specify both --confirm and --%s') % action)
  1035         raise error.Abort(_(b'cannot specify both --confirm and --%s') % action)
  1025     if dryrun and confirm:
  1036     if dryrun and confirm:
  1026         raise error.Abort(_('cannot specify both --confirm and --dry-run'))
  1037         raise error.Abort(_(b'cannot specify both --confirm and --dry-run'))
  1027 
  1038 
  1028     if action or repo.currenttransaction() is not None:
  1039     if action or repo.currenttransaction() is not None:
  1029         # in-memory rebase is not compatible with resuming rebases.
  1040         # in-memory rebase is not compatible with resuming rebases.
  1030         # (Or if it is run within a transaction, since the restart logic can
  1041         # (Or if it is run within a transaction, since the restart logic can
  1031         # fail the entire transaction.)
  1042         # fail the entire transaction.)
  1032         inmemory = False
  1043         inmemory = False
  1033 
  1044 
  1034     if opts.get('auto_orphans'):
  1045     if opts.get(b'auto_orphans'):
  1035         for key in opts:
  1046         for key in opts:
  1036             if key != 'auto_orphans' and opts.get(key):
  1047             if key != b'auto_orphans' and opts.get(key):
  1037                 raise error.Abort(
  1048                 raise error.Abort(
  1038                     _('--auto-orphans is incompatible with %s') % ('--' + key)
  1049                     _(b'--auto-orphans is incompatible with %s') % (b'--' + key)
  1039                 )
  1050                 )
  1040         userrevs = list(repo.revs(opts.get('auto_orphans')))
  1051         userrevs = list(repo.revs(opts.get(b'auto_orphans')))
  1041         opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)]
  1052         opts[b'rev'] = [revsetlang.formatspec(b'%ld and orphan()', userrevs)]
  1042         opts['dest'] = '_destautoorphanrebase(SRC)'
  1053         opts[b'dest'] = b'_destautoorphanrebase(SRC)'
  1043 
  1054 
  1044     if dryrun or confirm:
  1055     if dryrun or confirm:
  1045         return _dryrunrebase(ui, repo, action, opts)
  1056         return _dryrunrebase(ui, repo, action, opts)
  1046     elif action == 'stop':
  1057     elif action == b'stop':
  1047         rbsrt = rebaseruntime(repo, ui)
  1058         rbsrt = rebaseruntime(repo, ui)
  1048         with repo.wlock(), repo.lock():
  1059         with repo.wlock(), repo.lock():
  1049             rbsrt.restorestatus()
  1060             rbsrt.restorestatus()
  1050             if rbsrt.collapsef:
  1061             if rbsrt.collapsef:
  1051                 raise error.Abort(_("cannot stop in --collapse session"))
  1062                 raise error.Abort(_(b"cannot stop in --collapse session"))
  1052             allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
  1063             allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
  1053             if not (rbsrt.keepf or allowunstable):
  1064             if not (rbsrt.keepf or allowunstable):
  1054                 raise error.Abort(
  1065                 raise error.Abort(
  1055                     _(
  1066                     _(
  1056                         "cannot remove original changesets with"
  1067                         b"cannot remove original changesets with"
  1057                         " unrebased descendants"
  1068                         b" unrebased descendants"
  1058                     ),
  1069                     ),
  1059                     hint=_(
  1070                     hint=_(
  1060                         'either enable obsmarkers to allow unstable '
  1071                         b'either enable obsmarkers to allow unstable '
  1061                         'revisions or use --keep to keep original '
  1072                         b'revisions or use --keep to keep original '
  1062                         'changesets'
  1073                         b'changesets'
  1063                     ),
  1074                     ),
  1064                 )
  1075                 )
  1065             if needupdate(repo, rbsrt.state):
  1076             if needupdate(repo, rbsrt.state):
  1066                 # update to the current working revision
  1077                 # update to the current working revision
  1067                 # to clear interrupted merge
  1078                 # to clear interrupted merge
  1070             return 0
  1081             return 0
  1071     elif inmemory:
  1082     elif inmemory:
  1072         try:
  1083         try:
  1073             # in-memory merge doesn't support conflicts, so if we hit any, abort
  1084             # in-memory merge doesn't support conflicts, so if we hit any, abort
  1074             # and re-run as an on-disk merge.
  1085             # and re-run as an on-disk merge.
  1075             overrides = {('rebase', 'singletransaction'): True}
  1086             overrides = {(b'rebase', b'singletransaction'): True}
  1076             with ui.configoverride(overrides, 'rebase'):
  1087             with ui.configoverride(overrides, b'rebase'):
  1077                 return _dorebase(ui, repo, action, opts, inmemory=inmemory)
  1088                 return _dorebase(ui, repo, action, opts, inmemory=inmemory)
  1078         except error.InMemoryMergeConflictsError:
  1089         except error.InMemoryMergeConflictsError:
  1079             ui.warn(
  1090             ui.warn(
  1080                 _(
  1091                 _(
  1081                     'hit merge conflicts; re-running rebase without in-memory'
  1092                     b'hit merge conflicts; re-running rebase without in-memory'
  1082                     ' merge\n'
  1093                     b' merge\n'
  1083                 )
  1094                 )
  1084             )
  1095             )
  1085             # TODO: Make in-memory merge not use the on-disk merge state, so
  1096             # TODO: Make in-memory merge not use the on-disk merge state, so
  1086             # we don't have to clean it here
  1097             # we don't have to clean it here
  1087             mergemod.mergestate.clean(repo)
  1098             mergemod.mergestate.clean(repo)
  1092         return _dorebase(ui, repo, action, opts)
  1103         return _dorebase(ui, repo, action, opts)
  1093 
  1104 
  1094 
  1105 
  1095 def _dryrunrebase(ui, repo, action, opts):
  1106 def _dryrunrebase(ui, repo, action, opts):
  1096     rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
  1107     rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
  1097     confirm = opts.get('confirm')
  1108     confirm = opts.get(b'confirm')
  1098     if confirm:
  1109     if confirm:
  1099         ui.status(_('starting in-memory rebase\n'))
  1110         ui.status(_(b'starting in-memory rebase\n'))
  1100     else:
  1111     else:
  1101         ui.status(
  1112         ui.status(
  1102             _('starting dry-run rebase; repository will not be ' 'changed\n')
  1113             _(b'starting dry-run rebase; repository will not be ' b'changed\n')
  1103         )
  1114         )
  1104     with repo.wlock(), repo.lock():
  1115     with repo.wlock(), repo.lock():
  1105         needsabort = True
  1116         needsabort = True
  1106         try:
  1117         try:
  1107             overrides = {('rebase', 'singletransaction'): True}
  1118             overrides = {(b'rebase', b'singletransaction'): True}
  1108             with ui.configoverride(overrides, 'rebase'):
  1119             with ui.configoverride(overrides, b'rebase'):
  1109                 _origrebase(
  1120                 _origrebase(
  1110                     ui,
  1121                     ui,
  1111                     repo,
  1122                     repo,
  1112                     action,
  1123                     action,
  1113                     opts,
  1124                     opts,
  1114                     rbsrt,
  1125                     rbsrt,
  1115                     inmemory=True,
  1126                     inmemory=True,
  1116                     leaveunfinished=True,
  1127                     leaveunfinished=True,
  1117                 )
  1128                 )
  1118         except error.InMemoryMergeConflictsError:
  1129         except error.InMemoryMergeConflictsError:
  1119             ui.status(_('hit a merge conflict\n'))
  1130             ui.status(_(b'hit a merge conflict\n'))
  1120             return 1
  1131             return 1
  1121         except error.Abort:
  1132         except error.Abort:
  1122             needsabort = False
  1133             needsabort = False
  1123             raise
  1134             raise
  1124         else:
  1135         else:
  1125             if confirm:
  1136             if confirm:
  1126                 ui.status(_('rebase completed successfully\n'))
  1137                 ui.status(_(b'rebase completed successfully\n'))
  1127                 if not ui.promptchoice(
  1138                 if not ui.promptchoice(
  1128                     _(b'apply changes (yn)?' b'$$ &Yes $$ &No')
  1139                     _(b'apply changes (yn)?' b'$$ &Yes $$ &No')
  1129                 ):
  1140                 ):
  1130                     # finish unfinished rebase
  1141                     # finish unfinished rebase
  1131                     rbsrt._finishrebase()
  1142                     rbsrt._finishrebase()
  1135                     )
  1146                     )
  1136                 needsabort = False
  1147                 needsabort = False
  1137             else:
  1148             else:
  1138                 ui.status(
  1149                 ui.status(
  1139                     _(
  1150                     _(
  1140                         'dry-run rebase completed successfully; run without'
  1151                         b'dry-run rebase completed successfully; run without'
  1141                         ' -n/--dry-run to perform this rebase\n'
  1152                         b' -n/--dry-run to perform this rebase\n'
  1142                     )
  1153                     )
  1143                 )
  1154                 )
  1144             return 0
  1155             return 0
  1145         finally:
  1156         finally:
  1146             if needsabort:
  1157             if needsabort:
  1156 
  1167 
  1157 
  1168 
  1158 def _origrebase(
  1169 def _origrebase(
  1159     ui, repo, action, opts, rbsrt, inmemory=False, leaveunfinished=False
  1170     ui, repo, action, opts, rbsrt, inmemory=False, leaveunfinished=False
  1160 ):
  1171 ):
  1161     assert action != 'stop'
  1172     assert action != b'stop'
  1162     with repo.wlock(), repo.lock():
  1173     with repo.wlock(), repo.lock():
  1163         # Validate input and define rebasing points
  1174         # Validate input and define rebasing points
  1164         destf = opts.get('dest', None)
  1175         destf = opts.get(b'dest', None)
  1165         srcf = opts.get('source', None)
  1176         srcf = opts.get(b'source', None)
  1166         basef = opts.get('base', None)
  1177         basef = opts.get(b'base', None)
  1167         revf = opts.get('rev', [])
  1178         revf = opts.get(b'rev', [])
  1168         # search default destination in this space
  1179         # search default destination in this space
  1169         # used in the 'hg pull --rebase' case, see issue 5214.
  1180         # used in the 'hg pull --rebase' case, see issue 5214.
  1170         destspace = opts.get('_destspace')
  1181         destspace = opts.get(b'_destspace')
  1171         if opts.get('interactive'):
  1182         if opts.get(b'interactive'):
  1172             try:
  1183             try:
  1173                 if extensions.find('histedit'):
  1184                 if extensions.find(b'histedit'):
  1174                     enablehistedit = ''
  1185                     enablehistedit = b''
  1175             except KeyError:
  1186             except KeyError:
  1176                 enablehistedit = " --config extensions.histedit="
  1187                 enablehistedit = b" --config extensions.histedit="
  1177             help = "hg%s help -e histedit" % enablehistedit
  1188             help = b"hg%s help -e histedit" % enablehistedit
  1178             msg = (
  1189             msg = (
  1179                 _(
  1190                 _(
  1180                     "interactive history editing is supported by the "
  1191                     b"interactive history editing is supported by the "
  1181                     "'histedit' extension (see \"%s\")"
  1192                     b"'histedit' extension (see \"%s\")"
  1182                 )
  1193                 )
  1183                 % help
  1194                 % help
  1184             )
  1195             )
  1185             raise error.Abort(msg)
  1196             raise error.Abort(msg)
  1186 
  1197 
  1187         if rbsrt.collapsemsg and not rbsrt.collapsef:
  1198         if rbsrt.collapsemsg and not rbsrt.collapsef:
  1188             raise error.Abort(_('message can only be specified with collapse'))
  1199             raise error.Abort(_(b'message can only be specified with collapse'))
  1189 
  1200 
  1190         if action:
  1201         if action:
  1191             if rbsrt.collapsef:
  1202             if rbsrt.collapsef:
  1192                 raise error.Abort(
  1203                 raise error.Abort(
  1193                     _('cannot use collapse with continue or abort')
  1204                     _(b'cannot use collapse with continue or abort')
  1194                 )
  1205                 )
  1195             if srcf or basef or destf:
  1206             if srcf or basef or destf:
  1196                 raise error.Abort(
  1207                 raise error.Abort(
  1197                     _('abort and continue do not allow specifying revisions')
  1208                     _(b'abort and continue do not allow specifying revisions')
  1198                 )
  1209                 )
  1199             if action == 'abort' and opts.get('tool', False):
  1210             if action == b'abort' and opts.get(b'tool', False):
  1200                 ui.warn(_('tool option will be ignored\n'))
  1211                 ui.warn(_(b'tool option will be ignored\n'))
  1201             if action == 'continue':
  1212             if action == b'continue':
  1202                 ms = mergemod.mergestate.read(repo)
  1213                 ms = mergemod.mergestate.read(repo)
  1203                 mergeutil.checkunresolved(ms)
  1214                 mergeutil.checkunresolved(ms)
  1204 
  1215 
  1205             retcode = rbsrt._prepareabortorcontinue(isabort=(action == 'abort'))
  1216             retcode = rbsrt._prepareabortorcontinue(
       
  1217                 isabort=(action == b'abort')
       
  1218             )
  1206             if retcode is not None:
  1219             if retcode is not None:
  1207                 return retcode
  1220                 return retcode
  1208         else:
  1221         else:
  1209             destmap = _definedestmap(
  1222             destmap = _definedestmap(
  1210                 ui,
  1223                 ui,
  1221                 return retcode
  1234                 return retcode
  1222             storecollapsemsg(repo, rbsrt.collapsemsg)
  1235             storecollapsemsg(repo, rbsrt.collapsemsg)
  1223 
  1236 
  1224         tr = None
  1237         tr = None
  1225 
  1238 
  1226         singletr = ui.configbool('rebase', 'singletransaction')
  1239         singletr = ui.configbool(b'rebase', b'singletransaction')
  1227         if singletr:
  1240         if singletr:
  1228             tr = repo.transaction('rebase')
  1241             tr = repo.transaction(b'rebase')
  1229 
  1242 
  1230         # If `rebase.singletransaction` is enabled, wrap the entire operation in
  1243         # If `rebase.singletransaction` is enabled, wrap the entire operation in
  1231         # one transaction here. Otherwise, transactions are obtained when
  1244         # one transaction here. Otherwise, transactions are obtained when
  1232         # committing each node, which is slower but allows partial success.
  1245         # committing each node, which is slower but allows partial success.
  1233         with util.acceptintervention(tr):
  1246         with util.acceptintervention(tr):
  1234             # Same logic for the dirstate guard, except we don't create one when
  1247             # Same logic for the dirstate guard, except we don't create one when
  1235             # rebasing in-memory (it's not needed).
  1248             # rebasing in-memory (it's not needed).
  1236             dsguard = None
  1249             dsguard = None
  1237             if singletr and not inmemory:
  1250             if singletr and not inmemory:
  1238                 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
  1251                 dsguard = dirstateguard.dirstateguard(repo, b'rebase')
  1239             with util.acceptintervention(dsguard):
  1252             with util.acceptintervention(dsguard):
  1240                 rbsrt._performrebase(tr)
  1253                 rbsrt._performrebase(tr)
  1241                 if not leaveunfinished:
  1254                 if not leaveunfinished:
  1242                     rbsrt._finishrebase()
  1255                     rbsrt._finishrebase()
  1243 
  1256 
  1257         revf = []
  1270         revf = []
  1258 
  1271 
  1259     # destspace is here to work around issues with `hg pull --rebase` see
  1272     # destspace is here to work around issues with `hg pull --rebase` see
  1260     # issue5214 for details
  1273     # issue5214 for details
  1261     if srcf and basef:
  1274     if srcf and basef:
  1262         raise error.Abort(_('cannot specify both a source and a base'))
  1275         raise error.Abort(_(b'cannot specify both a source and a base'))
  1263     if revf and basef:
  1276     if revf and basef:
  1264         raise error.Abort(_('cannot specify both a revision and a base'))
  1277         raise error.Abort(_(b'cannot specify both a revision and a base'))
  1265     if revf and srcf:
  1278     if revf and srcf:
  1266         raise error.Abort(_('cannot specify both a revision and a source'))
  1279         raise error.Abort(_(b'cannot specify both a revision and a source'))
  1267 
  1280 
  1268     if not inmemory:
  1281     if not inmemory:
  1269         cmdutil.checkunfinished(repo)
  1282         cmdutil.checkunfinished(repo)
  1270         cmdutil.bailifchanged(repo)
  1283         cmdutil.bailifchanged(repo)
  1271 
  1284 
  1272     if ui.configbool('commands', 'rebase.requiredest') and not destf:
  1285     if ui.configbool(b'commands', b'rebase.requiredest') and not destf:
  1273         raise error.Abort(
  1286         raise error.Abort(
  1274             _('you must specify a destination'), hint=_('use: hg rebase -d REV')
  1287             _(b'you must specify a destination'),
       
  1288             hint=_(b'use: hg rebase -d REV'),
  1275         )
  1289         )
  1276 
  1290 
  1277     dest = None
  1291     dest = None
  1278 
  1292 
  1279     if revf:
  1293     if revf:
  1280         rebaseset = scmutil.revrange(repo, revf)
  1294         rebaseset = scmutil.revrange(repo, revf)
  1281         if not rebaseset:
  1295         if not rebaseset:
  1282             ui.status(_('empty "rev" revision set - nothing to rebase\n'))
  1296             ui.status(_(b'empty "rev" revision set - nothing to rebase\n'))
  1283             return None
  1297             return None
  1284     elif srcf:
  1298     elif srcf:
  1285         src = scmutil.revrange(repo, [srcf])
  1299         src = scmutil.revrange(repo, [srcf])
  1286         if not src:
  1300         if not src:
  1287             ui.status(_('empty "source" revision set - nothing to rebase\n'))
  1301             ui.status(_(b'empty "source" revision set - nothing to rebase\n'))
  1288             return None
  1302             return None
  1289         rebaseset = repo.revs('(%ld)::', src)
  1303         rebaseset = repo.revs(b'(%ld)::', src)
  1290         assert rebaseset
  1304         assert rebaseset
  1291     else:
  1305     else:
  1292         base = scmutil.revrange(repo, [basef or '.'])
  1306         base = scmutil.revrange(repo, [basef or b'.'])
  1293         if not base:
  1307         if not base:
  1294             ui.status(
  1308             ui.status(
  1295                 _('empty "base" revision set - ' "can't compute rebase set\n")
  1309                 _(b'empty "base" revision set - ' b"can't compute rebase set\n")
  1296             )
  1310             )
  1297             return None
  1311             return None
  1298         if destf:
  1312         if destf:
  1299             # --base does not support multiple destinations
  1313             # --base does not support multiple destinations
  1300             dest = scmutil.revsingle(repo, destf)
  1314             dest = scmutil.revsingle(repo, destf)
  1303             destf = bytes(dest)
  1317             destf = bytes(dest)
  1304 
  1318 
  1305         roots = []  # selected children of branching points
  1319         roots = []  # selected children of branching points
  1306         bpbase = {}  # {branchingpoint: [origbase]}
  1320         bpbase = {}  # {branchingpoint: [origbase]}
  1307         for b in base:  # group bases by branching points
  1321         for b in base:  # group bases by branching points
  1308             bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
  1322             bp = repo.revs(b'ancestor(%d, %d)', b, dest.rev()).first()
  1309             bpbase[bp] = bpbase.get(bp, []) + [b]
  1323             bpbase[bp] = bpbase.get(bp, []) + [b]
  1310         if None in bpbase:
  1324         if None in bpbase:
  1311             # emulate the old behavior, showing "nothing to rebase" (a better
  1325             # emulate the old behavior, showing "nothing to rebase" (a better
  1312             # behavior may be abort with "cannot find branching point" error)
  1326             # behavior may be abort with "cannot find branching point" error)
  1313             bpbase.clear()
  1327             bpbase.clear()
  1314         for bp, bs in bpbase.iteritems():  # calculate roots
  1328         for bp, bs in bpbase.iteritems():  # calculate roots
  1315             roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
  1329             roots += list(repo.revs(b'children(%d) & ancestors(%ld)', bp, bs))
  1316 
  1330 
  1317         rebaseset = repo.revs('%ld::', roots)
  1331         rebaseset = repo.revs(b'%ld::', roots)
  1318 
  1332 
  1319         if not rebaseset:
  1333         if not rebaseset:
  1320             # transform to list because smartsets are not comparable to
  1334             # transform to list because smartsets are not comparable to
  1321             # lists. This should be improved to honor laziness of
  1335             # lists. This should be improved to honor laziness of
  1322             # smartset.
  1336             # smartset.
  1323             if list(base) == [dest.rev()]:
  1337             if list(base) == [dest.rev()]:
  1324                 if basef:
  1338                 if basef:
  1325                     ui.status(
  1339                     ui.status(
  1326                         _(
  1340                         _(
  1327                             'nothing to rebase - %s is both "base"'
  1341                             b'nothing to rebase - %s is both "base"'
  1328                             ' and destination\n'
  1342                             b' and destination\n'
  1329                         )
  1343                         )
  1330                         % dest
  1344                         % dest
  1331                     )
  1345                     )
  1332                 else:
  1346                 else:
  1333                     ui.status(
  1347                     ui.status(
  1334                         _(
  1348                         _(
  1335                             'nothing to rebase - working directory '
  1349                             b'nothing to rebase - working directory '
  1336                             'parent is also destination\n'
  1350                             b'parent is also destination\n'
  1337                         )
  1351                         )
  1338                     )
  1352                     )
  1339             elif not repo.revs('%ld - ::%d', base, dest.rev()):
  1353             elif not repo.revs(b'%ld - ::%d', base, dest.rev()):
  1340                 if basef:
  1354                 if basef:
  1341                     ui.status(
  1355                     ui.status(
  1342                         _(
  1356                         _(
  1343                             'nothing to rebase - "base" %s is '
  1357                             b'nothing to rebase - "base" %s is '
  1344                             'already an ancestor of destination '
  1358                             b'already an ancestor of destination '
  1345                             '%s\n'
  1359                             b'%s\n'
  1346                         )
  1360                         )
  1347                         % ('+'.join(bytes(repo[r]) for r in base), dest)
  1361                         % (b'+'.join(bytes(repo[r]) for r in base), dest)
  1348                     )
  1362                     )
  1349                 else:
  1363                 else:
  1350                     ui.status(
  1364                     ui.status(
  1351                         _(
  1365                         _(
  1352                             'nothing to rebase - working '
  1366                             b'nothing to rebase - working '
  1353                             'directory parent is already an '
  1367                             b'directory parent is already an '
  1354                             'ancestor of destination %s\n'
  1368                             b'ancestor of destination %s\n'
  1355                         )
  1369                         )
  1356                         % dest
  1370                         % dest
  1357                     )
  1371                     )
  1358             else:  # can it happen?
  1372             else:  # can it happen?
  1359                 ui.status(
  1373                 ui.status(
  1360                     _('nothing to rebase from %s to %s\n')
  1374                     _(b'nothing to rebase from %s to %s\n')
  1361                     % ('+'.join(bytes(repo[r]) for r in base), dest)
  1375                     % (b'+'.join(bytes(repo[r]) for r in base), dest)
  1362                 )
  1376                 )
  1363             return None
  1377             return None
  1364 
  1378 
  1365     rebasingwcp = repo['.'].rev() in rebaseset
  1379     rebasingwcp = repo[b'.'].rev() in rebaseset
  1366     ui.log(
  1380     ui.log(
  1367         "rebase",
  1381         b"rebase",
  1368         "rebasing working copy parent: %r\n",
  1382         b"rebasing working copy parent: %r\n",
  1369         rebasingwcp,
  1383         rebasingwcp,
  1370         rebase_rebasing_wcp=rebasingwcp,
  1384         rebase_rebasing_wcp=rebasingwcp,
  1371     )
  1385     )
  1372     if inmemory and rebasingwcp:
  1386     if inmemory and rebasingwcp:
  1373         # Check these since we did not before.
  1387         # Check these since we did not before.
  1376 
  1390 
  1377     if not destf:
  1391     if not destf:
  1378         dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
  1392         dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
  1379         destf = bytes(dest)
  1393         destf = bytes(dest)
  1380 
  1394 
  1381     allsrc = revsetlang.formatspec('%ld', rebaseset)
  1395     allsrc = revsetlang.formatspec(b'%ld', rebaseset)
  1382     alias = {'ALLSRC': allsrc}
  1396     alias = {b'ALLSRC': allsrc}
  1383 
  1397 
  1384     if dest is None:
  1398     if dest is None:
  1385         try:
  1399         try:
  1386             # fast path: try to resolve dest without SRC alias
  1400             # fast path: try to resolve dest without SRC alias
  1387             dest = scmutil.revsingle(repo, destf, localalias=alias)
  1401             dest = scmutil.revsingle(repo, destf, localalias=alias)
  1388         except error.RepoLookupError:
  1402         except error.RepoLookupError:
  1389             # multi-dest path: resolve dest for each SRC separately
  1403             # multi-dest path: resolve dest for each SRC separately
  1390             destmap = {}
  1404             destmap = {}
  1391             for r in rebaseset:
  1405             for r in rebaseset:
  1392                 alias['SRC'] = revsetlang.formatspec('%d', r)
  1406                 alias[b'SRC'] = revsetlang.formatspec(b'%d', r)
  1393                 # use repo.anyrevs instead of scmutil.revsingle because we
  1407                 # use repo.anyrevs instead of scmutil.revsingle because we
  1394                 # don't want to abort if destset is empty.
  1408                 # don't want to abort if destset is empty.
  1395                 destset = repo.anyrevs([destf], user=True, localalias=alias)
  1409                 destset = repo.anyrevs([destf], user=True, localalias=alias)
  1396                 size = len(destset)
  1410                 size = len(destset)
  1397                 if size == 1:
  1411                 if size == 1:
  1398                     destmap[r] = destset.first()
  1412                     destmap[r] = destset.first()
  1399                 elif size == 0:
  1413                 elif size == 0:
  1400                     ui.note(_('skipping %s - empty destination\n') % repo[r])
  1414                     ui.note(_(b'skipping %s - empty destination\n') % repo[r])
  1401                 else:
  1415                 else:
  1402                     raise error.Abort(
  1416                     raise error.Abort(
  1403                         _('rebase destination for %s is not ' 'unique')
  1417                         _(b'rebase destination for %s is not ' b'unique')
  1404                         % repo[r]
  1418                         % repo[r]
  1405                     )
  1419                     )
  1406 
  1420 
  1407     if dest is not None:
  1421     if dest is not None:
  1408         # single-dest case: assign dest to each rev in rebaseset
  1422         # single-dest case: assign dest to each rev in rebaseset
  1409         destrev = dest.rev()
  1423         destrev = dest.rev()
  1410         destmap = {r: destrev for r in rebaseset}  # {srcrev: destrev}
  1424         destmap = {r: destrev for r in rebaseset}  # {srcrev: destrev}
  1411 
  1425 
  1412     if not destmap:
  1426     if not destmap:
  1413         ui.status(_('nothing to rebase - empty destination\n'))
  1427         ui.status(_(b'nothing to rebase - empty destination\n'))
  1414         return None
  1428         return None
  1415 
  1429 
  1416     return destmap
  1430     return destmap
  1417 
  1431 
  1418 
  1432 
  1433         return nullrev
  1447         return nullrev
  1434     if len(parents) == 1:
  1448     if len(parents) == 1:
  1435         return parents.pop()
  1449         return parents.pop()
  1436     raise error.Abort(
  1450     raise error.Abort(
  1437         _(
  1451         _(
  1438             'unable to collapse on top of %d, there is more '
  1452             b'unable to collapse on top of %d, there is more '
  1439             'than one external parent: %s'
  1453             b'than one external parent: %s'
  1440         )
  1454         )
  1441         % (max(destancestors), ', '.join("%d" % p for p in sorted(parents)))
  1455         % (max(destancestors), b', '.join(b"%d" % p for p in sorted(parents)))
  1442     )
  1456     )
  1443 
  1457 
  1444 
  1458 
  1445 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg):
  1459 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg):
  1446     '''Commit the memory changes with parents p1 and p2.
  1460     '''Commit the memory changes with parents p1 and p2.
  1447     Return node of committed revision.'''
  1461     Return node of committed revision.'''
  1448     # Replicates the empty check in ``repo.commit``.
  1462     # Replicates the empty check in ``repo.commit``.
  1449     if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
  1463     if wctx.isempty() and not repo.ui.configbool(b'ui', b'allowemptycommit'):
  1450         return None
  1464         return None
  1451 
  1465 
  1452     # By convention, ``extra['branch']`` (set by extrafn) clobbers
  1466     # By convention, ``extra['branch']`` (set by extrafn) clobbers
  1453     # ``branch`` (used when passing ``--keepbranches``).
  1467     # ``branch`` (used when passing ``--keepbranches``).
  1454     branch = repo[p1].branch()
  1468     branch = repo[p1].branch()
  1455     if 'branch' in extra:
  1469     if b'branch' in extra:
  1456         branch = extra['branch']
  1470         branch = extra[b'branch']
  1457 
  1471 
  1458     memctx = wctx.tomemctx(
  1472     memctx = wctx.tomemctx(
  1459         commitmsg,
  1473         commitmsg,
  1460         parents=(p1, p2),
  1474         parents=(p1, p2),
  1461         date=date,
  1475         date=date,
  1471 
  1485 
  1472 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg):
  1486 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg):
  1473     '''Commit the wd changes with parents p1 and p2.
  1487     '''Commit the wd changes with parents p1 and p2.
  1474     Return node of committed revision.'''
  1488     Return node of committed revision.'''
  1475     dsguard = util.nullcontextmanager()
  1489     dsguard = util.nullcontextmanager()
  1476     if not repo.ui.configbool('rebase', 'singletransaction'):
  1490     if not repo.ui.configbool(b'rebase', b'singletransaction'):
  1477         dsguard = dirstateguard.dirstateguard(repo, 'rebase')
  1491         dsguard = dirstateguard.dirstateguard(repo, b'rebase')
  1478     with dsguard:
  1492     with dsguard:
  1479         repo.setparents(repo[p1].node(), repo[p2].node())
  1493         repo.setparents(repo[p1].node(), repo[p2].node())
  1480 
  1494 
  1481         # Commit might fail if unresolved files exist
  1495         # Commit might fail if unresolved files exist
  1482         newnode = repo.commit(
  1496         newnode = repo.commit(
  1486         repo.dirstate.setbranch(repo[newnode].branch())
  1500         repo.dirstate.setbranch(repo[newnode].branch())
  1487         return newnode
  1501         return newnode
  1488 
  1502 
  1489 
  1503 
  1490 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
  1504 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
  1491     'Rebase a single revision rev on top of p1 using base as merge ancestor'
  1505     b'Rebase a single revision rev on top of p1 using base as merge ancestor'
  1492     # Merge phase
  1506     # Merge phase
  1493     # Update to destination and merge it with local
  1507     # Update to destination and merge it with local
  1494     if wctx.isinmemory():
  1508     if wctx.isinmemory():
  1495         wctx.setbase(repo[p1])
  1509         wctx.setbase(repo[p1])
  1496     else:
  1510     else:
  1497         if repo['.'].rev() != p1:
  1511         if repo[b'.'].rev() != p1:
  1498             repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
  1512             repo.ui.debug(b" update to %d:%s\n" % (p1, repo[p1]))
  1499             mergemod.update(repo, p1, branchmerge=False, force=True)
  1513             mergemod.update(repo, p1, branchmerge=False, force=True)
  1500         else:
  1514         else:
  1501             repo.ui.debug(" already in destination\n")
  1515             repo.ui.debug(b" already in destination\n")
  1502         # This is, alas, necessary to invalidate workingctx's manifest cache,
  1516         # This is, alas, necessary to invalidate workingctx's manifest cache,
  1503         # as well as other data we litter on it in other places.
  1517         # as well as other data we litter on it in other places.
  1504         wctx = repo[None]
  1518         wctx = repo[None]
  1505         repo.dirstate.write(repo.currenttransaction())
  1519         repo.dirstate.write(repo.currenttransaction())
  1506     repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
  1520     repo.ui.debug(b" merge against %d:%s\n" % (rev, repo[rev]))
  1507     if base is not None:
  1521     if base is not None:
  1508         repo.ui.debug("   detach base %d:%s\n" % (base, repo[base]))
  1522         repo.ui.debug(b"   detach base %d:%s\n" % (base, repo[base]))
  1509     # When collapsing in-place, the parent is the common ancestor, we
  1523     # When collapsing in-place, the parent is the common ancestor, we
  1510     # have to allow merging with it.
  1524     # have to allow merging with it.
  1511     stats = mergemod.update(
  1525     stats = mergemod.update(
  1512         repo,
  1526         repo,
  1513         rev,
  1527         rev,
  1514         branchmerge=True,
  1528         branchmerge=True,
  1515         force=True,
  1529         force=True,
  1516         ancestor=base,
  1530         ancestor=base,
  1517         mergeancestor=collapse,
  1531         mergeancestor=collapse,
  1518         labels=['dest', 'source'],
  1532         labels=[b'dest', b'source'],
  1519         wc=wctx,
  1533         wc=wctx,
  1520     )
  1534     )
  1521     if collapse:
  1535     if collapse:
  1522         copies.duplicatecopies(repo, wctx, rev, dest)
  1536         copies.duplicatecopies(repo, wctx, rev, dest)
  1523     else:
  1537     else:
  1593 
  1607 
  1594     result = []
  1608     result = []
  1595     for prev in repo.changelog.parentrevs(rev):
  1609     for prev in repo.changelog.parentrevs(rev):
  1596         adjusted = dest
  1610         adjusted = dest
  1597         if prev != nullrev:
  1611         if prev != nullrev:
  1598             candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
  1612             candidate = repo.revs(b'max(%ld and (::%d))', source, prev).first()
  1599             if candidate is not None:
  1613             if candidate is not None:
  1600                 adjusted = state[candidate]
  1614                 adjusted = state[candidate]
  1601         if adjusted == dest and dest in state:
  1615         if adjusted == dest and dest in state:
  1602             adjusted = state[dest]
  1616             adjusted = state[dest]
  1603             if adjusted == revtodo:
  1617             if adjusted == revtodo:
  1604                 # sortsource should produce an order that makes this impossible
  1618                 # sortsource should produce an order that makes this impossible
  1605                 raise error.ProgrammingError(
  1619                 raise error.ProgrammingError(
  1606                     'rev %d should be rebased already at this time' % dest
  1620                     b'rev %d should be rebased already at this time' % dest
  1607                 )
  1621                 )
  1608         result.append(adjusted)
  1622         result.append(adjusted)
  1609     return result
  1623     return result
  1610 
  1624 
  1611 
  1625 
  1616     `rebaseobsrevs`: set of obsolete revision in source
  1630     `rebaseobsrevs`: set of obsolete revision in source
  1617     `rebaseobsskipped`: set of revisions from source skipped because they have
  1631     `rebaseobsskipped`: set of revisions from source skipped because they have
  1618     successors in destination or no non-obsolete successor.
  1632     successors in destination or no non-obsolete successor.
  1619     """
  1633     """
  1620     # Obsolete node with successors not in dest leads to divergence
  1634     # Obsolete node with successors not in dest leads to divergence
  1621     divergenceok = ui.configbool('experimental', 'evolution.allowdivergence')
  1635     divergenceok = ui.configbool(b'experimental', b'evolution.allowdivergence')
  1622     divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
  1636     divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
  1623 
  1637 
  1624     if divergencebasecandidates and not divergenceok:
  1638     if divergencebasecandidates and not divergenceok:
  1625         divhashes = (bytes(repo[r]) for r in divergencebasecandidates)
  1639         divhashes = (bytes(repo[r]) for r in divergencebasecandidates)
  1626         msg = _("this rebase will cause " "divergences from: %s")
  1640         msg = _(b"this rebase will cause " b"divergences from: %s")
  1627         h = _(
  1641         h = _(
  1628             "to force the rebase please set "
  1642             b"to force the rebase please set "
  1629             "experimental.evolution.allowdivergence=True"
  1643             b"experimental.evolution.allowdivergence=True"
  1630         )
  1644         )
  1631         raise error.Abort(msg % (",".join(divhashes),), hint=h)
  1645         raise error.Abort(msg % (b",".join(divhashes),), hint=h)
  1632 
  1646 
  1633 
  1647 
  1634 def successorrevs(unfi, rev):
  1648 def successorrevs(unfi, rev):
  1635     """yield revision numbers for successors of rev"""
  1649     """yield revision numbers for successors of rev"""
  1636     assert unfi.filtername is None
  1650     assert unfi.filtername is None
  1746         #    /|    # None of A and B will be changed to D and rebase fails.
  1760         #    /|    # None of A and B will be changed to D and rebase fails.
  1747         #   A B D
  1761         #   A B D
  1748         if set(newps) == set(oldps) and dest not in newps:
  1762         if set(newps) == set(oldps) and dest not in newps:
  1749             raise error.Abort(
  1763             raise error.Abort(
  1750                 _(
  1764                 _(
  1751                     'cannot rebase %d:%s without '
  1765                     b'cannot rebase %d:%s without '
  1752                     'moving at least one of its parents'
  1766                     b'moving at least one of its parents'
  1753                 )
  1767                 )
  1754                 % (rev, repo[rev])
  1768                 % (rev, repo[rev])
  1755             )
  1769             )
  1756 
  1770 
  1757     # Source should not be ancestor of dest. The check here guarantees it's
  1771     # Source should not be ancestor of dest. The check here guarantees it's
  1758     # impossible. With multi-dest, the initial check does not cover complex
  1772     # impossible. With multi-dest, the initial check does not cover complex
  1759     # cases since we don't have abstractions to dry-run rebase cheaply.
  1773     # cases since we don't have abstractions to dry-run rebase cheaply.
  1760     if any(p != nullrev and isancestor(rev, p) for p in newps):
  1774     if any(p != nullrev and isancestor(rev, p) for p in newps):
  1761         raise error.Abort(_('source is ancestor of destination'))
  1775         raise error.Abort(_(b'source is ancestor of destination'))
  1762 
  1776 
  1763     # "rebasenode" updates to new p1, use the corresponding merge base.
  1777     # "rebasenode" updates to new p1, use the corresponding merge base.
  1764     if bases[0] != nullrev:
  1778     if bases[0] != nullrev:
  1765         base = bases[0]
  1779         base = bases[0]
  1766     else:
  1780     else:
  1787             if base == nullrev:
  1801             if base == nullrev:
  1788                 continue
  1802                 continue
  1789             # Revisions in the side (not chosen as merge base) branch that
  1803             # Revisions in the side (not chosen as merge base) branch that
  1790             # might contain "surprising" contents
  1804             # might contain "surprising" contents
  1791             siderevs = list(
  1805             siderevs = list(
  1792                 repo.revs('((%ld-%d) %% (%d+%d))', bases, base, base, dest)
  1806                 repo.revs(b'((%ld-%d) %% (%d+%d))', bases, base, base, dest)
  1793             )
  1807             )
  1794 
  1808 
  1795             # If those revisions are covered by rebaseset, the result is good.
  1809             # If those revisions are covered by rebaseset, the result is good.
  1796             # A merge in rebaseset would be considered to cover its ancestors.
  1810             # A merge in rebaseset would be considered to cover its ancestors.
  1797             if siderevs:
  1811             if siderevs:
  1801                 merges = [
  1815                 merges = [
  1802                     r for r in rebaseset if cl.parentrevs(r)[1] != nullrev
  1816                     r for r in rebaseset if cl.parentrevs(r)[1] != nullrev
  1803                 ]
  1817                 ]
  1804                 unwanted[i] = list(
  1818                 unwanted[i] = list(
  1805                     repo.revs(
  1819                     repo.revs(
  1806                         '%ld - (::%ld) - %ld', siderevs, merges, rebaseset
  1820                         b'%ld - (::%ld) - %ld', siderevs, merges, rebaseset
  1807                     )
  1821                     )
  1808                 )
  1822                 )
  1809 
  1823 
  1810         # Choose a merge base that has a minimal number of unwanted revs.
  1824         # Choose a merge base that has a minimal number of unwanted revs.
  1811         l, i = min(
  1825         l, i = min(
  1823             newps[0], newps[i] = newps[i], newps[0]
  1837             newps[0], newps[i] = newps[i], newps[0]
  1824 
  1838 
  1825         # The merge will include unwanted revisions. Abort now. Revisit this if
  1839         # The merge will include unwanted revisions. Abort now. Revisit this if
  1826         # we have a more advanced merge algorithm that handles multiple bases.
  1840         # we have a more advanced merge algorithm that handles multiple bases.
  1827         if l > 0:
  1841         if l > 0:
  1828             unwanteddesc = _(' or ').join(
  1842             unwanteddesc = _(b' or ').join(
  1829                 (
  1843                 (
  1830                     ', '.join('%d:%s' % (r, repo[r]) for r in revs)
  1844                     b', '.join(b'%d:%s' % (r, repo[r]) for r in revs)
  1831                     for revs in unwanted
  1845                     for revs in unwanted
  1832                     if revs is not None
  1846                     if revs is not None
  1833                 )
  1847                 )
  1834             )
  1848             )
  1835             raise error.Abort(
  1849             raise error.Abort(
  1836                 _('rebasing %d:%s will include unwanted changes from %s')
  1850                 _(b'rebasing %d:%s will include unwanted changes from %s')
  1837                 % (rev, repo[rev], unwanteddesc)
  1851                 % (rev, repo[rev], unwanteddesc)
  1838             )
  1852             )
  1839 
  1853 
  1840     repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
  1854     repo.ui.debug(b" future parents are %d and %d\n" % tuple(newps))
  1841 
  1855 
  1842     return newps[0], newps[1], base
  1856     return newps[0], newps[1], base
  1843 
  1857 
  1844 
  1858 
  1845 def isagitpatch(repo, patchname):
  1859 def isagitpatch(repo, patchname):
  1846     'Return true if the given patch is in git format'
  1860     b'Return true if the given patch is in git format'
  1847     mqpatch = os.path.join(repo.mq.path, patchname)
  1861     mqpatch = os.path.join(repo.mq.path, patchname)
  1848     for line in patch.linereader(open(mqpatch, 'rb')):
  1862     for line in patch.linereader(open(mqpatch, b'rb')):
  1849         if line.startswith('diff --git'):
  1863         if line.startswith(b'diff --git'):
  1850             return True
  1864             return True
  1851     return False
  1865     return False
  1852 
  1866 
  1853 
  1867 
  1854 def updatemq(repo, state, skipped, **opts):
  1868 def updatemq(repo, state, skipped, **opts):
  1855     'Update rebased mq patches - finalize and then import them'
  1869     b'Update rebased mq patches - finalize and then import them'
  1856     mqrebase = {}
  1870     mqrebase = {}
  1857     mq = repo.mq
  1871     mq = repo.mq
  1858     original_series = mq.fullseries[:]
  1872     original_series = mq.fullseries[:]
  1859     skippedpatches = set()
  1873     skippedpatches = set()
  1860 
  1874 
  1861     for p in mq.applied:
  1875     for p in mq.applied:
  1862         rev = repo[p.node].rev()
  1876         rev = repo[p.node].rev()
  1863         if rev in state:
  1877         if rev in state:
  1864             repo.ui.debug(
  1878             repo.ui.debug(
  1865                 'revision %d is an mq patch (%s), finalize it.\n'
  1879                 b'revision %d is an mq patch (%s), finalize it.\n'
  1866                 % (rev, p.name)
  1880                 % (rev, p.name)
  1867             )
  1881             )
  1868             mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
  1882             mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
  1869         else:
  1883         else:
  1870             # Applied but not rebased, not sure this should happen
  1884             # Applied but not rebased, not sure this should happen
  1876         # We must start import from the newest revision
  1890         # We must start import from the newest revision
  1877         for rev in sorted(mqrebase, reverse=True):
  1891         for rev in sorted(mqrebase, reverse=True):
  1878             if rev not in skipped:
  1892             if rev not in skipped:
  1879                 name, isgit = mqrebase[rev]
  1893                 name, isgit = mqrebase[rev]
  1880                 repo.ui.note(
  1894                 repo.ui.note(
  1881                     _('updating mq patch %s to %d:%s\n')
  1895                     _(b'updating mq patch %s to %d:%s\n')
  1882                     % (name, state[rev], repo[state[rev]])
  1896                     % (name, state[rev], repo[state[rev]])
  1883                 )
  1897                 )
  1884                 mq.qimport(
  1898                 mq.qimport(
  1885                     repo, (), patchname=name, git=isgit, rev=["%d" % state[rev]]
  1899                     repo,
       
  1900                     (),
       
  1901                     patchname=name,
       
  1902                     git=isgit,
       
  1903                     rev=[b"%d" % state[rev]],
  1886                 )
  1904                 )
  1887             else:
  1905             else:
  1888                 # Rebased and skipped
  1906                 # Rebased and skipped
  1889                 skippedpatches.add(mqrebase[rev][0])
  1907                 skippedpatches.add(mqrebase[rev][0])
  1890 
  1908 
  1900         mq.seriesdirty = True
  1918         mq.seriesdirty = True
  1901         mq.savedirty()
  1919         mq.savedirty()
  1902 
  1920 
  1903 
  1921 
  1904 def storecollapsemsg(repo, collapsemsg):
  1922 def storecollapsemsg(repo, collapsemsg):
  1905     'Store the collapse message to allow recovery'
  1923     b'Store the collapse message to allow recovery'
  1906     collapsemsg = collapsemsg or ''
  1924     collapsemsg = collapsemsg or b''
  1907     f = repo.vfs("last-message.txt", "w")
  1925     f = repo.vfs(b"last-message.txt", b"w")
  1908     f.write("%s\n" % collapsemsg)
  1926     f.write(b"%s\n" % collapsemsg)
  1909     f.close()
  1927     f.close()
  1910 
  1928 
  1911 
  1929 
  1912 def clearcollapsemsg(repo):
  1930 def clearcollapsemsg(repo):
  1913     'Remove collapse message file'
  1931     b'Remove collapse message file'
  1914     repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
  1932     repo.vfs.unlinkpath(b"last-message.txt", ignoremissing=True)
  1915 
  1933 
  1916 
  1934 
  1917 def restorecollapsemsg(repo, isabort):
  1935 def restorecollapsemsg(repo, isabort):
  1918     'Restore previously stored collapse message'
  1936     b'Restore previously stored collapse message'
  1919     try:
  1937     try:
  1920         f = repo.vfs("last-message.txt")
  1938         f = repo.vfs(b"last-message.txt")
  1921         collapsemsg = f.readline().strip()
  1939         collapsemsg = f.readline().strip()
  1922         f.close()
  1940         f.close()
  1923     except IOError as err:
  1941     except IOError as err:
  1924         if err.errno != errno.ENOENT:
  1942         if err.errno != errno.ENOENT:
  1925             raise
  1943             raise
  1926         if isabort:
  1944         if isabort:
  1927             # Oh well, just abort like normal
  1945             # Oh well, just abort like normal
  1928             collapsemsg = ''
  1946             collapsemsg = b''
  1929         else:
  1947         else:
  1930             raise error.Abort(_('missing .hg/last-message.txt for rebase'))
  1948             raise error.Abort(_(b'missing .hg/last-message.txt for rebase'))
  1931     return collapsemsg
  1949     return collapsemsg
  1932 
  1950 
  1933 
  1951 
  1934 def clearstatus(repo):
  1952 def clearstatus(repo):
  1935     'Remove the status files'
  1953     b'Remove the status files'
  1936     # Make sure the active transaction won't write the state file
  1954     # Make sure the active transaction won't write the state file
  1937     tr = repo.currenttransaction()
  1955     tr = repo.currenttransaction()
  1938     if tr:
  1956     if tr:
  1939         tr.removefilegenerator('rebasestate')
  1957         tr.removefilegenerator(b'rebasestate')
  1940     repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
  1958     repo.vfs.unlinkpath(b"rebasestate", ignoremissing=True)
  1941 
  1959 
  1942 
  1960 
  1943 def needupdate(repo, state):
  1961 def needupdate(repo, state):
  1944     '''check whether we should `update --clean` away from a merge, or if
  1962     '''check whether we should `update --clean` away from a merge, or if
  1945     somehow the working dir got forcibly updated, e.g. by older hg'''
  1963     somehow the working dir got forcibly updated, e.g. by older hg'''
  1978         result = []
  1996         result = []
  1979         for r in srclist:
  1997         for r in srclist:
  1980             if destmap[r] not in srcset:
  1998             if destmap[r] not in srcset:
  1981                 result.append(r)
  1999                 result.append(r)
  1982         if not result:
  2000         if not result:
  1983             raise error.Abort(_('source and destination form a cycle'))
  2001             raise error.Abort(_(b'source and destination form a cycle'))
  1984         srcset -= set(result)
  2002         srcset -= set(result)
  1985         yield result
  2003         yield result
  1986 
  2004 
  1987 
  2005 
  1988 def buildstate(repo, destmap, collapse):
  2006 def buildstate(repo, destmap, collapse):
  1990 
  2008 
  1991     repo: repo
  2009     repo: repo
  1992     destmap: {srcrev: destrev}
  2010     destmap: {srcrev: destrev}
  1993     '''
  2011     '''
  1994     rebaseset = destmap.keys()
  2012     rebaseset = destmap.keys()
  1995     originalwd = repo['.'].rev()
  2013     originalwd = repo[b'.'].rev()
  1996 
  2014 
  1997     # This check isn't strictly necessary, since mq detects commits over an
  2015     # This check isn't strictly necessary, since mq detects commits over an
  1998     # applied patch. But it prevents messing up the working directory when
  2016     # applied patch. But it prevents messing up the working directory when
  1999     # a partially completed rebase is blocked by mq.
  2017     # a partially completed rebase is blocked by mq.
  2000     if 'qtip' in repo.tags():
  2018     if b'qtip' in repo.tags():
  2001         mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
  2019         mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
  2002         if set(destmap.values()) & mqapplied:
  2020         if set(destmap.values()) & mqapplied:
  2003             raise error.Abort(_('cannot rebase onto an applied mq patch'))
  2021             raise error.Abort(_(b'cannot rebase onto an applied mq patch'))
  2004 
  2022 
  2005     # Get "cycle" error early by exhausting the generator.
  2023     # Get "cycle" error early by exhausting the generator.
  2006     sortedsrc = list(sortsource(destmap))  # a list of sorted revs
  2024     sortedsrc = list(sortsource(destmap))  # a list of sorted revs
  2007     if not sortedsrc:
  2025     if not sortedsrc:
  2008         raise error.Abort(_('no matching revisions'))
  2026         raise error.Abort(_(b'no matching revisions'))
  2009 
  2027 
  2010     # Only check the first batch of revisions to rebase not depending on other
  2028     # Only check the first batch of revisions to rebase not depending on other
  2011     # rebaseset. This means "source is ancestor of destination" for the second
  2029     # rebaseset. This means "source is ancestor of destination" for the second
  2012     # (and following) batches of revisions are not checked here. We rely on
  2030     # (and following) batches of revisions are not checked here. We rely on
  2013     # "defineparents" to do that check.
  2031     # "defineparents" to do that check.
  2014     roots = list(repo.set('roots(%ld)', sortedsrc[0]))
  2032     roots = list(repo.set(b'roots(%ld)', sortedsrc[0]))
  2015     if not roots:
  2033     if not roots:
  2016         raise error.Abort(_('no matching revisions'))
  2034         raise error.Abort(_(b'no matching revisions'))
  2017 
  2035 
  2018     def revof(r):
  2036     def revof(r):
  2019         return r.rev()
  2037         return r.rev()
  2020 
  2038 
  2021     roots = sorted(roots, key=revof)
  2039     roots = sorted(roots, key=revof)
  2023     emptyrebase = len(sortedsrc) == 1
  2041     emptyrebase = len(sortedsrc) == 1
  2024     for root in roots:
  2042     for root in roots:
  2025         dest = repo[destmap[root.rev()]]
  2043         dest = repo[destmap[root.rev()]]
  2026         commonbase = root.ancestor(dest)
  2044         commonbase = root.ancestor(dest)
  2027         if commonbase == root:
  2045         if commonbase == root:
  2028             raise error.Abort(_('source is ancestor of destination'))
  2046             raise error.Abort(_(b'source is ancestor of destination'))
  2029         if commonbase == dest:
  2047         if commonbase == dest:
  2030             wctx = repo[None]
  2048             wctx = repo[None]
  2031             if dest == wctx.p1():
  2049             if dest == wctx.p1():
  2032                 # when rebasing to '.', it will use the current wd branch name
  2050                 # when rebasing to '.', it will use the current wd branch name
  2033                 samebranch = root.branch() == wctx.branch()
  2051                 samebranch = root.branch() == wctx.branch()
  2035                 samebranch = root.branch() == dest.branch()
  2053                 samebranch = root.branch() == dest.branch()
  2036             if not collapse and samebranch and dest in root.parents():
  2054             if not collapse and samebranch and dest in root.parents():
  2037                 # mark the revision as done by setting its new revision
  2055                 # mark the revision as done by setting its new revision
  2038                 # equal to its old (current) revisions
  2056                 # equal to its old (current) revisions
  2039                 state[root.rev()] = root.rev()
  2057                 state[root.rev()] = root.rev()
  2040                 repo.ui.debug('source is a child of destination\n')
  2058                 repo.ui.debug(b'source is a child of destination\n')
  2041                 continue
  2059                 continue
  2042 
  2060 
  2043         emptyrebase = False
  2061         emptyrebase = False
  2044         repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
  2062         repo.ui.debug(b'rebase onto %s starting from %s\n' % (dest, root))
  2045     if emptyrebase:
  2063     if emptyrebase:
  2046         return None
  2064         return None
  2047     for rev in sorted(state):
  2065     for rev in sorted(state):
  2048         parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
  2066         parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
  2049         # if all parents of this revision are done, then so is this revision
  2067         # if all parents of this revision are done, then so is this revision
  2102         fl = fm.formatlist
  2120         fl = fm.formatlist
  2103         fd = fm.formatdict
  2121         fd = fm.formatdict
  2104         changes = {}
  2122         changes = {}
  2105         for oldns, newn in replacements.iteritems():
  2123         for oldns, newn in replacements.iteritems():
  2106             for oldn in oldns:
  2124             for oldn in oldns:
  2107                 changes[hf(oldn)] = fl([hf(n) for n in newn], name='node')
  2125                 changes[hf(oldn)] = fl([hf(n) for n in newn], name=b'node')
  2108         nodechanges = fd(changes, key="oldnode", value="newnodes")
  2126         nodechanges = fd(changes, key=b"oldnode", value=b"newnodes")
  2109         fm.data(nodechanges=nodechanges)
  2127         fm.data(nodechanges=nodechanges)
  2110     if keepf:
  2128     if keepf:
  2111         replacements = {}
  2129         replacements = {}
  2112     scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup)
  2130     scmutil.cleanupnodes(repo, replacements, b'rebase', moves, backup=backup)
  2113 
  2131 
  2114 
  2132 
  2115 def pullrebase(orig, ui, repo, *args, **opts):
  2133 def pullrebase(orig, ui, repo, *args, **opts):
  2116     'Call rebase after pull if the latter has been invoked with --rebase'
  2134     b'Call rebase after pull if the latter has been invoked with --rebase'
  2117     if opts.get(r'rebase'):
  2135     if opts.get(r'rebase'):
  2118         if ui.configbool('commands', 'rebase.requiredest'):
  2136         if ui.configbool(b'commands', b'rebase.requiredest'):
  2119             msg = _('rebase destination required by configuration')
  2137             msg = _(b'rebase destination required by configuration')
  2120             hint = _('use hg pull followed by hg rebase -d DEST')
  2138             hint = _(b'use hg pull followed by hg rebase -d DEST')
  2121             raise error.Abort(msg, hint=hint)
  2139             raise error.Abort(msg, hint=hint)
  2122 
  2140 
  2123         with repo.wlock(), repo.lock():
  2141         with repo.wlock(), repo.lock():
  2124             if opts.get(r'update'):
  2142             if opts.get(r'update'):
  2125                 del opts[r'update']
  2143                 del opts[r'update']
  2126                 ui.debug(
  2144                 ui.debug(
  2127                     '--update and --rebase are not compatible, ignoring '
  2145                     b'--update and --rebase are not compatible, ignoring '
  2128                     'the update flag\n'
  2146                     b'the update flag\n'
  2129                 )
  2147                 )
  2130 
  2148 
  2131             cmdutil.checkunfinished(repo, skipmerge=True)
  2149             cmdutil.checkunfinished(repo, skipmerge=True)
  2132             cmdutil.bailifchanged(
  2150             cmdutil.bailifchanged(
  2133                 repo,
  2151                 repo,
  2134                 hint=_(
  2152                 hint=_(
  2135                     'cannot pull with rebase: '
  2153                     b'cannot pull with rebase: '
  2136                     'please commit or shelve your changes first'
  2154                     b'please commit or shelve your changes first'
  2137                 ),
  2155                 ),
  2138             )
  2156             )
  2139 
  2157 
  2140             revsprepull = len(repo)
  2158             revsprepull = len(repo)
  2141             origpostincoming = commands.postincoming
  2159             origpostincoming = commands.postincoming
  2164                 try:
  2182                 try:
  2165                     rebase(ui, repo, **opts)
  2183                     rebase(ui, repo, **opts)
  2166                 except error.NoMergeDestAbort:
  2184                 except error.NoMergeDestAbort:
  2167                     # we can maybe update instead
  2185                     # we can maybe update instead
  2168                     rev, _a, _b = destutil.destupdate(repo)
  2186                     rev, _a, _b = destutil.destupdate(repo)
  2169                     if rev == repo['.'].rev():
  2187                     if rev == repo[b'.'].rev():
  2170                         ui.status(_('nothing to rebase\n'))
  2188                         ui.status(_(b'nothing to rebase\n'))
  2171                     else:
  2189                     else:
  2172                         ui.status(_('nothing to rebase - updating instead\n'))
  2190                         ui.status(_(b'nothing to rebase - updating instead\n'))
  2173                         # not passing argument to get the bare update behavior
  2191                         # not passing argument to get the bare update behavior
  2174                         # with warning and trumpets
  2192                         # with warning and trumpets
  2175                         commands.update(ui, repo)
  2193                         commands.update(ui, repo)
  2176     else:
  2194     else:
  2177         if opts.get(r'tool'):
  2195         if opts.get(r'tool'):
  2178             raise error.Abort(_('--tool can only be used with --rebase'))
  2196             raise error.Abort(_(b'--tool can only be used with --rebase'))
  2179         ret = orig(ui, repo, *args, **opts)
  2197         ret = orig(ui, repo, *args, **opts)
  2180 
  2198 
  2181     return ret
  2199     return ret
  2182 
  2200 
  2183 
  2201 
  2203     obsoleteextinctsuccessors = set()
  2221     obsoleteextinctsuccessors = set()
  2204 
  2222 
  2205     assert repo.filtername is None
  2223     assert repo.filtername is None
  2206     cl = repo.changelog
  2224     cl = repo.changelog
  2207     nodemap = cl.nodemap
  2225     nodemap = cl.nodemap
  2208     extinctrevs = set(repo.revs('extinct()'))
  2226     extinctrevs = set(repo.revs(b'extinct()'))
  2209     for srcrev in rebaseobsrevs:
  2227     for srcrev in rebaseobsrevs:
  2210         srcnode = cl.node(srcrev)
  2228         srcnode = cl.node(srcrev)
  2211         # XXX: more advanced APIs are required to handle split correctly
  2229         # XXX: more advanced APIs are required to handle split correctly
  2212         successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
  2230         successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
  2213         # obsutil.allsuccessors includes node itself
  2231         # obsutil.allsuccessors includes node itself
  2256         rbsrt._performrebase(None)
  2274         rbsrt._performrebase(None)
  2257         rbsrt._finishrebase()
  2275         rbsrt._finishrebase()
  2258 
  2276 
  2259 
  2277 
  2260 def summaryhook(ui, repo):
  2278 def summaryhook(ui, repo):
  2261     if not repo.vfs.exists('rebasestate'):
  2279     if not repo.vfs.exists(b'rebasestate'):
  2262         return
  2280         return
  2263     try:
  2281     try:
  2264         rbsrt = rebaseruntime(repo, ui, {})
  2282         rbsrt = rebaseruntime(repo, ui, {})
  2265         rbsrt.restorestatus()
  2283         rbsrt.restorestatus()
  2266         state = rbsrt.state
  2284         state = rbsrt.state
  2267     except error.RepoLookupError:
  2285     except error.RepoLookupError:
  2268         # i18n: column positioning for "hg summary"
  2286         # i18n: column positioning for "hg summary"
  2269         msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
  2287         msg = _(b'rebase: (use "hg rebase --abort" to clear broken state)\n')
  2270         ui.write(msg)
  2288         ui.write(msg)
  2271         return
  2289         return
  2272     numrebased = len([i for i in state.itervalues() if i >= 0])
  2290     numrebased = len([i for i in state.itervalues() if i >= 0])
  2273     # i18n: column positioning for "hg summary"
  2291     # i18n: column positioning for "hg summary"
  2274     ui.write(
  2292     ui.write(
  2275         _('rebase: %s, %s (rebase --continue)\n')
  2293         _(b'rebase: %s, %s (rebase --continue)\n')
  2276         % (
  2294         % (
  2277             ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
  2295             ui.label(_(b'%d rebased'), b'rebase.rebased') % numrebased,
  2278             ui.label(_('%d remaining'), 'rebase.remaining')
  2296             ui.label(_(b'%d remaining'), b'rebase.remaining')
  2279             % (len(state) - numrebased),
  2297             % (len(state) - numrebased),
  2280         )
  2298         )
  2281     )
  2299     )
  2282 
  2300 
  2283 
  2301 
  2284 def uisetup(ui):
  2302 def uisetup(ui):
  2285     # Replace pull with a decorator to provide --rebase option
  2303     # Replace pull with a decorator to provide --rebase option
  2286     entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
  2304     entry = extensions.wrapcommand(commands.table, b'pull', pullrebase)
  2287     entry[1].append(
  2305     entry[1].append(
  2288         ('', 'rebase', None, _("rebase working directory to branch head"))
  2306         (b'', b'rebase', None, _(b"rebase working directory to branch head"))
  2289     )
  2307     )
  2290     entry[1].append(('t', 'tool', '', _("specify merge tool for rebase")))
  2308     entry[1].append((b't', b'tool', b'', _(b"specify merge tool for rebase")))
  2291     cmdutil.summaryhooks.add('rebase', summaryhook)
  2309     cmdutil.summaryhooks.add(b'rebase', summaryhook)
  2292     statemod.addunfinished(
  2310     statemod.addunfinished(
  2293         'rebase',
  2311         b'rebase',
  2294         fname='rebasestate',
  2312         fname=b'rebasestate',
  2295         stopflag=True,
  2313         stopflag=True,
  2296         continueflag=True,
  2314         continueflag=True,
  2297         abortfunc=abortrebase,
  2315         abortfunc=abortrebase,
  2298         continuefunc=continuerebase,
  2316         continuefunc=continuerebase,
  2299     )
  2317     )