hgext/absorb.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43105 649d3ac37a12
--- a/hgext/absorb.py	Sun Oct 06 09:45:02 2019 -0400
+++ b/hgext/absorb.py	Sun Oct 06 09:48:39 2019 -0400
@@ -59,7 +59,7 @@
 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
 # be specifying the version(s) of Mercurial they are tested with, or
 # leave the attribute unspecified.
-testedwith = 'ships-with-hg-core'
+testedwith = b'ships-with-hg-core'
 
 cmdtable = {}
 command = registrar.command(cmdtable)
@@ -67,14 +67,14 @@
 configtable = {}
 configitem = registrar.configitem(configtable)
 
-configitem('absorb', 'add-noise', default=True)
-configitem('absorb', 'amend-flag', default=None)
-configitem('absorb', 'max-stack-size', default=50)
+configitem(b'absorb', b'add-noise', default=True)
+configitem(b'absorb', b'amend-flag', default=None)
+configitem(b'absorb', b'max-stack-size', default=50)
 
 colortable = {
-    'absorb.description': 'yellow',
-    'absorb.node': 'blue bold',
-    'absorb.path': 'bold',
+    b'absorb.description': b'yellow',
+    b'absorb.node': b'blue bold',
+    b'absorb.path': b'bold',
 }
 
 defaultdict = collections.defaultdict
@@ -98,7 +98,7 @@
     """minimal filecontext representing an empty file"""
 
     def data(self):
-        return ''
+        return b''
 
     def node(self):
         return node.nullid
@@ -364,11 +364,11 @@
             if self.ui.debugflag:
                 idx = (max(rev - 1, 0)) // 2
                 self.ui.write(
-                    _('%s: chunk %d:%d -> %d lines\n')
+                    _(b'%s: chunk %d:%d -> %d lines\n')
                     % (node.short(self.fctxs[idx].node()), a1, a2, len(blines))
                 )
             self.linelog.replacelines(rev, a1, a2, b1, b2)
-        if self.opts.get('edit_lines', False):
+        if self.opts.get(b'edit_lines', False):
             self.finalcontents = self._checkoutlinelogwithedits()
         else:
             self.finalcontents = self._checkoutlinelog()
@@ -434,7 +434,7 @@
         """like mdiff.allblocks, but only care about differences"""
         blocks = mdiff.allblocks(a, b, lines1=alines, lines2=blines)
         for chunk, btype in blocks:
-            if btype != '!':
+            if btype != b'!':
                 continue
             yield chunk
 
@@ -443,7 +443,7 @@
         this is similar to running a partial "annotate".
         """
         llog = linelog.linelog()
-        a, alines = '', []
+        a, alines = b'', []
         for i in pycompat.xrange(len(self.contents)):
             b, blines = self.contents[i], self.contentlines[i]
             llrev = i * 2 + 1
@@ -459,7 +459,7 @@
         for i in pycompat.xrange(len(self.contents)):
             rev = (i + 1) * 2
             self.linelog.annotate(rev)
-            content = ''.join(map(self._getline, self.linelog.annotateresult))
+            content = b''.join(map(self._getline, self.linelog.annotateresult))
             contents.append(content)
         return contents
 
@@ -469,8 +469,8 @@
         # header
         editortext = (
             _(
-                'HG: editing %s\nHG: "y" means the line to the right '
-                'exists in the changeset to the top\nHG:\n'
+                b'HG: editing %s\nHG: "y" means the line to the right '
+                b'exists in the changeset to the top\nHG:\n'
             )
             % self.fctxs[-1].path()
         )
@@ -481,13 +481,13 @@
             if not isinstance(f, emptyfilecontext)
         ]
         for i, (j, f) in enumerate(visiblefctxs):
-            editortext += _('HG: %s/%s %s %s\n') % (
-                '|' * i,
-                '-' * (len(visiblefctxs) - i + 1),
+            editortext += _(b'HG: %s/%s %s %s\n') % (
+                b'|' * i,
+                b'-' * (len(visiblefctxs) - i + 1),
                 node.short(f.node()),
-                f.description().split('\n', 1)[0],
+                f.description().split(b'\n', 1)[0],
             )
-        editortext += _('HG: %s\n') % ('|' * len(visiblefctxs))
+        editortext += _(b'HG: %s\n') % (b'|' * len(visiblefctxs))
         # figure out the lifetime of a line, this is relatively inefficient,
         # but probably fine
         lineset = defaultdict(lambda: set())  # {(llrev, linenum): {llrev}}
@@ -497,33 +497,33 @@
                 lineset[l].add(i)
         # append lines
         for l in alllines:
-            editortext += '    %s : %s' % (
-                ''.join(
+            editortext += b'    %s : %s' % (
+                b''.join(
                     [
-                        ('y' if i in lineset[l] else ' ')
+                        (b'y' if i in lineset[l] else b' ')
                         for i, _f in visiblefctxs
                     ]
                 ),
                 self._getline(l),
             )
         # run editor
-        editedtext = self.ui.edit(editortext, '', action='absorb')
+        editedtext = self.ui.edit(editortext, b'', action=b'absorb')
         if not editedtext:
-            raise error.Abort(_('empty editor text'))
+            raise error.Abort(_(b'empty editor text'))
         # parse edited result
-        contents = ['' for i in self.fctxs]
+        contents = [b'' for i in self.fctxs]
         leftpadpos = 4
         colonpos = leftpadpos + len(visiblefctxs) + 1
         for l in mdiff.splitnewlines(editedtext):
-            if l.startswith('HG:'):
+            if l.startswith(b'HG:'):
                 continue
-            if l[colonpos - 1 : colonpos + 2] != ' : ':
-                raise error.Abort(_('malformed line: %s') % l)
+            if l[colonpos - 1 : colonpos + 2] != b' : ':
+                raise error.Abort(_(b'malformed line: %s') % l)
             linecontent = l[colonpos + 2 :]
             for i, ch in enumerate(
                 pycompat.bytestr(l[leftpadpos : colonpos - 1])
             ):
-                if ch == 'y':
+                if ch == b'y':
                     contents[visiblefctxs[i][0]] += linecontent
         # chunkstats is hard to calculate if anything changes, therefore
         # set them to just a simple value (1, 1).
@@ -589,7 +589,7 @@
 
     def _showchanges(self, fm, alines, blines, chunk, fixups):
         def trim(line):
-            if line.endswith('\n'):
+            if line.endswith(b'\n'):
                 line = line[:-1]
             return line
 
@@ -605,25 +605,25 @@
 
         fm.startitem()
         fm.write(
-            'hunk',
-            '        %s\n',
-            '@@ -%d,%d +%d,%d @@' % (a1, a2 - a1, b1, b2 - b1),
-            label='diff.hunk',
+            b'hunk',
+            b'        %s\n',
+            b'@@ -%d,%d +%d,%d @@' % (a1, a2 - a1, b1, b2 - b1),
+            label=b'diff.hunk',
         )
-        fm.data(path=self.path, linetype='hunk')
+        fm.data(path=self.path, linetype=b'hunk')
 
         def writeline(idx, diffchar, line, linetype, linelabel):
             fm.startitem()
-            node = ''
+            node = b''
             if idx:
                 ctx = self.fctxs[idx]
                 fm.context(fctx=ctx)
                 node = ctx.hex()
                 self.ctxaffected.add(ctx.changectx())
-            fm.write('node', '%-7.7s ', node, label='absorb.node')
+            fm.write(b'node', b'%-7.7s ', node, label=b'absorb.node')
             fm.write(
-                'diffchar ' + linetype,
-                '%s%s\n',
+                b'diffchar ' + linetype,
+                b'%s%s\n',
                 diffchar,
                 line,
                 label=linelabel,
@@ -632,11 +632,19 @@
 
         for i in pycompat.xrange(a1, a2):
             writeline(
-                aidxs[i - a1], '-', trim(alines[i]), 'deleted', 'diff.deleted'
+                aidxs[i - a1],
+                b'-',
+                trim(alines[i]),
+                b'deleted',
+                b'diff.deleted',
             )
         for i in pycompat.xrange(b1, b2):
             writeline(
-                bidxs[i - b1], '+', trim(blines[i]), 'inserted', 'diff.inserted'
+                bidxs[i - b1],
+                b'+',
+                trim(blines[i]),
+                b'inserted',
+                b'diff.inserted',
             )
 
 
@@ -681,7 +689,7 @@
         self.paths = []
         # but if --edit-lines is used, the user may want to edit files
         # even if they are not modified
-        editopt = self.opts.get('edit_lines')
+        editopt = self.opts.get(b'edit_lines')
         if not self.status.modified and editopt and match:
             interestingpaths = match.files()
         else:
@@ -691,7 +699,7 @@
         # sorting is necessary to eliminate ambiguity for the "double move"
         # case: "hg cp A B; hg cp A C; hg rm A", then only "B" can affect "A".
         for path in sorted(interestingpaths):
-            self.ui.debug('calculating fixups for %s\n' % path)
+            self.ui.debug(b'calculating fixups for %s\n' % path)
             targetfctx = targetctx[path]
             fctxs, ctx2fctx = getfilestack(self.stack, path, seenfctxs)
             # ignore symbolic links or binary, or unchanged files
@@ -708,9 +716,9 @@
             fstate = filefixupstate(fctxs, path, ui=self.ui, opts=self.opts)
             if fm is not None:
                 fm.startitem()
-                fm.plain('showing changes for ')
-                fm.write('path', '%s\n', path, label='absorb.path')
-                fm.data(linetype='path')
+                fm.plain(b'showing changes for ')
+                fm.write(b'path', b'%s\n', path, label=b'absorb.path')
+                fm.data(linetype=b'path')
             fstate.diffwith(targetfctx, fm)
             self.fixupmap[path] = fstate
             self.paths.append(path)
@@ -720,7 +728,7 @@
         """apply fixups to individual filefixupstates"""
         for path, state in self.fixupmap.iteritems():
             if self.ui.debugflag:
-                self.ui.write(_('applying fixups to %s\n') % path)
+                self.ui.write(_(b'applying fixups to %s\n') % path)
             state.apply()
 
     @property
@@ -733,10 +741,10 @@
 
     def commit(self):
         """commit changes. update self.finalnode, self.replacemap"""
-        with self.repo.transaction('absorb') as tr:
+        with self.repo.transaction(b'absorb') as tr:
             self._commitstack()
             self._movebookmarks(tr)
-            if self.repo['.'].node() in self.replacemap:
+            if self.repo[b'.'].node() in self.replacemap:
                 self._moveworkingdirectoryparent()
             self._cleanupoldcommits()
         return self.finalnode
@@ -750,14 +758,14 @@
             for path, stat in chunkstats.iteritems():
                 if stat[0]:
                     ui.write(
-                        _('%s: %d of %d chunk(s) applied\n')
+                        _(b'%s: %d of %d chunk(s) applied\n')
                         % (path, stat[0], stat[1])
                     )
         elif not ui.quiet:
             # a summary for all files
             stats = chunkstats.values()
             applied, total = (sum(s[i] for s in stats) for i in (0, 1))
-            ui.write(_('%d of %d chunk(s) applied\n') % (applied, total))
+            ui.write(_(b'%d of %d chunk(s) applied\n') % (applied, total))
 
     def _commitstack(self):
         """make new commits. update self.finalnode, self.replacemap.
@@ -777,7 +785,7 @@
             if self._willbecomenoop(memworkingcopy, ctx, nextp1):
                 # changeset is no longer necessary
                 self.replacemap[ctx.node()] = None
-                msg = _('became empty and was dropped')
+                msg = _(b'became empty and was dropped')
             else:
                 # changeset needs re-commit
                 nodestr = self._commitsingle(memworkingcopy, ctx, p1=nextp1)
@@ -785,21 +793,21 @@
                 nextp1 = lastcommitted
                 self.replacemap[ctx.node()] = lastcommitted.node()
                 if memworkingcopy:
-                    msg = _('%d file(s) changed, became %s') % (
+                    msg = _(b'%d file(s) changed, became %s') % (
                         len(memworkingcopy),
                         self._ctx2str(lastcommitted),
                     )
                 else:
-                    msg = _('became %s') % self._ctx2str(lastcommitted)
+                    msg = _(b'became %s') % self._ctx2str(lastcommitted)
             if self.ui.verbose and msg:
-                self.ui.write(_('%s: %s\n') % (self._ctx2str(ctx), msg))
+                self.ui.write(_(b'%s: %s\n') % (self._ctx2str(ctx), msg))
         self.finalnode = lastcommitted and lastcommitted.node()
 
     def _ctx2str(self, ctx):
         if self.ui.debugflag:
-            return '%d:%s' % (ctx.rev(), ctx.hex())
+            return b'%d:%s' % (ctx.rev(), ctx.hex())
         else:
-            return '%d:%s' % (ctx.rev(), node.short(ctx.node()))
+            return b'%d:%s' % (ctx.rev(), node.short(ctx.node()))
 
     def _getnewfilecontents(self, ctx):
         """(ctx) -> {path: str}
@@ -832,18 +840,18 @@
                 changes.append((name, hsh))
                 if self.ui.verbose:
                     self.ui.write(
-                        _('moving bookmark %s to %s\n') % (name, node.hex(hsh))
+                        _(b'moving bookmark %s to %s\n') % (name, node.hex(hsh))
                     )
             else:
                 changes.append((name, None))
                 if self.ui.verbose:
-                    self.ui.write(_('deleting bookmark %s\n') % name)
+                    self.ui.write(_(b'deleting bookmark %s\n') % name)
         repo._bookmarks.applychanges(repo, tr, changes)
 
     def _moveworkingdirectoryparent(self):
         if not self.finalnode:
             # Find the latest not-{obsoleted,stripped} parent.
-            revs = self.repo.revs('max(::. - %ln)', self.replacemap.keys())
+            revs = self.repo.revs(b'max(::. - %ln)', self.replacemap.keys())
             ctx = self.repo[revs.first()]
             self.finalnode = ctx.node()
         else:
@@ -854,7 +862,7 @@
         # be slow. in absorb's case, no need to invalidate fsmonitorstate.
         noop = lambda: 0
         restore = noop
-        if util.safehasattr(dirstate, '_fsmonitorstate'):
+        if util.safehasattr(dirstate, b'_fsmonitorstate'):
             bak = dirstate._fsmonitorstate.invalidate
 
             def restore():
@@ -901,8 +909,8 @@
         """
         parents = p1 and (p1, node.nullid)
         extra = ctx.extra()
-        if self._useobsolete and self.ui.configbool('absorb', 'add-noise'):
-            extra['absorb_source'] = ctx.hex()
+        if self._useobsolete and self.ui.configbool(b'absorb', b'add-noise'):
+            extra[b'absorb_source'] = ctx.hex()
         mctx = overlaycontext(memworkingcopy, ctx, parents, extra=extra)
         return mctx.commit()
 
@@ -918,7 +926,7 @@
         }
         if replacements:
             scmutil.cleanupnodes(
-                self.repo, replacements, operation='absorb', fixphase=True
+                self.repo, replacements, operation=b'absorb', fixphase=True
             )
 
 
@@ -935,7 +943,7 @@
     patchlines = mdiff.splitnewlines(buf.getvalue())
     # hunk.prettystr() will update hunk.removed
     a2 = a1 + hunk.removed
-    blines = [l[1:] for l in patchlines[1:] if not l.startswith('-')]
+    blines = [l[1:] for l in patchlines[1:] if not l.startswith(b'-')]
     return path, (a1, a2, blines)
 
 
@@ -967,7 +975,7 @@
         lines = mdiff.splitnewlines(ctx[path].data())
         for a1, a2, blines in patches:
             lines[a1:a2] = blines
-        memworkingcopy[path] = ''.join(lines)
+        memworkingcopy[path] = b''.join(lines)
     return overlaycontext(memworkingcopy, ctx)
 
 
@@ -979,18 +987,21 @@
     return fixupstate.
     """
     if stack is None:
-        limit = ui.configint('absorb', 'max-stack-size')
-        headctx = repo['.']
+        limit = ui.configint(b'absorb', b'max-stack-size')
+        headctx = repo[b'.']
         if len(headctx.parents()) > 1:
-            raise error.Abort(_('cannot absorb into a merge'))
+            raise error.Abort(_(b'cannot absorb into a merge'))
         stack = getdraftstack(headctx, limit)
         if limit and len(stack) >= limit:
             ui.warn(
-                _('absorb: only the recent %d changesets will ' 'be analysed\n')
+                _(
+                    b'absorb: only the recent %d changesets will '
+                    b'be analysed\n'
+                )
                 % limit
             )
     if not stack:
-        raise error.Abort(_('no mutable changeset to change'))
+        raise error.Abort(_(b'no mutable changeset to change'))
     if targetctx is None:  # default to working copy
         targetctx = repo[None]
     if pats is None:
@@ -999,85 +1010,89 @@
         opts = {}
     state = fixupstate(stack, ui=ui, opts=opts)
     matcher = scmutil.match(targetctx, pats, opts)
-    if opts.get('interactive'):
+    if opts.get(b'interactive'):
         diff = patch.diff(repo, stack[-1].node(), targetctx.node(), matcher)
         origchunks = patch.parsepatch(diff)
         chunks = cmdutil.recordfilter(ui, origchunks, matcher)[0]
         targetctx = overlaydiffcontext(stack[-1], chunks)
     fm = None
-    if opts.get('print_changes') or not opts.get('apply_changes'):
-        fm = ui.formatter('absorb', opts)
+    if opts.get(b'print_changes') or not opts.get(b'apply_changes'):
+        fm = ui.formatter(b'absorb', opts)
     state.diffwith(targetctx, matcher, fm)
     if fm is not None:
         fm.startitem()
-        fm.write("count", "\n%d changesets affected\n", len(state.ctxaffected))
-        fm.data(linetype='summary')
+        fm.write(
+            b"count", b"\n%d changesets affected\n", len(state.ctxaffected)
+        )
+        fm.data(linetype=b'summary')
         for ctx in reversed(stack):
             if ctx not in state.ctxaffected:
                 continue
             fm.startitem()
             fm.context(ctx=ctx)
-            fm.data(linetype='changeset')
-            fm.write('node', '%-7.7s ', ctx.hex(), label='absorb.node')
+            fm.data(linetype=b'changeset')
+            fm.write(b'node', b'%-7.7s ', ctx.hex(), label=b'absorb.node')
             descfirstline = ctx.description().splitlines()[0]
             fm.write(
-                'descfirstline',
-                '%s\n',
+                b'descfirstline',
+                b'%s\n',
                 descfirstline,
-                label='absorb.description',
+                label=b'absorb.description',
             )
         fm.end()
-    if not opts.get('dry_run'):
+    if not opts.get(b'dry_run'):
         if (
-            not opts.get('apply_changes')
+            not opts.get(b'apply_changes')
             and state.ctxaffected
-            and ui.promptchoice("apply changes (yn)? $$ &Yes $$ &No", default=1)
+            and ui.promptchoice(
+                b"apply changes (yn)? $$ &Yes $$ &No", default=1
+            )
         ):
-            raise error.Abort(_('absorb cancelled\n'))
+            raise error.Abort(_(b'absorb cancelled\n'))
 
         state.apply()
         if state.commit():
             state.printchunkstats()
         elif not ui.quiet:
-            ui.write(_('nothing applied\n'))
+            ui.write(_(b'nothing applied\n'))
     return state
 
 
 @command(
-    'absorb',
+    b'absorb',
     [
         (
-            'a',
-            'apply-changes',
+            b'a',
+            b'apply-changes',
             None,
-            _('apply changes without prompting for confirmation'),
+            _(b'apply changes without prompting for confirmation'),
         ),
         (
-            'p',
-            'print-changes',
+            b'p',
+            b'print-changes',
             None,
-            _('always print which changesets are modified by which changes'),
+            _(b'always print which changesets are modified by which changes'),
         ),
         (
-            'i',
-            'interactive',
+            b'i',
+            b'interactive',
             None,
-            _('interactively select which chunks to apply (EXPERIMENTAL)'),
+            _(b'interactively select which chunks to apply (EXPERIMENTAL)'),
         ),
         (
-            'e',
-            'edit-lines',
+            b'e',
+            b'edit-lines',
             None,
             _(
-                'edit what lines belong to which changesets before commit '
-                '(EXPERIMENTAL)'
+                b'edit what lines belong to which changesets before commit '
+                b'(EXPERIMENTAL)'
             ),
         ),
     ]
     + commands.dryrunopts
     + commands.templateopts
     + commands.walkopts,
-    _('hg absorb [OPTION] [FILE]...'),
+    _(b'hg absorb [OPTION] [FILE]...'),
     helpcategory=command.CATEGORY_COMMITTING,
     helpbasic=True,
 )
@@ -1108,7 +1123,7 @@
     opts = pycompat.byteskwargs(opts)
 
     with repo.wlock(), repo.lock():
-        if not opts['dry_run']:
+        if not opts[b'dry_run']:
             cmdutil.checkunfinished(repo)
 
         state = absorb(ui, repo, pats=pats, opts=opts)