diff hgext/absorb.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents c1bf63ac30c5
children 687b865b95ad
line wrap: on
line diff
--- a/hgext/absorb.py	Sat Oct 05 10:29:34 2019 -0400
+++ b/hgext/absorb.py	Sun Oct 06 09:45:02 2019 -0400
@@ -53,9 +53,7 @@
     scmutil,
     util,
 )
-from mercurial.utils import (
-    stringutil,
-)
+from mercurial.utils import stringutil
 
 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
@@ -81,8 +79,10 @@
 
 defaultdict = collections.defaultdict
 
+
 class nullui(object):
     """blank ui object doing nothing"""
+
     debugflag = False
     verbose = False
     quiet = True
@@ -90,16 +90,20 @@
     def __getitem__(name):
         def nullfunc(*args, **kwds):
             return
+
         return nullfunc
 
+
 class emptyfilecontext(object):
     """minimal filecontext representing an empty file"""
+
     def data(self):
         return ''
 
     def node(self):
         return node.nullid
 
+
 def uniq(lst):
     """list -> list. remove duplicated items without changing the order"""
     seen = set()
@@ -110,6 +114,7 @@
             result.append(x)
     return result
 
+
 def getdraftstack(headctx, limit=None):
     """(ctx, int?) -> [ctx]. get a linear stack of non-public changesets.
 
@@ -132,6 +137,7 @@
     result.reverse()
     return result
 
+
 def getfilestack(stack, path, seenfctxs=None):
     """([ctx], str, set) -> [fctx], {ctx: fctx}
 
@@ -179,25 +185,25 @@
     fctxs = []
     fctxmap = {}
 
-    pctx = stack[0].p1() # the public (immutable) ctx we stop at
+    pctx = stack[0].p1()  # the public (immutable) ctx we stop at
     for ctx in reversed(stack):
-        if path not in ctx: # the file is added in the next commit
+        if path not in ctx:  # the file is added in the next commit
             pctx = ctx
             break
         fctx = ctx[path]
         fctxs.append(fctx)
-        if fctx in seenfctxs: # treat fctx as the immutable one
-            pctx = None # do not add another immutable fctx
+        if fctx in seenfctxs:  # treat fctx as the immutable one
+            pctx = None  # do not add another immutable fctx
             break
-        fctxmap[ctx] = fctx # only for mutable fctxs
+        fctxmap[ctx] = fctx  # only for mutable fctxs
         copy = fctx.copysource()
         if copy:
-            path = copy # follow rename
-            if path in ctx: # but do not follow copy
+            path = copy  # follow rename
+            if path in ctx:  # but do not follow copy
                 pctx = ctx.p1()
                 break
 
-    if pctx is not None: # need an extra immutable fctx
+    if pctx is not None:  # need an extra immutable fctx
         if path in pctx:
             fctxs.append(pctx[path])
         else:
@@ -213,10 +219,12 @@
     # remove uniq and find a different way to identify fctxs.
     return uniq(fctxs), fctxmap
 
+
 class overlaystore(patch.filestore):
     """read-only, hybrid store based on a dict and ctx.
     memworkingcopy: {path: content}, overrides file contents.
     """
+
     def __init__(self, basectx, memworkingcopy):
         self.basectx = basectx
         self.memworkingcopy = memworkingcopy
@@ -234,6 +242,7 @@
         copy = fctx.copysource()
         return content, mode, copy
 
+
 def overlaycontext(memworkingcopy, ctx, parents=None, extra=None):
     """({path: content}, ctx, (p1node, p2node)?, {}?) -> memctx
     memworkingcopy overrides file contents.
@@ -249,9 +258,17 @@
     files = set(ctx.files()).union(memworkingcopy)
     store = overlaystore(ctx, memworkingcopy)
     return context.memctx(
-        repo=ctx.repo(), parents=parents, text=desc,
-        files=files, filectxfn=store, user=user, date=date,
-        branch=None, extra=extra)
+        repo=ctx.repo(),
+        parents=parents,
+        text=desc,
+        files=files,
+        filectxfn=store,
+        user=user,
+        date=date,
+        branch=None,
+        extra=extra,
+    )
+
 
 class filefixupstate(object):
     """state needed to apply fixups to a single file
@@ -294,10 +311,10 @@
             assert self._checkoutlinelog() == self.contents
 
         # following fields will be filled later
-        self.chunkstats = [0, 0] # [adopted, total : int]
-        self.targetlines = [] # [str]
-        self.fixups = [] # [(linelog rev, a1, a2, b1, b2)]
-        self.finalcontents = [] # [str]
+        self.chunkstats = [0, 0]  # [adopted, total : int]
+        self.targetlines = []  # [str]
+        self.fixups = []  # [(linelog rev, a1, a2, b1, b2)]
+        self.finalcontents = []  # [str]
         self.ctxaffected = set()
 
     def diffwith(self, targetfctx, fm=None):
@@ -319,7 +336,7 @@
         self.targetlines = blines
 
         self.linelog.annotate(self.linelog.maxrev)
-        annotated = self.linelog.annotateresult # [(linelog rev, linenum)]
+        annotated = self.linelog.annotateresult  # [(linelog rev, linenum)]
         assert len(annotated) == len(alines)
         # add a dummy end line to make insertion at the end easier
         if annotated:
@@ -329,7 +346,7 @@
         # analyse diff blocks
         for chunk in self._alldiffchunks(a, b, alines, blines):
             newfixups = self._analysediffchunk(chunk, annotated)
-            self.chunkstats[0] += bool(newfixups) # 1 or 0
+            self.chunkstats[0] += bool(newfixups)  # 1 or 0
             self.chunkstats[1] += 1
             self.fixups += newfixups
             if fm is not None:
@@ -346,9 +363,10 @@
             blines = self.targetlines[b1:b2]
             if self.ui.debugflag:
                 idx = (max(rev - 1, 0)) // 2
-                self.ui.write(_('%s: chunk %d:%d -> %d lines\n')
-                              % (node.short(self.fctxs[idx].node()),
-                                 a1, a2, len(blines)))
+                self.ui.write(
+                    _('%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):
             self.finalcontents = self._checkoutlinelogwithedits()
@@ -382,12 +400,13 @@
         a1, a2, b1, b2 = chunk
         # find involved indexes from annotate result
         involved = annotated[a1:a2]
-        if not involved and annotated: # a1 == a2 and a is not empty
+        if not involved and annotated:  # a1 == a2 and a is not empty
             # pure insertion, check nearby lines. ignore lines belong
             # to the public (first) changeset (i.e. annotated[i][0] == 1)
             nearbylinenums = {a2, max(0, a1 - 1)}
-            involved = [annotated[i]
-                        for i in nearbylinenums if annotated[i][0] != 1]
+            involved = [
+                annotated[i] for i in nearbylinenums if annotated[i][0] != 1
+            ]
         involvedrevs = list(set(r for r, l in involved))
         newfixups = []
         if len(involvedrevs) == 1 and self._iscontinuous(a1, a2 - 1, True):
@@ -401,9 +420,9 @@
             for i in pycompat.xrange(a1, a2):
                 rev, linenum = annotated[i]
                 if rev > 1:
-                    if b1 == b2: # deletion, simply remove that single line
+                    if b1 == b2:  # deletion, simply remove that single line
                         nb1 = nb2 = 0
-                    else: # 1:1 line mapping, change the corresponding rev
+                    else:  # 1:1 line mapping, change the corresponding rev
                         nb1 = b1 + i - a1
                         nb2 = nb1 + 1
                     fixuprev = rev + 1
@@ -448,32 +467,45 @@
         """() -> [str]. prompt all lines for edit"""
         alllines = self.linelog.getalllines()
         # header
-        editortext = (_('HG: editing %s\nHG: "y" means the line to the right '
-                        'exists in the changeset to the top\nHG:\n')
-                      % self.fctxs[-1].path())
+        editortext = (
+            _(
+                'HG: editing %s\nHG: "y" means the line to the right '
+                'exists in the changeset to the top\nHG:\n'
+            )
+            % self.fctxs[-1].path()
+        )
         # [(idx, fctx)]. hide the dummy emptyfilecontext
-        visiblefctxs = [(i, f)
-                        for i, f in enumerate(self.fctxs)
-                        if not isinstance(f, emptyfilecontext)]
+        visiblefctxs = [
+            (i, f)
+            for i, f in enumerate(self.fctxs)
+            if not isinstance(f, emptyfilecontext)
+        ]
         for i, (j, f) in enumerate(visiblefctxs):
-            editortext += (_('HG: %s/%s %s %s\n') %
-                           ('|' * i, '-' * (len(visiblefctxs) - i + 1),
-                            node.short(f.node()),
-                            f.description().split('\n',1)[0]))
+            editortext += _('HG: %s/%s %s %s\n') % (
+                '|' * i,
+                '-' * (len(visiblefctxs) - i + 1),
+                node.short(f.node()),
+                f.description().split('\n', 1)[0],
+            )
         editortext += _('HG: %s\n') % ('|' * len(visiblefctxs))
         # figure out the lifetime of a line, this is relatively inefficient,
         # but probably fine
-        lineset = defaultdict(lambda: set()) # {(llrev, linenum): {llrev}}
+        lineset = defaultdict(lambda: set())  # {(llrev, linenum): {llrev}}
         for i, f in visiblefctxs:
             self.linelog.annotate((i + 1) * 2)
             for l in self.linelog.annotateresult:
                 lineset[l].add(i)
         # append lines
         for l in alllines:
-            editortext += ('    %s : %s' %
-                           (''.join([('y' if i in lineset[l] else ' ')
-                                     for i, _f in visiblefctxs]),
-                            self._getline(l)))
+            editortext += '    %s : %s' % (
+                ''.join(
+                    [
+                        ('y' if i in lineset[l] else ' ')
+                        for i, _f in visiblefctxs
+                    ]
+                ),
+                self._getline(l),
+            )
         # run editor
         editedtext = self.ui.edit(editortext, '', action='absorb')
         if not editedtext:
@@ -485,11 +517,12 @@
         for l in mdiff.splitnewlines(editedtext):
             if l.startswith('HG:'):
                 continue
-            if l[colonpos - 1:colonpos + 2] != ' : ':
+            if l[colonpos - 1 : colonpos + 2] != ' : ':
                 raise error.Abort(_('malformed line: %s') % l)
-            linecontent = l[colonpos + 2:]
+            linecontent = l[colonpos + 2 :]
             for i, ch in enumerate(
-                    pycompat.bytestr(l[leftpadpos:colonpos - 1])):
+                pycompat.bytestr(l[leftpadpos : colonpos - 1])
+            ):
                 if ch == 'y':
                     contents[visiblefctxs[i][0]] += linecontent
         # chunkstats is hard to calculate if anything changes, therefore
@@ -501,9 +534,9 @@
     def _getline(self, lineinfo):
         """((rev, linenum)) -> str. convert rev+line number to line content"""
         rev, linenum = lineinfo
-        if rev & 1: # odd: original line taken from fctxs
+        if rev & 1:  # odd: original line taken from fctxs
             return self.contentlines[rev // 2][linenum]
-        else: # even: fixup line from targetfctx
+        else:  # even: fixup line from targetfctx
             return self.targetlines[linenum]
 
     def _iscontinuous(self, a1, a2, closedinterval=False):
@@ -539,8 +572,12 @@
             lastrev = pcurrentchunk[0][0]
             lasta2 = pcurrentchunk[0][2]
             lastb2 = pcurrentchunk[0][4]
-            if (a1 == lasta2 and b1 == lastb2 and rev == lastrev and
-                    self._iscontinuous(max(a1 - 1, 0), a1)):
+            if (
+                a1 == lasta2
+                and b1 == lastb2
+                and rev == lastrev
+                and self._iscontinuous(max(a1 - 1, 0), a1)
+            ):
                 # merge into currentchunk
                 pcurrentchunk[0][2] = a2
                 pcurrentchunk[0][4] = b2
@@ -551,7 +588,6 @@
         return result
 
     def _showchanges(self, fm, alines, blines, chunk, fixups):
-
         def trim(line):
             if line.endswith('\n'):
                 line = line[:-1]
@@ -568,9 +604,12 @@
                 bidxs[i - b1] = (max(idx, 1) - 1) // 2
 
         fm.startitem()
-        fm.write('hunk', '        %s\n',
-                 '@@ -%d,%d +%d,%d @@'
-                 % (a1, a2 - a1, b1, b2 - b1), label='diff.hunk')
+        fm.write(
+            'hunk',
+            '        %s\n',
+            '@@ -%d,%d +%d,%d @@' % (a1, a2 - a1, b1, b2 - b1),
+            label='diff.hunk',
+        )
         fm.data(path=self.path, linetype='hunk')
 
         def writeline(idx, diffchar, line, linetype, linelabel):
@@ -582,16 +621,24 @@
                 node = ctx.hex()
                 self.ctxaffected.add(ctx.changectx())
             fm.write('node', '%-7.7s ', node, label='absorb.node')
-            fm.write('diffchar ' + linetype, '%s%s\n', diffchar, line,
-                     label=linelabel)
+            fm.write(
+                'diffchar ' + linetype,
+                '%s%s\n',
+                diffchar,
+                line,
+                label=linelabel,
+            )
             fm.data(path=self.path, linetype=linetype)
 
         for i in pycompat.xrange(a1, a2):
-            writeline(aidxs[i - a1], '-', trim(alines[i]), 'deleted',
-                      'diff.deleted')
+            writeline(
+                aidxs[i - a1], '-', trim(alines[i]), 'deleted', 'diff.deleted'
+            )
         for i in pycompat.xrange(b1, b2):
-            writeline(bidxs[i - b1], '+', trim(blines[i]), 'inserted',
-                      'diff.inserted')
+            writeline(
+                bidxs[i - b1], '+', trim(blines[i]), 'inserted', 'diff.inserted'
+            )
+
 
 class fixupstate(object):
     """state needed to run absorb
@@ -619,13 +666,13 @@
         self.repo = stack[-1].repo().unfiltered()
 
         # following fields will be filled later
-        self.paths = [] # [str]
-        self.status = None # ctx.status output
-        self.fctxmap = {} # {path: {ctx: fctx}}
-        self.fixupmap = {} # {path: filefixupstate}
-        self.replacemap = {} # {oldnode: newnode or None}
-        self.finalnode = None # head after all fixups
-        self.ctxaffected = set() # ctx that will be absorbed into
+        self.paths = []  # [str]
+        self.status = None  # ctx.status output
+        self.fctxmap = {}  # {path: {ctx: fctx}}
+        self.fixupmap = {}  # {path: filefixupstate}
+        self.replacemap = {}  # {oldnode: newnode or None}
+        self.finalnode = None  # head after all fixups
+        self.ctxaffected = set()  # ctx that will be absorbed into
 
     def diffwith(self, targetctx, match=None, fm=None):
         """diff and prepare fixups. update self.fixupmap, self.paths"""
@@ -648,9 +695,11 @@
             targetfctx = targetctx[path]
             fctxs, ctx2fctx = getfilestack(self.stack, path, seenfctxs)
             # ignore symbolic links or binary, or unchanged files
-            if any(f.islink() or stringutil.binary(f.data())
-                   for f in [targetfctx] + fctxs
-                   if not isinstance(f, emptyfilecontext)):
+            if any(
+                f.islink() or stringutil.binary(f.data())
+                for f in [targetfctx] + fctxs
+                if not isinstance(f, emptyfilecontext)
+            ):
                 continue
             if targetfctx.data() == fctxs[-1].data() and not editopt:
                 continue
@@ -677,8 +726,10 @@
     @property
     def chunkstats(self):
         """-> {path: chunkstats}. collect chunkstats from filefixupstates"""
-        return dict((path, state.chunkstats)
-                    for path, state in self.fixupmap.iteritems())
+        return dict(
+            (path, state.chunkstats)
+            for path, state in self.fixupmap.iteritems()
+        )
 
     def commit(self):
         """commit changes. update self.finalnode, self.replacemap"""
@@ -698,8 +749,10 @@
             # chunkstats for each file
             for path, stat in chunkstats.iteritems():
                 if stat[0]:
-                    ui.write(_('%s: %d of %d chunk(s) applied\n')
-                             % (path, stat[0], stat[1]))
+                    ui.write(
+                        _('%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()
@@ -733,7 +786,9 @@
                 self.replacemap[ctx.node()] = lastcommitted.node()
                 if memworkingcopy:
                     msg = _('%d file(s) changed, became %s') % (
-                        len(memworkingcopy), self._ctx2str(lastcommitted))
+                        len(memworkingcopy),
+                        self._ctx2str(lastcommitted),
+                    )
                 else:
                     msg = _('became %s') % self._ctx2str(lastcommitted)
             if self.ui.verbose and msg:
@@ -754,7 +809,7 @@
         """
         result = {}
         for path in self.paths:
-            ctx2fctx = self.fctxmap[path] # {ctx: fctx}
+            ctx2fctx = self.fctxmap[path]  # {ctx: fctx}
             if ctx not in ctx2fctx:
                 continue
             fctx = ctx2fctx[ctx]
@@ -766,16 +821,19 @@
 
     def _movebookmarks(self, tr):
         repo = self.repo
-        needupdate = [(name, self.replacemap[hsh])
-                      for name, hsh in repo._bookmarks.iteritems()
-                      if hsh in self.replacemap]
+        needupdate = [
+            (name, self.replacemap[hsh])
+            for name, hsh in repo._bookmarks.iteritems()
+            if hsh in self.replacemap
+        ]
         changes = []
         for name, hsh in needupdate:
             if hsh:
                 changes.append((name, hsh))
                 if self.ui.verbose:
-                    self.ui.write(_('moving bookmark %s to %s\n')
-                                  % (name, node.hex(hsh)))
+                    self.ui.write(
+                        _('moving bookmark %s to %s\n') % (name, node.hex(hsh))
+                    )
             else:
                 changes.append((name, None))
                 if self.ui.verbose:
@@ -798,8 +856,10 @@
         restore = noop
         if util.safehasattr(dirstate, '_fsmonitorstate'):
             bak = dirstate._fsmonitorstate.invalidate
+
             def restore():
                 dirstate._fsmonitorstate.invalidate = bak
+
             dirstate._fsmonitorstate.invalidate = noop
         try:
             with dirstate.parentchange():
@@ -852,11 +912,15 @@
         return obsolete.isenabled(self.repo, obsolete.createmarkersopt)
 
     def _cleanupoldcommits(self):
-        replacements = {k: ([v] if v is not None else [])
-                        for k, v in self.replacemap.iteritems()}
+        replacements = {
+            k: ([v] if v is not None else [])
+            for k, v in self.replacemap.iteritems()
+        }
         if replacements:
-            scmutil.cleanupnodes(self.repo, replacements, operation='absorb',
-                                 fixphase=True)
+            scmutil.cleanupnodes(
+                self.repo, replacements, operation='absorb', fixphase=True
+            )
+
 
 def _parsechunk(hunk):
     """(crecord.uihunk or patch.recordhunk) -> (path, (a1, a2, [bline]))"""
@@ -874,6 +938,7 @@
     blines = [l[1:] for l in patchlines[1:] if not l.startswith('-')]
     return path, (a1, a2, blines)
 
+
 def overlaydiffcontext(ctx, chunks):
     """(ctx, [crecord.uihunk]) -> memctx
 
@@ -889,8 +954,8 @@
     # as we only care about applying changes to modified files, no mode
     # change, no binary diff, and no renames, it's probably okay to
     # re-invent the logic using much simpler code here.
-    memworkingcopy = {} # {path: content}
-    patchmap = defaultdict(lambda: []) # {path: [(a1, a2, [bline])]}
+    memworkingcopy = {}  # {path: content}
+    patchmap = defaultdict(lambda: [])  # {path: [(a1, a2, [bline])]}
     for path, info in map(_parsechunk, chunks):
         if not path or not info:
             continue
@@ -905,6 +970,7 @@
         memworkingcopy[path] = ''.join(lines)
     return overlaycontext(memworkingcopy, ctx)
 
+
 def absorb(ui, repo, stack=None, targetctx=None, pats=None, opts=None):
     """pick fixup chunks from targetctx, apply them to stack.
 
@@ -919,12 +985,13 @@
             raise error.Abort(_('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')
-                    % limit)
+            ui.warn(
+                _('absorb: only the recent %d changesets will ' 'be analysed\n')
+                % limit
+            )
     if not stack:
         raise error.Abort(_('no mutable changeset to change'))
-    if targetctx is None: # default to working copy
+    if targetctx is None:  # default to working copy
         targetctx = repo[None]
     if pats is None:
         pats = ()
@@ -953,13 +1020,19 @@
             fm.data(linetype='changeset')
             fm.write('node', '%-7.7s ', ctx.hex(), label='absorb.node')
             descfirstline = ctx.description().splitlines()[0]
-            fm.write('descfirstline', '%s\n', descfirstline,
-                     label='absorb.description')
+            fm.write(
+                'descfirstline',
+                '%s\n',
+                descfirstline,
+                label='absorb.description',
+            )
         fm.end()
     if not opts.get('dry_run'):
-        if (not opts.get('apply_changes') and
-            state.ctxaffected and
-            ui.promptchoice("apply changes (yn)? $$ &Yes $$ &No", default=1)):
+        if (
+            not opts.get('apply_changes')
+            and state.ctxaffected
+            and ui.promptchoice("apply changes (yn)? $$ &Yes $$ &No", default=1)
+        ):
             raise error.Abort(_('absorb cancelled\n'))
 
         state.apply()
@@ -969,20 +1042,45 @@
             ui.write(_('nothing applied\n'))
     return state
 
-@command('absorb',
-         [('a', 'apply-changes', None,
-           _('apply changes without prompting for confirmation')),
-          ('p', 'print-changes', None,
-           _('always print which changesets are modified by which changes')),
-          ('i', 'interactive', None,
-           _('interactively select which chunks to apply (EXPERIMENTAL)')),
-          ('e', 'edit-lines', None,
-           _('edit what lines belong to which changesets before commit '
-             '(EXPERIMENTAL)')),
-         ] + commands.dryrunopts + commands.templateopts + commands.walkopts,
-         _('hg absorb [OPTION] [FILE]...'),
-         helpcategory=command.CATEGORY_COMMITTING,
-         helpbasic=True)
+
+@command(
+    'absorb',
+    [
+        (
+            'a',
+            'apply-changes',
+            None,
+            _('apply changes without prompting for confirmation'),
+        ),
+        (
+            'p',
+            'print-changes',
+            None,
+            _('always print which changesets are modified by which changes'),
+        ),
+        (
+            'i',
+            'interactive',
+            None,
+            _('interactively select which chunks to apply (EXPERIMENTAL)'),
+        ),
+        (
+            'e',
+            'edit-lines',
+            None,
+            _(
+                'edit what lines belong to which changesets before commit '
+                '(EXPERIMENTAL)'
+            ),
+        ),
+    ]
+    + commands.dryrunopts
+    + commands.templateopts
+    + commands.walkopts,
+    _('hg absorb [OPTION] [FILE]...'),
+    helpcategory=command.CATEGORY_COMMITTING,
+    helpbasic=True,
+)
 def absorbcmd(ui, repo, *pats, **opts):
     """incorporate corrections into the stack of draft changesets