Patchwork D3174: cmdutil: refactor I/O for exporting

login
register
mail settings
Submitter phabricator
Date April 6, 2018, 10:10 p.m.
Message ID <differential-rev-PHID-DREV-mn5grlyljylms2wfirzm-req@phab.mercurial-scm.org>
Download mbox | patch
Permalink /patch/30471/
State Superseded
Headers show

Comments

phabricator - April 6, 2018, 10:10 p.m.
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Using generators for feeding chunks into I/O is more flexible than
  passing a file object or write function around.
  
  This commit refactors the patch exporting code to emit a series a data
  chunks instead of doing push-based I/O. As a side-effect, we could now
  obtain the patch representation of a changeset without having to use
  a file object. Yay!
  
  Code around the management of the file handles has also been cleaned up
  a bit.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D3174

AFFECTED FILES
  mercurial/cmdutil.py

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: mercurial-devel

Patch

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1526,7 +1526,7 @@ 
 # it is given two arguments (sequencenumber, changectx)
 extraexportmap = {}
 
-def _exportsingle(repo, ctx, match, switch_parent, seqno, write, diffopts):
+def _exportsingle(repo, ctx, match, switch_parent, seqno, diffopts):
     node = scmutil.binnode(ctx)
     parents = [p.node() for p in ctx.parents() if p]
     branch = ctx.branch()
@@ -1538,26 +1538,26 @@ 
     else:
         prev = nullid
 
-    write("# HG changeset patch\n")
-    write("# User %s\n" % ctx.user())
-    write("# Date %d %d\n" % ctx.date())
-    write("#      %s\n" % dateutil.datestr(ctx.date()))
+    yield "# HG changeset patch\n", None
+    yield "# User %s\n" % ctx.user(), None
+    yield "# Date %d %d\n" % ctx.date(), None
+    yield "#      %s\n" % dateutil.datestr(ctx.date()), None
     if branch and branch != 'default':
-        write("# Branch %s\n" % branch)
-    write("# Node ID %s\n" % hex(node))
-    write("# Parent  %s\n" % hex(prev))
+        yield "# Branch %s\n" % branch, None
+    yield "# Node ID %s\n" % hex(node), None
+    yield "# Parent  %s\n" % hex(prev), None
     if len(parents) > 1:
-        write("# Parent  %s\n" % hex(parents[1]))
+        yield "# Parent  %s\n" % hex(parents[1]), None
 
     for headerid in extraexport:
         header = extraexportmap[headerid](seqno, ctx)
         if header is not None:
-            write('# %s\n' % header)
-    write(ctx.description().rstrip())
-    write("\n\n")
+            yield '# %s\n' % header, None
+    yield ctx.description().rstrip(), None
+    yield "\n\n", None
 
     for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
-        write(chunk, label=label)
+        yield chunk, label
 
 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
            opts=None, match=None):
@@ -1590,30 +1590,40 @@ 
     revwidth = max(len(str(rev)) for rev in revs)
     filemode = {}
 
-    write = None
     dest = '<unnamed>'
     if fp:
+        allfp = fp
         dest = getattr(fp, 'name', dest)
-        def write(s, **kw):
-            fp.write(s)
+        supportslabel = False
     elif not fntemplate:
-        write = repo.ui.write
+        allfp = repo.ui
+        supportslabel = True
+    else:
+        allfp = None
+        supportslabel = False
+
+    def doexport(ctx, fp, dest):
+        if not dest.startswith('<'):
+            repo.ui.note('%s\n' % dest)
+
+        for s, label in _exportsingle(repo, ctx, match, switch_parent, seqno,
+                                      opts):
+            if supportslabel and label:
+                fp.write(s, label=label)
+            else:
+                fp.write(s)
 
     for seqno, rev in enumerate(revs, 1):
         ctx = repo[rev]
-        fo = None
-        if not fp and fntemplate:
-            fo = makefileobj(ctx, fntemplate, mode='wb', modemap=filemode,
-                             total=total, seqno=seqno, revwidth=revwidth)
-            dest = fo.name
-            def write(s, **kw):
-                fo.write(s)
-        if not dest.startswith('<'):
-            repo.ui.note("%s\n" % dest)
-        _exportsingle(
-            repo, ctx, match, switch_parent, seqno, write, opts)
-        if fo is not None:
-            fo.close()
+
+        if not allfp:
+            assert fntemplate
+
+            with makefileobj(ctx, fntemplate, mode='wb', modemap=filemode,
+                             total=total, seqno=seqno, revwidth=revwidth) as fp:
+                doexport(ctx, fp, fp.name)
+        else:
+            doexport(ctx, allfp, dest)
 
 def showmarker(fm, marker, index=None):
     """utility function to display obsolescence marker in a readable way