Enhance the file filtering capabilities.
We now allow filtering through either pipes or pairs of temporary
files. The latter appear to be mandatory for use on Windows.
--- a/doc/hgrc.5.txt Wed Sep 21 09:56:30 2005 -0700
+++ b/doc/hgrc.5.txt Wed Sep 21 11:44:08 2005 -0700
@@ -67,20 +67,53 @@
localization/canonicalization of files.
Filters consist of a filter pattern followed by a filter command.
- The command must accept data on stdin and return the transformed
- data on stdout.
+ Filter patterns are globs by default, rooted at the repository
+ root. For example, to match any file ending in ".txt" in the root
+ directory only, use the pattern "*.txt". To match any file ending
+ in ".c" anywhere in the repository, use the pattern "**.c".
- Example:
+ The filter command can start with a specifier, either "pipe:" or
+ "tempfile:". If no specifier is given, "pipe:" is used by default.
+
+ A "pipe:" command must accept data on stdin and return the
+ transformed data on stdout.
+
+ Pipe example:
[encode]
# uncompress gzip files on checkin to improve delta compression
# note: not necessarily a good idea, just an example
- *.gz = gunzip
+ *.gz = pipe: gunzip
[decode]
- # recompress gzip files when writing them to the working dir
+ # recompress gzip files when writing them to the working dir (we
+ # can safely omit "pipe:", because it's the default)
*.gz = gzip
+ A "tempfile:" command is a template. The string INFILE is replaced
+ with the name of a temporary file that contains the data to be
+ filtered by the command. The string OUTFILE is replaced with the
+ name of an empty temporary file, where the filtered data must be
+ written by the command.
+
+ NOTE: the tempfile mechanism is recommended for Windows systems,
+ where the standard shell I/O redirection operators often have
+ strange effects. In particular, if you are doing line ending
+ conversion on Windows using the popular dos2unix and unix2dos
+ programs, you *must* use the tempfile mechanism, as using pipes will
+ corrupt the contents of your files.
+
+ Tempfile example:
+
+ [encode]
+ # convert files to unix line ending conventions on checkin
+ **.txt = tempfile: dos2unix -n INFILE OUTFILE
+
+ [decode]
+ # convert files to windows line ending conventions when writing
+ # them to the working dir
+ **.txt = tempfile: unix2dos -n INFILE OUTFILE
+
hooks::
Commands that get automatically executed by various actions such as
starting or finishing a commit.
--- a/mercurial/util.py Wed Sep 21 09:56:30 2005 -0700
+++ b/mercurial/util.py Wed Sep 21 11:44:08 2005 -0700
@@ -12,10 +12,10 @@
import os, errno
from demandload import *
-demandload(globals(), "re cStringIO shutil popen2 threading")
+demandload(globals(), "re cStringIO shutil popen2 tempfile threading")
-def filter(s, cmd):
- "filter a string through a command that transforms its input to its output"
+def pipefilter(s, cmd):
+ '''filter string S through command CMD, returning its output'''
(pout, pin) = popen2.popen2(cmd, -1, 'b')
def writer():
pin.write(s)
@@ -30,6 +30,45 @@
w.join()
return f
+def tempfilter(s, cmd):
+ '''filter string S through a pair of temporary files with CMD.
+ CMD is used as a template to create the real command to be run,
+ with the strings INFILE and OUTFILE replaced by the real names of
+ the temporary files generated.'''
+ inname, outname = None, None
+ try:
+ infd, inname = tempfile.mkstemp(prefix='hgfin')
+ fp = os.fdopen(infd, 'wb')
+ fp.write(s)
+ fp.close()
+ outfd, outname = tempfile.mkstemp(prefix='hgfout')
+ os.close(outfd)
+ cmd = cmd.replace('INFILE', inname)
+ cmd = cmd.replace('OUTFILE', outname)
+ code = os.system(cmd)
+ if code: raise Abort("command '%s' failed: %s" %
+ (cmd, explain_exit(code)))
+ return open(outname, 'rb').read()
+ finally:
+ try:
+ if inname: os.unlink(inname)
+ except: pass
+ try:
+ if outname: os.unlink(outname)
+ except: pass
+
+filtertable = {
+ 'tempfile:': tempfilter,
+ 'pipe:': pipefilter,
+ }
+
+def filter(s, cmd):
+ "filter a string through a command that transforms its input to its output"
+ for name, fn in filtertable.iteritems():
+ if cmd.startswith(name):
+ return fn(s, cmd[len(name):].lstrip())
+ return pipefilter(s, cmd)
+
def patch(strip, patchname, ui):
"""apply the patch <patchname> to the working directory.
a list of patched files is returned"""
@@ -43,7 +82,7 @@
files.setdefault(pf, 1)
code = fp.close()
if code:
- raise Abort("patch command failed: exit status %s " % code)
+ raise Abort("patch command failed: %s" % explain_exit(code))
return files.keys()
def binary(s):