[PATCH RFC] annotate: add '--ignore-expression' option

Angel Ezquerra angel.ezquerra at gmail.com
Tue Jun 5 14:17:10 CDT 2012


# HG changeset patch
# User Angel Ezquerra <angel.ezquerra at gmail.com>
# Date 1338844044 -7200
# Node ID 8a2f7c216f39013c9e7868e036ff75881efdfc17
# Parent  d566aa319d5f7c58c69b985b53ff7498f08e53c6
annotate: add '--ignore-expression' option

Add a new annotate option, --ignore-expression, with -e as its short version.
This new option lets the user specify a regular expression substitution that
will be applied to all lines of a file before comparing the a file to its parent
revision. This gives a flexible mechanism to control which changes must be taken
into account when annotating files.

The "ignore expression" is a simplified "perl-like" or vim-like expression with
the following format:

SEPARATORCHARsearchstringSEPARATORCHAR[replacestring]

(e.g. /hello/world or :hello:world)

where:

- SEPARATORCHAR: Any character (usually "/", ":" or "$")
- searchstring: The string that will be looked for. It will be used as the first
parameter of re.sub(), which means that it can specify substitution groups, etc
- replacestring: An optional string which will be used as the second parameter
of re.sub() (i.e. it can refer to groups as \1, \2, etc). If not specified, an
empty string will be used (which means that the searchstring will be removed)


This can be used to ignore certain changes. For example:

- ignore a change of an identifier (e.g. myvar) into another (mynewvar):

hg annotate -e "/myvar/mynewvar"

- ignore a change in EOL characters:

hg annotate -e ":(\r\n|\n):\n" myfile.txt

- ignore a change of tabs to 4 spaces:

hg annotate -e ":\t:    " myfile.txt

- ignore a change of "unsigned" into "unsigned int", using a group substitution:

hg annotate -e ":(unsigned):\1 int" myfile.txt

- ignore all white space change (i.e. same as -w, but slower):

hg annotate -e ":[\t ]:" myfile.txt

- ignore all trailing white space changes (on Windows):

hg annotate -e ":[ ]+\r\n:\r\n" myfile.txt

NOTES:
- When unused, this should have no impact on the speed of the "hg annotate"
command.
- When used, the speed of "hg annotate" will depend on the complexity of the
regular expression. In any case it should be slower than the -w and -b and -B
options, which are implemented in C.

For example:

hg annotate -e ":[\t ]:" mercurial\patch.py

is about 4 or 5 times slower than the equivalent:

hg annotate -w mercurial\patch.py

- The matching is performed on the whole file contents, not line by line. Thus
you need to end the expression with \n or \r if you want to match up to the
end of a line.

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -113,6 +113,8 @@
      _('ignore changes in the amount of white space')),
     ('B', 'ignore-blank-lines', None,
      _('ignore changes whose lines are all blank')),
+    ('e', 'ignore-expression', '',
+     _('apply a regular expression substitution before looking for line changes')),
     ]
 
 diffopts2 = [
diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py
--- a/mercurial/mdiff.py
+++ b/mercurial/mdiff.py
@@ -40,6 +40,7 @@
         'ignorews': False,
         'ignorewsamount': False,
         'ignoreblanklines': False,
+        'ignoreexpr': None,
         'upgrade': False,
         }
 
@@ -70,6 +71,12 @@
         text = bdiff.fixws(text, 1)
     elif opts.ignorewsamount:
         text = bdiff.fixws(text, 0)
+    if opts.ignoreexpr:
+        # ignoreexpr must be a 2 element tuple
+        # The first element is a complied regular expression
+        # The second is a string containing the replace string
+        searchexpr, replacexpr = opts.ignoreexpr
+        text = searchexpr.sub(replacexpr, text)
     if blank and opts.ignoreblanklines:
         text = re.sub('\n+', '\n', text).strip('\n')
     return text
@@ -111,7 +118,7 @@
     """
     if opts is None:
         opts = defaultopts
-    if opts.ignorews or opts.ignorewsamount:
+    if opts.ignorews or opts.ignorewsamount or opts.ignoreexpr:
         text1 = wsclean(opts, text1, False)
         text2 = wsclean(opts, text2, False)
     diff = bdiff.blocks(text1, text2)
diff --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -1559,6 +1559,19 @@
     def get(key, name=None, getter=ui.configbool):
         return ((opts and opts.get(key)) or
                 getter(section, name or key, None, untrusted=untrusted))
+    def getignoreexpr():
+        ignoreexpr = get('ignore_expression', 'ignoreexpr')
+        if ignoreexpr:
+            separator = ignoreexpr[0]
+            ignoreexpr = ignoreexpr[1:].split(separator)
+            ignorere = re.compile(ignoreexpr[0])
+            replacexpr = ''
+            if len(ignoreexpr) > 1:
+                if len(ignoreexpr) > 2:
+                    ui.warn(_('too many fields in ignore expression'))
+                replacexpr = ignoreexpr[1]
+            ignoreexpr = (ignorere, replacexpr)
+        return ignoreexpr
     return mdiff.diffopts(
         text=opts and opts.get('text'),
         git=get('git'),
@@ -1567,6 +1580,7 @@
         ignorews=get('ignore_all_space', 'ignorews'),
         ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
         ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
+        ignoreexpr=getignoreexpr(),
         context=get('unified', getter=ui.config))
 
 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,


More information about the Mercurial-devel mailing list