[PATCH 1 of 1] convert: add option --contentsfilter scriptname

Jason Harris jason.f.harris at gmail.com
Sun Apr 17 19:36:23 CDT 2011


# HG changeset patch
# User jfh <jason at jasonfharris.com>
# Date 1303084955 -7200
# Node ID 3637c5384a73e35bbbecc611004351a75d8d6b8f
# Parent  c860adb9feb161c27722af9d3e913e82ecb75e1e
convert: add option --contentsfilter scriptname

The contentsfilter can potentially transform the contents of every file at every
revision as the conversion happens. If the --contentsfilter scriptname option is
specified, then scriptname should be the full path name of a script which takes
three arguments - (i) original file name, (ii) the original changeset hash, and
(iii) the original contents of the file. The script should transform the passed
in file contents into the desired final output form and write this to stdout.
The result code returned should be 0 if the contents were modified and 1 if the
contents did not need modification.

The contentsfilter option is useful for example to transform all of the new
lines to / from unix for every file at every revision in the repository, or do
some special replacement for every file in the repository. For a more
complicated example one might transform c sources files using uncrustify to
transform to a new coding standard.

diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py
--- a/hgext/convert/__init__.py
+++ b/hgext/convert/__init__.py
@@ -102,6 +102,22 @@
     it is converted. To rename from a subdirectory into the root of
     the repository, use ``.`` as the path to rename to.
 
+    The contentsfilter can potentially transform the contents of every file at
+    every revision as the conversion happens. If the --contentsfilter scriptname
+    option is specified, then scriptname should be the full path name of a
+    script which takes three arguments - (i) original file name, (ii) the
+    original changeset hash, and (iii) the original contents of the file. The
+    script should transform the passed in file contents into the desired final
+    output form and write this to stdout. The result code returned should be 0
+    if the contents were modified and 1 if the contents did not need
+    modification.
+
+    The contentsfilter option is useful for example to transform all of the new
+    lines to / from unix for every file at every revision in the repository, or
+    do some special replacement for every file in the repository. For a more
+    complicated example one might transform c sources files using uncrustify to
+    transform to a new coding standard.
+
     The splicemap is a file that allows insertion of synthetic
     history, letting you specify the parents of a revision. This is
     useful if you want to e.g. give a Subversion merge two parents, or
@@ -300,6 +316,9 @@
            _('import up to target revision REV'), _('REV')),
           ('A', 'authormap', '',
            _('remap usernames using this file'), _('FILE')),
+          ('', 'contentsfilter', '',
+           _('transform the contents of each file according to an external filter'),
+           _('FILE')),
           ('', 'filemap', '',
            _('remap file names using contents of file'), _('FILE')),
           ('', 'splicemap', '',
diff --git a/hgext/convert/convcmd.py b/hgext/convert/convcmd.py
--- a/hgext/convert/convcmd.py
+++ b/hgext/convert/convcmd.py
@@ -120,6 +120,7 @@
 
         self.splicemap = mapfile(ui, opts.get('splicemap'))
         self.branchmap = mapfile(ui, opts.get('branchmap'))
+        self.contentsfilter = opts.get('contentsfilter')
 
     def walktree(self, heads):
         '''Return a mapping that identifies the uncommitted parents of every
@@ -326,6 +327,7 @@
         except KeyError:
             parents = [b[0] for b in pbranches]
         source = progresssource(self.ui, self.source, len(files))
+        self.dest.contentsfilter = self.contentsfilter
         newnode = self.dest.putcommit(files, copies, parents, commit,
                                       source, self.map)
         source.close()
diff --git a/hgext/convert/hg.py b/hgext/convert/hg.py
--- a/hgext/convert/hg.py
+++ b/hgext/convert/hg.py
@@ -18,7 +18,7 @@
 #   source.
 
 
-import os, time, cStringIO
+import os, time, cStringIO, subprocess
 from mercurial.i18n import _
 from mercurial.node import bin, hex, nullid
 from mercurial import hg, util, context, bookmarks, error
@@ -135,6 +135,18 @@
         def getfilectx(repo, memctx, f):
             v = files[f]
             data, mode = source.getfile(f, v)
+            if isinstance(data, str) and hasattr(memctx, '_contentsfilter'):
+                filter = memctx._contentsfilter
+                if filter != '':
+                    try:
+                        hash = v[1] if len(v) >= 2 else '' 
+                        p = subprocess.Popen([filter, f, hash, data],
+                              stdout=subprocess.PIPE, close_fds=True)
+                        child_stdout = p.communicate()[0]
+                        if p.returncode == 0:
+                            data = child_stdout
+                    except:
+                        pass
             if f == '.hgtags':
                 data = self._rewritetags(source, revmap, data)
             return context.memfilectx(f, data, 'l' in mode, 'x' in mode,
@@ -168,6 +180,7 @@
             p2 = parents.pop(0)
             ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
                                  getfilectx, commit.author, commit.date, extra)
+            ctx._contentsfilter = self.contentsfilter
             self.repo.commitctx(ctx)
             text = "(octopus merge fixup)\n"
             p2 = hex(self.repo.changelog.tip())


More information about the Mercurial-devel mailing list