hgext3rd/evolve/obshistory.py
changeset 4814 48b30ff742cb
parent 4801 16c1398b0063
child 4827 4c6dd20e8cc2
equal deleted inserted replaced
4812:67567d7f1174 4814:48b30ff742cb
    29 )
    29 )
    30 
    30 
    31 eh = exthelper.exthelper()
    31 eh = exthelper.exthelper()
    32 
    32 
    33 # Config
    33 # Config
    34 efd = {'default': True} # pass a default value unless the config is registered
    34 efd = {b'default': True} # pass a default value unless the config is registered
    35 
    35 
    36 @eh.extsetup
    36 @eh.extsetup
    37 def enableeffectflags(ui):
    37 def enableeffectflags(ui):
    38     item = (getattr(ui, '_knownconfig', {})
    38     item = (getattr(ui, '_knownconfig', {})
    39             .get('experimental', {})
    39             .get(b'experimental', {})
    40             .get('evolution.effect-flags'))
    40             .get(b'evolution.effect-flags'))
    41     if item is not None:
    41     if item is not None:
    42         item.default = True
    42         item.default = True
    43         efd.clear()
    43         efd.clear()
    44 
    44 
    45 @eh.command(
    45 @eh.command(
    77 
    77 
    78     Paths in the DAG are represented with '|', '/' and so forth.
    78     Paths in the DAG are represented with '|', '/' and so forth.
    79 
    79 
    80     Returns 0 on success.
    80     Returns 0 on success.
    81     """
    81     """
    82     ui.pager('obslog')
    82     ui.pager(b'obslog')
    83     revs = list(revs) + opts['rev']
    83     revs = list(revs) + opts['rev']
    84     if not revs:
    84     if not revs:
    85         revs = ['.']
    85         revs = [b'.']
    86     revs = scmutil.revrange(repo, revs)
    86     revs = scmutil.revrange(repo, revs)
    87 
    87 
    88     if opts['graph']:
    88     if opts['graph']:
    89         return _debugobshistorygraph(ui, repo, revs, opts)
    89         return _debugobshistorygraph(ui, repo, revs, opts)
    90 
    90 
   129             if not foundany:
   129             if not foundany:
   130                 fullsuccessorsets.append(compat._succs())
   130                 fullsuccessorsets.append(compat._succs())
   131 
   131 
   132     values = []
   132     values = []
   133     for sset in fullsuccessorsets:
   133     for sset in fullsuccessorsets:
   134         values.append({'successors': sset, 'markers': sset.markers})
   134         values.append({b'successors': sset, b'markers': sset.markers})
   135 
   135 
   136     return values
   136     return values
   137 
   137 
   138 class obsmarker_printer(compat.changesetprinter):
   138 class obsmarker_printer(compat.changesetprinter):
   139     """show (available) information about a node
   139     """show (available) information about a node
   152         super(obsmarker_printer, self).__init__(ui, repo, *args, **kwargs)
   152         super(obsmarker_printer, self).__init__(ui, repo, *args, **kwargs)
   153         diffopts = kwargs.get('diffopts', {})
   153         diffopts = kwargs.get('diffopts', {})
   154 
   154 
   155         # Compat 4.6
   155         # Compat 4.6
   156         if not util.safehasattr(self, "_includediff"):
   156         if not util.safehasattr(self, "_includediff"):
   157             self._includediff = diffopts and diffopts.get('patch')
   157             self._includediff = diffopts and diffopts.get(b'patch')
   158 
   158 
   159         self.template = diffopts and diffopts.get('template')
   159         self.template = diffopts and diffopts.get(b'template')
   160         self.filter = diffopts and diffopts.get('filternonlocal')
   160         self.filter = diffopts and diffopts.get(b'filternonlocal')
   161 
   161 
   162     def show(self, ctx, copies=None, matchfn=None, **props):
   162     def show(self, ctx, copies=None, matchfn=None, **props):
   163         if self.buffered:
   163         if self.buffered:
   164             self.ui.pushbuffer(labeled=True)
   164             self.ui.pushbuffer(labeled=True)
   165 
   165 
   166             changenode = ctx.node()
   166             changenode = ctx.node()
   167 
   167 
   168             _props = {"template": self.template}
   168             _props = {b"template": self.template}
   169             fm = self.ui.formatter('debugobshistory', _props)
   169             fm = self.ui.formatter(b'debugobshistory', _props)
   170 
   170 
   171             _debugobshistorydisplaynode(fm, self.repo, changenode)
   171             _debugobshistorydisplaynode(fm, self.repo, changenode)
   172 
   172 
   173             markerfm = fm.nested("markers")
   173             markerfm = fm.nested(b"markers")
   174 
   174 
   175             # Succs markers
   175             # Succs markers
   176             if self.filter is False:
   176             if self.filter is False:
   177                 succs = self.repo.obsstore.successors.get(changenode, ())
   177                 succs = self.repo.obsstore.successors.get(changenode, ())
   178                 succs = sorted(succs)
   178                 succs = sorted(succs)
   184 
   184 
   185             else:
   185             else:
   186                 r = _successorsandmarkers(self.repo, ctx)
   186                 r = _successorsandmarkers(self.repo, ctx)
   187 
   187 
   188                 for succset in sorted(r):
   188                 for succset in sorted(r):
   189                     markers = succset["markers"]
   189                     markers = succset[b"markers"]
   190                     if not markers:
   190                     if not markers:
   191                         continue
   191                         continue
   192                     successors = succset["successors"]
   192                     successors = succset[b"successors"]
   193                     _debugobshistorydisplaysuccsandmarkers(markerfm, successors, markers, ctx.node(), self.repo, self._includediff)
   193                     _debugobshistorydisplaysuccsandmarkers(markerfm, successors, markers, ctx.node(), self.repo, self._includediff)
   194 
   194 
   195             markerfm.end()
   195             markerfm.end()
   196 
   196 
   197             markerfm.plain('\n')
   197             markerfm.plain(b'\n')
   198             fm.end()
   198             fm.end()
   199 
   199 
   200             self.hunk[ctx.node()] = self.ui.popbuffer()
   200             self.hunk[ctx.node()] = self.ui.popbuffer()
   201         else:
   201         else:
   202             ### graph output is buffered only
   202             ### graph output is buffered only
   203             msg = 'cannot be used outside of the graphlog (yet)'
   203             msg = b'cannot be used outside of the graphlog (yet)'
   204             raise error.ProgrammingError(msg)
   204             raise error.ProgrammingError(msg)
   205 
   205 
   206     def flush(self, ctx):
   206     def flush(self, ctx):
   207         ''' changeset_printer has some logic around buffering data
   207         ''' changeset_printer has some logic around buffering data
   208         in self.headers that we don't use
   208         in self.headers that we don't use
   209         '''
   209         '''
   210         pass
   210         pass
   211 
   211 
   212 def patchavailable(node, repo, successors):
   212 def patchavailable(node, repo, successors):
   213     if node not in repo:
   213     if node not in repo:
   214         return False, "context is not local"
   214         return False, b"context is not local"
   215 
   215 
   216     if len(successors) == 0:
   216     if len(successors) == 0:
   217         return False, "no successors"
   217         return False, b"no successors"
   218     elif len(successors) > 1:
   218     elif len(successors) > 1:
   219         return False, "too many successors (%d)" % len(successors)
   219         return False, b"too many successors (%d)" % len(successors)
   220 
   220 
   221     succ = successors[0]
   221     succ = successors[0]
   222 
   222 
   223     if succ not in repo:
   223     if succ not in repo:
   224         return False, "successor is unknown locally"
   224         return False, b"successor is unknown locally"
   225 
   225 
   226     # Check that both node and succ have the same parents
   226     # Check that both node and succ have the same parents
   227     nodep1, nodep2 = repo[node].p1(), repo[node].p2()
   227     nodep1, nodep2 = repo[node].p1(), repo[node].p2()
   228     succp1, succp2 = repo[succ].p1(), repo[succ].p2()
   228     succp1, succp2 = repo[succ].p1(), repo[succ].p2()
   229 
   229 
   230     if nodep1 != succp1 or nodep2 != succp2:
   230     if nodep1 != succp1 or nodep2 != succp2:
   231         return False, "changesets rebased"
   231         return False, b"changesets rebased"
   232 
   232 
   233     return True, succ
   233     return True, succ
   234 
   234 
   235 def getmarkerdescriptionpatch(repo, basedesc, succdesc):
   235 def getmarkerdescriptionpatch(repo, basedesc, succdesc):
   236     # description are stored without final new line,
   236     # description are stored without final new line,
   237     # add one to avoid ugly diff
   237     # add one to avoid ugly diff
   238     basedesc += '\n'
   238     basedesc += b'\n'
   239     succdesc += '\n'
   239     succdesc += b'\n'
   240 
   240 
   241     # fake file name
   241     # fake file name
   242     basename = "changeset-description"
   242     basename = b"changeset-description"
   243     succname = "changeset-description"
   243     succname = b"changeset-description"
   244 
   244 
   245     d = compat.strdiff(basedesc, succdesc, basename, succname)
   245     d = compat.strdiff(basedesc, succdesc, basename, succname)
   246     uheaders, hunks = d
   246     uheaders, hunks = d
   247 
   247 
   248     # Copied from patch.diff
   248     # Copied from patch.diff
   249     text = ''.join(sum((list(hlines) for hrange, hlines in hunks), []))
   249     text = b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
   250     patch = "\n".join(uheaders + [text])
   250     patch = b"\n".join(uheaders + [text])
   251 
   251 
   252     return patch
   252     return patch
   253 
   253 
   254 class missingchangectx(object):
   254 class missingchangectx(object):
   255     ''' a minimal object mimicking changectx for change contexts
   255     ''' a minimal object mimicking changectx for change contexts
   331             assert cycle
   331             assert cycle
   332 
   332 
   333             # Then choose a random node from the cycle
   333             # Then choose a random node from the cycle
   334             breaknode = sorted(cycle)[0]
   334             breaknode = sorted(cycle)[0]
   335             # And display it by force
   335             # And display it by force
   336             repo.ui.debug('obs-cycle detected, forcing display of %s\n'
   336             repo.ui.debug(b'obs-cycle detected, forcing display of %s\n'
   337                           % nodemod.short(breaknode))
   337                           % nodemod.short(breaknode))
   338             validcandidates = [breaknode]
   338             validcandidates = [breaknode]
   339 
   339 
   340         # Display all valid candidates
   340         # Display all valid candidates
   341         for cand in sorted(validcandidates):
   341         for cand in sorted(validcandidates):
   433     compat.displaygraph(ui, repo, walker, displayer, edges)
   433     compat.displaygraph(ui, repo, walker, displayer, edges)
   434 
   434 
   435 def _debugobshistoryrevs(ui, repo, revs, opts):
   435 def _debugobshistoryrevs(ui, repo, revs, opts):
   436     """ Display the obsolescence history for revset
   436     """ Display the obsolescence history for revset
   437     """
   437     """
   438     fm = ui.formatter('debugobshistory', pycompat.byteskwargs(opts))
   438     fm = ui.formatter(b'debugobshistory', pycompat.byteskwargs(opts))
   439     precursors = repo.obsstore.predecessors
   439     precursors = repo.obsstore.predecessors
   440     successors = repo.obsstore.successors
   440     successors = repo.obsstore.successors
   441     nodec = repo.changelog.node
   441     nodec = repo.changelog.node
   442     unfi = repo.unfiltered()
   442     unfi = repo.unfiltered()
   443     nodes = [nodec(r) for r in revs]
   443     nodes = [nodec(r) for r in revs]
   449 
   449 
   450         _debugobshistorydisplaynode(fm, unfi, ctxnode)
   450         _debugobshistorydisplaynode(fm, unfi, ctxnode)
   451 
   451 
   452         succs = successors.get(ctxnode, ())
   452         succs = successors.get(ctxnode, ())
   453 
   453 
   454         markerfm = fm.nested("markers")
   454         markerfm = fm.nested(b"markers")
   455         for successor in sorted(succs):
   455         for successor in sorted(succs):
   456             includediff = opts and opts.get("patch")
   456             includediff = opts and opts.get("patch")
   457             _debugobshistorydisplaymarker(markerfm, successor, ctxnode, unfi, includediff)
   457             _debugobshistorydisplaymarker(markerfm, successor, ctxnode, unfi, includediff)
   458         markerfm.end()
   458         markerfm.end()
   459 
   459 
   475     shortdescription = ctx.description().strip()
   475     shortdescription = ctx.description().strip()
   476     if shortdescription:
   476     if shortdescription:
   477         shortdescription = shortdescription.splitlines()[0]
   477         shortdescription = shortdescription.splitlines()[0]
   478 
   478 
   479     fm.startitem()
   479     fm.startitem()
   480     fm.write('node', '%s', bytes(ctx),
   480     fm.write(b'node', b'%s', bytes(ctx),
   481              label="evolve.node")
   481              label=b"evolve.node")
   482     fm.plain(' ')
   482     fm.plain(b' ')
   483 
   483 
   484     fm.write('rev', '(%d)', ctx.rev(),
   484     fm.write(b'rev', b'(%d)', ctx.rev(),
   485              label="evolve.rev")
   485              label=b"evolve.rev")
   486     fm.plain(' ')
   486     fm.plain(b' ')
   487 
   487 
   488     fm.write('shortdescription', '%s', shortdescription,
   488     fm.write(b'shortdescription', b'%s', shortdescription,
   489              label="evolve.short_description")
   489              label=b"evolve.short_description")
   490     fm.plain('\n')
   490     fm.plain(b'\n')
   491 
   491 
   492 def _debugobshistorydisplaymissingctx(fm, nodewithoutctx):
   492 def _debugobshistorydisplaymissingctx(fm, nodewithoutctx):
   493     hexnode = nodemod.short(nodewithoutctx)
   493     hexnode = nodemod.short(nodewithoutctx)
   494     fm.startitem()
   494     fm.startitem()
   495     fm.write('node', '%s', hexnode,
   495     fm.write(b'node', b'%s', hexnode,
   496              label="evolve.node evolve.missing_change_ctx")
   496              label=b"evolve.node evolve.missing_change_ctx")
   497     fm.plain('\n')
   497     fm.plain(b'\n')
   498 
   498 
   499 def _debugobshistorydisplaymarker(fm, marker, node, repo, includediff=False):
   499 def _debugobshistorydisplaymarker(fm, marker, node, repo, includediff=False):
   500     succnodes = marker[1]
   500     succnodes = marker[1]
   501     date = marker[4]
   501     date = marker[4]
   502     metadata = dict(marker[3])
   502     metadata = dict(marker[3])
   503 
   503 
   504     fm.startitem()
   504     fm.startitem()
   505     fm.plain('  ')
   505     fm.plain(b'  ')
   506 
   506 
   507     # Detect pruned revisions
   507     # Detect pruned revisions
   508     if len(succnodes) == 0:
   508     if len(succnodes) == 0:
   509         verb = 'pruned'
   509         verb = b'pruned'
   510     else:
   510     else:
   511         verb = 'rewritten'
   511         verb = b'rewritten'
   512 
   512 
   513     fm.write('verb', '%s', verb,
   513     fm.write(b'verb', b'%s', verb,
   514              label="evolve.verb")
   514              label=b"evolve.verb")
   515 
   515 
   516     effectflag = metadata.get('ef1')
   516     effectflag = metadata.get(b'ef1')
   517     if effectflag is not None:
   517     if effectflag is not None:
   518         try:
   518         try:
   519             effectflag = int(effectflag)
   519             effectflag = int(effectflag)
   520         except ValueError:
   520         except ValueError:
   521             effectflag = None
   521             effectflag = None
   522     if effectflag:
   522     if effectflag:
   523         effect = []
   523         effect = []
   524 
   524 
   525         # XXX should be a dict
   525         # XXX should be a dict
   526         if effectflag & DESCCHANGED:
   526         if effectflag & DESCCHANGED:
   527             effect.append('description')
   527             effect.append(b'description')
   528         if effectflag & METACHANGED:
   528         if effectflag & METACHANGED:
   529             effect.append('meta')
   529             effect.append(b'meta')
   530         if effectflag & USERCHANGED:
   530         if effectflag & USERCHANGED:
   531             effect.append('user')
   531             effect.append(b'user')
   532         if effectflag & DATECHANGED:
   532         if effectflag & DATECHANGED:
   533             effect.append('date')
   533             effect.append(b'date')
   534         if effectflag & BRANCHCHANGED:
   534         if effectflag & BRANCHCHANGED:
   535             effect.append('branch')
   535             effect.append(b'branch')
   536         if effectflag & PARENTCHANGED:
   536         if effectflag & PARENTCHANGED:
   537             effect.append('parent')
   537             effect.append(b'parent')
   538         if effectflag & DIFFCHANGED:
   538         if effectflag & DIFFCHANGED:
   539             effect.append('content')
   539             effect.append(b'content')
   540 
   540 
   541         if effect:
   541         if effect:
   542             fmteffect = fm.formatlist(effect, 'effect', sep=', ')
   542             fmteffect = fm.formatlist(effect, b'effect', sep=b', ')
   543             fm.write('effect', '(%s)', fmteffect)
   543             fm.write(b'effect', b'(%s)', fmteffect)
   544 
   544 
   545     if len(succnodes) > 0:
   545     if len(succnodes) > 0:
   546         fm.plain(' as ')
   546         fm.plain(b' as ')
   547 
   547 
   548         shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes))
   548         shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes))
   549         nodes = fm.formatlist(shortsnodes, 'succnodes', sep=', ')
   549         nodes = fm.formatlist(shortsnodes, b'succnodes', sep=b', ')
   550         fm.write('succnodes', '%s', nodes,
   550         fm.write(b'succnodes', b'%s', nodes,
   551                  label="evolve.node")
   551                  label=b"evolve.node")
   552 
   552 
   553     operation = metadata.get('operation')
   553     operation = metadata.get(b'operation')
   554     if operation:
   554     if operation:
   555         fm.plain(' using ')
   555         fm.plain(b' using ')
   556         fm.write('operation', '%s', operation, label="evolve.operation")
   556         fm.write(b'operation', b'%s', operation, label=b"evolve.operation")
   557 
   557 
   558     fm.plain(' by ')
   558     fm.plain(b' by ')
   559 
   559 
   560     fm.write('user', '%s', metadata['user'],
   560     fm.write(b'user', b'%s', metadata[b'user'],
   561              label="evolve.user")
   561              label=b"evolve.user")
   562     fm.plain(' ')
   562     fm.plain(b' ')
   563 
   563 
   564     fm.write('date', '(%s)', fm.formatdate(date),
   564     fm.write(b'date', b'(%s)', fm.formatdate(date),
   565              label="evolve.date")
   565              label=b"evolve.date")
   566 
   566 
   567     # initial support for showing note
   567     # initial support for showing note
   568     if metadata.get('note'):
   568     if metadata.get(b'note'):
   569         fm.plain('\n    note: ')
   569         fm.plain(b'\n    note: ')
   570         fm.write('note', "%s", metadata['note'], label="evolve.note")
   570         fm.write(b'note', b"%s", metadata[b'note'], label=b"evolve.note")
   571 
   571 
   572     # Patch display
   572     # Patch display
   573     if includediff is True:
   573     if includediff is True:
   574         _patchavailable = patchavailable(node, repo, marker[1])
   574         _patchavailable = patchavailable(node, repo, marker[1])
   575 
   575 
   583                                                          basectx.description(),
   583                                                          basectx.description(),
   584                                                          succctx.description())
   584                                                          succctx.description())
   585 
   585 
   586             if descriptionpatch:
   586             if descriptionpatch:
   587                 # add the diffheader
   587                 # add the diffheader
   588                 diffheader = "diff -r %s -r %s changeset-description\n" % \
   588                 diffheader = b"diff -r %s -r %s changeset-description\n" %\
   589                              (basectx, succctx)
   589                              (basectx, succctx)
   590                 descriptionpatch = diffheader + descriptionpatch
   590                 descriptionpatch = diffheader + descriptionpatch
   591 
   591 
   592                 def tolist(text):
   592                 def tolist(text):
   593                     return [text]
   593                     return [text]
   594 
   594 
   595                 fm.plain("\n")
   595                 fm.plain(b"\n")
   596 
   596 
   597                 for chunk, label in patch.difflabel(tolist, descriptionpatch):
   597                 for chunk, label in patch.difflabel(tolist, descriptionpatch):
   598                     chunk = chunk.strip('\t')
   598                     chunk = chunk.strip(b'\t')
   599                     if chunk and chunk != '\n':
   599                     if chunk and chunk != b'\n':
   600                         fm.plain('    ')
   600                         fm.plain(b'    ')
   601                     fm.write('desc-diff', '%s', chunk, label=label)
   601                     fm.write(b'desc-diff', b'%s', chunk, label=label)
   602 
   602 
   603             # Content patch
   603             # Content patch
   604             diffopts = patch.diffallopts(repo.ui, {})
   604             diffopts = patch.diffallopts(repo.ui, {})
   605             matchfn = scmutil.matchall(repo)
   605             matchfn = scmutil.matchall(repo)
   606             firstline = True
   606             firstline = True
   607             for chunk, label in patch.diffui(repo, node, succ, matchfn,
   607             for chunk, label in patch.diffui(repo, node, succ, matchfn,
   608                                              opts=diffopts):
   608                                              opts=diffopts):
   609                 if firstline:
   609                 if firstline:
   610                     fm.plain('\n')
   610                     fm.plain(b'\n')
   611                     firstline = False
   611                     firstline = False
   612                 if chunk and chunk != '\n':
   612                 if chunk and chunk != b'\n':
   613                     fm.plain('    ')
   613                     fm.plain(b'    ')
   614                 fm.write('patch', '%s', chunk, label=label)
   614                 fm.write(b'patch', b'%s', chunk, label=label)
   615         else:
   615         else:
   616             nopatch = "    (No patch available, %s)" % _patchavailable[1]
   616             nopatch = b"    (No patch available, %s)" % _patchavailable[1]
   617             fm.plain("\n")
   617             fm.plain(b"\n")
   618             # TODO: should be in json too
   618             # TODO: should be in json too
   619             fm.plain(nopatch)
   619             fm.plain(nopatch)
   620 
   620 
   621     fm.plain("\n")
   621     fm.plain(b"\n")
   622 
   622 
   623 def _debugobshistorydisplaysuccsandmarkers(fm, succnodes, markers, node, repo, includediff=False):
   623 def _debugobshistorydisplaysuccsandmarkers(fm, succnodes, markers, node, repo, includediff=False):
   624     """
   624     """
   625     This function is a duplication of _debugobshistorydisplaymarker modified
   625     This function is a duplication of _debugobshistorydisplaymarker modified
   626     to accept multiple markers as input.
   626     to accept multiple markers as input.
   627     """
   627     """
   628     fm.startitem()
   628     fm.startitem()
   629     fm.plain('  ')
   629     fm.plain(b'  ')
   630 
   630 
   631     # Detect pruned revisions
   631     # Detect pruned revisions
   632     verb = _successorsetverb(succnodes, markers)["verb"]
   632     verb = _successorsetverb(succnodes, markers)[b"verb"]
   633 
   633 
   634     fm.write('verb', '%s', verb,
   634     fm.write(b'verb', b'%s', verb,
   635              label="evolve.verb")
   635              label=b"evolve.verb")
   636 
   636 
   637     # Effect flag
   637     # Effect flag
   638     metadata = [dict(marker[3]) for marker in markers]
   638     metadata = [dict(marker[3]) for marker in markers]
   639     ef1 = [data.get('ef1') for data in metadata]
   639     ef1 = [data.get(b'ef1') for data in metadata]
   640 
   640 
   641     effectflag = 0
   641     effectflag = 0
   642     for ef in ef1:
   642     for ef in ef1:
   643         if ef:
   643         if ef:
   644             effectflag |= int(ef)
   644             effectflag |= int(ef)
   646     if effectflag:
   646     if effectflag:
   647         effect = []
   647         effect = []
   648 
   648 
   649         # XXX should be a dict
   649         # XXX should be a dict
   650         if effectflag & DESCCHANGED:
   650         if effectflag & DESCCHANGED:
   651             effect.append('description')
   651             effect.append(b'description')
   652         if effectflag & METACHANGED:
   652         if effectflag & METACHANGED:
   653             effect.append('meta')
   653             effect.append(b'meta')
   654         if effectflag & USERCHANGED:
   654         if effectflag & USERCHANGED:
   655             effect.append('user')
   655             effect.append(b'user')
   656         if effectflag & DATECHANGED:
   656         if effectflag & DATECHANGED:
   657             effect.append('date')
   657             effect.append(b'date')
   658         if effectflag & BRANCHCHANGED:
   658         if effectflag & BRANCHCHANGED:
   659             effect.append('branch')
   659             effect.append(b'branch')
   660         if effectflag & PARENTCHANGED:
   660         if effectflag & PARENTCHANGED:
   661             effect.append('parent')
   661             effect.append(b'parent')
   662         if effectflag & DIFFCHANGED:
   662         if effectflag & DIFFCHANGED:
   663             effect.append('content')
   663             effect.append(b'content')
   664 
   664 
   665         if effect:
   665         if effect:
   666             fmteffect = fm.formatlist(effect, 'effect', sep=', ')
   666             fmteffect = fm.formatlist(effect, b'effect', sep=b', ')
   667             fm.write('effect', '(%s)', fmteffect)
   667             fm.write(b'effect', b'(%s)', fmteffect)
   668 
   668 
   669     if len(succnodes) > 0:
   669     if len(succnodes) > 0:
   670         fm.plain(' as ')
   670         fm.plain(b' as ')
   671 
   671 
   672         shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes))
   672         shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes))
   673         nodes = fm.formatlist(shortsnodes, 'succnodes', sep=', ')
   673         nodes = fm.formatlist(shortsnodes, b'succnodes', sep=b', ')
   674         fm.write('succnodes', '%s', nodes,
   674         fm.write(b'succnodes', b'%s', nodes,
   675                  label="evolve.node")
   675                  label=b"evolve.node")
   676 
   676 
   677     # Operations
   677     # Operations
   678     operations = compat.markersoperations(markers)
   678     operations = compat.markersoperations(markers)
   679     if operations:
   679     if operations:
   680         fm.plain(' using ')
   680         fm.plain(b' using ')
   681         fm.write('operation', '%s', ", ".join(operations), label="evolve.operation")
   681         fm.write(b'operation', b'%s', b", ".join(operations), label=b"evolve.operation")
   682 
   682 
   683     fm.plain(' by ')
   683     fm.plain(b' by ')
   684 
   684 
   685     # Users
   685     # Users
   686     users = compat.markersusers(markers)
   686     users = compat.markersusers(markers)
   687     fm.write('user', '%s', ", ".join(users),
   687     fm.write(b'user', b'%s', b", ".join(users),
   688              label="evolve.user")
   688              label=b"evolve.user")
   689     fm.plain(' ')
   689     fm.plain(b' ')
   690 
   690 
   691     # Dates
   691     # Dates
   692     dates = compat.markersdates(markers)
   692     dates = compat.markersdates(markers)
   693     if dates:
   693     if dates:
   694         min_date = min(dates)
   694         min_date = min(dates)
   695         max_date = max(dates)
   695         max_date = max(dates)
   696 
   696 
   697         if min_date == max_date:
   697         if min_date == max_date:
   698             fm.write("date", "(at %s)", fm.formatdate(min_date), label="evolve.date")
   698             fm.write(b"date", b"(at %s)", fm.formatdate(min_date), label=b"evolve.date")
   699         else:
   699         else:
   700             fm.write("date", "(between %s and %s)", fm.formatdate(min_date),
   700             fm.write(b"date", b"(between %s and %s)", fm.formatdate(min_date),
   701                      fm.formatdate(max_date), label="evolve.date")
   701                      fm.formatdate(max_date), label=b"evolve.date")
   702 
   702 
   703     # initial support for showing note
   703     # initial support for showing note
   704     # if metadata.get('note'):
   704     # if metadata.get('note'):
   705     #     fm.plain('\n    note: ')
   705     #     fm.plain('\n    note: ')
   706     #     fm.write('note', "%s", metadata['note'], label="evolve.note")
   706     #     fm.write('note', "%s", metadata['note'], label="evolve.note")
   719                                                          basectx.description(),
   719                                                          basectx.description(),
   720                                                          succctx.description())
   720                                                          succctx.description())
   721 
   721 
   722             if descriptionpatch:
   722             if descriptionpatch:
   723                 # add the diffheader
   723                 # add the diffheader
   724                 diffheader = "diff -r %s -r %s changeset-description\n" % \
   724                 diffheader = b"diff -r %s -r %s changeset-description\n" %\
   725                              (basectx, succctx)
   725                              (basectx, succctx)
   726                 descriptionpatch = diffheader + descriptionpatch
   726                 descriptionpatch = diffheader + descriptionpatch
   727 
   727 
   728                 def tolist(text):
   728                 def tolist(text):
   729                     return [text]
   729                     return [text]
   730 
   730 
   731                 fm.plain("\n")
   731                 fm.plain(b"\n")
   732 
   732 
   733                 for chunk, label in patch.difflabel(tolist, descriptionpatch):
   733                 for chunk, label in patch.difflabel(tolist, descriptionpatch):
   734                     chunk = chunk.strip('\t')
   734                     chunk = chunk.strip(b'\t')
   735                     if chunk and chunk != '\n':
   735                     if chunk and chunk != b'\n':
   736                         fm.plain('    ')
   736                         fm.plain(b'    ')
   737                     fm.write('desc-diff', '%s', chunk, label=label)
   737                     fm.write(b'desc-diff', b'%s', chunk, label=label)
   738 
   738 
   739             # Content patch
   739             # Content patch
   740             diffopts = patch.diffallopts(repo.ui, {})
   740             diffopts = patch.diffallopts(repo.ui, {})
   741             matchfn = scmutil.matchall(repo)
   741             matchfn = scmutil.matchall(repo)
   742             firstline = True
   742             firstline = True
   743             for chunk, label in patch.diffui(repo, node, succ, matchfn,
   743             for chunk, label in patch.diffui(repo, node, succ, matchfn,
   744                                              opts=diffopts):
   744                                              opts=diffopts):
   745                 if firstline:
   745                 if firstline:
   746                     fm.plain('\n')
   746                     fm.plain(b'\n')
   747                     firstline = False
   747                     firstline = False
   748                 if chunk and chunk != '\n':
   748                 if chunk and chunk != b'\n':
   749                     fm.plain('    ')
   749                     fm.plain(b'    ')
   750                 fm.write('patch', '%s', chunk, label=label)
   750                 fm.write(b'patch', b'%s', chunk, label=label)
   751         else:
   751         else:
   752             nopatch = "    (No patch available, %s)" % _patchavailable[1]
   752             nopatch = b"    (No patch available, %s)" % _patchavailable[1]
   753             fm.plain("\n")
   753             fm.plain(b"\n")
   754             # TODO: should be in json too
   754             # TODO: should be in json too
   755             fm.plain(nopatch)
   755             fm.plain(nopatch)
   756 
   756 
   757     fm.plain("\n")
   757     fm.plain(b"\n")
   758 
   758 
   759 # logic around storing and using effect flags
   759 # logic around storing and using effect flags
   760 DESCCHANGED = 1 << 0 # action changed the description
   760 DESCCHANGED = 1 << 0 # action changed the description
   761 METACHANGED = 1 << 1 # action change the meta
   761 METACHANGED = 1 << 1 # action change the meta
   762 PARENTCHANGED = 1 << 2 # action change the parent
   762 PARENTCHANGED = 1 << 2 # action change the parent
   812     - superseed_split
   812     - superseed_split
   813     """
   813     """
   814 
   814 
   815     if len(successorssets) == 0:
   815     if len(successorssets) == 0:
   816         # The commit has been pruned
   816         # The commit has been pruned
   817         return 'pruned'
   817         return b'pruned'
   818     elif len(successorssets) > 1:
   818     elif len(successorssets) > 1:
   819         return 'diverged'
   819         return b'diverged'
   820     else:
   820     else:
   821         # No divergence, only one set of successors
   821         # No divergence, only one set of successors
   822         successors = successorssets[0]
   822         successors = successorssets[0]
   823 
   823 
   824         if len(successors) == 1:
   824         if len(successors) == 1:
   825             return 'superseed'
   825             return b'superseed'
   826         else:
   826         else:
   827             return 'superseed_split'
   827             return b'superseed_split'
   828 
   828 
   829 def _getobsfateandsuccs(repo, revnode, successorssets=None):
   829 def _getobsfateandsuccs(repo, revnode, successorssets=None):
   830     """ Return a tuple containing:
   830     """ Return a tuple containing:
   831     - the reason a revision is obsolete (diverged, pruned or superseed)
   831     - the reason a revision is obsolete (diverged, pruned or superseed)
   832     - the list of successors short node if the revision is neither pruned
   832     - the list of successors short node if the revision is neither pruned
   855         return {}
   855         return {}
   856 
   856 
   857     dates = [m[4] for m in markers]
   857     dates = [m[4] for m in markers]
   858 
   858 
   859     return {
   859     return {
   860         'min_date': min(dates),
   860         b'min_date': min(dates),
   861         'max_date': max(dates)
   861         b'max_date': max(dates)
   862     }
   862     }
   863 
   863 
   864 def _successorsetusers(successorset, markers):
   864 def _successorsetusers(successorset, markers):
   865     """ Returns a sorted list of markers users without duplicates
   865     """ Returns a sorted list of markers users without duplicates
   866     """
   866     """
   867     if not markers:
   867     if not markers:
   868         return {}
   868         return {}
   869 
   869 
   870     # Check that user is present in meta
   870     # Check that user is present in meta
   871     markersmeta = [dict(m[3]) for m in markers]
   871     markersmeta = [dict(m[3]) for m in markers]
   872     users = set(meta.get('user') for meta in markersmeta if meta.get('user'))
   872     users = set(meta.get(b'user') for meta in markersmeta if meta.get(b'user'))
   873 
   873 
   874     return {'users': sorted(users)}
   874     return {b'users': sorted(users)}
   875 
   875 
   876 VERBMAPPING = {
   876 VERBMAPPING = {
   877     DESCCHANGED: "reworded",
   877     DESCCHANGED: b"reworded",
   878     METACHANGED: "meta-changed",
   878     METACHANGED: b"meta-changed",
   879     USERCHANGED: "reauthored",
   879     USERCHANGED: b"reauthored",
   880     DATECHANGED: "date-changed",
   880     DATECHANGED: b"date-changed",
   881     BRANCHCHANGED: "branch-changed",
   881     BRANCHCHANGED: b"branch-changed",
   882     PARENTCHANGED: "rebased",
   882     PARENTCHANGED: b"rebased",
   883     DIFFCHANGED: "amended"
   883     DIFFCHANGED: b"amended"
   884 }
   884 }
   885 
   885 
   886 def _successorsetverb(successorset, markers):
   886 def _successorsetverb(successorset, markers):
   887     """ Return the verb summarizing the successorset
   887     """ Return the verb summarizing the successorset
   888     """
   888     """
   889     verb = None
   889     verb = None
   890     if not successorset:
   890     if not successorset:
   891         verb = 'pruned'
   891         verb = b'pruned'
   892     elif len(successorset) == 1:
   892     elif len(successorset) == 1:
   893         # Check for effect flag
   893         # Check for effect flag
   894 
   894 
   895         metadata = [dict(marker[3]) for marker in markers]
   895         metadata = [dict(marker[3]) for marker in markers]
   896         ef1 = [data.get('ef1') for data in metadata]
   896         ef1 = [data.get(b'ef1') for data in metadata]
   897 
   897 
   898         if all(ef1):
   898         if all(ef1):
   899             combined = 0
   899             combined = 0
   900             for ef in ef1:
   900             for ef in ef1:
   901                 combined |= int(ef)
   901                 combined |= int(ef)
   903             # Combined will be in VERBMAPPING only of one bit is set
   903             # Combined will be in VERBMAPPING only of one bit is set
   904             if combined in VERBMAPPING:
   904             if combined in VERBMAPPING:
   905                 verb = VERBMAPPING[combined]
   905                 verb = VERBMAPPING[combined]
   906 
   906 
   907         if verb is None:
   907         if verb is None:
   908             verb = 'rewritten'
   908             verb = b'rewritten'
   909     else:
   909     else:
   910         verb = 'split'
   910         verb = b'split'
   911     return {'verb': verb}
   911     return {b'verb': verb}
   912 
   912 
   913 # Use a more advanced version of obsfateverb that uses effect-flag
   913 # Use a more advanced version of obsfateverb that uses effect-flag
   914 if util.safehasattr(obsutil, 'obsfateverb'):
   914 if util.safehasattr(obsutil, 'obsfateverb'):
   915 
   915 
   916     @eh.wrapfunction(obsutil, 'obsfateverb')
   916     @eh.wrapfunction(obsutil, 'obsfateverb')
   917     def obsfateverb(orig, *args, **kwargs):
   917     def obsfateverb(orig, *args, **kwargs):
   918         return _successorsetverb(*args, **kwargs)['verb']
   918         return _successorsetverb(*args, **kwargs)[b'verb']
   919 
   919 
   920 # Hijack callers of successorsetverb
   920 # Hijack callers of successorsetverb
   921 elif util.safehasattr(obsutil, 'obsfateprinter'):
   921 elif util.safehasattr(obsutil, 'obsfateprinter'):
   922 
   922 
   923     @eh.wrapfunction(obsutil, 'obsfateprinter')
   923     @eh.wrapfunction(obsutil, 'obsfateprinter')
   924     def obsfateprinter(orig, successors, markers, ui):
   924     def obsfateprinter(orig, successors, markers, ui):
   925 
   925 
   926         def closure(successors):
   926         def closure(successors):
   927             return _successorsetverb(successors, markers)['verb']
   927             return _successorsetverb(successors, markers)[b'verb']
   928 
   928 
   929         if not util.safehasattr(obsutil, 'successorsetverb'):
   929         if not util.safehasattr(obsutil, 'successorsetverb'):
   930             return orig(successors, markers, ui)
   930             return orig(successors, markers, ui)
   931 
   931 
   932         # Save the old value
   932         # Save the old value
   998         newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
   998         newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
   999         markers.append(newmarker)
   999         markers.append(newmarker)
  1000 
  1000 
  1001     # Format basic data
  1001     # Format basic data
  1002     data = {
  1002     data = {
  1003         "successors": sorted(successorset),
  1003         b"successors": sorted(successorset),
  1004         "markers": sorted(markers)
  1004         b"markers": sorted(markers)
  1005     }
  1005     }
  1006 
  1006 
  1007     # Call an extensible list of functions to override or add new data
  1007     # Call an extensible list of functions to override or add new data
  1008     for function in FORMATSSETSFUNCTIONS:
  1008     for function in FORMATSSETSFUNCTIONS:
  1009         data.update(function(successorset, markers))
  1009         data.update(function(successorset, markers))