hgext/win32text.py
changeset 6484 ab8038bf5127
parent 6473 9c897ffd3637
parent 6483 0a803195bb29
child 6508 4b2c266bf059
equal deleted inserted replaced
6480:ee7762515a41 6484:ab8038bf5127
     1 # win32text.py - LF <-> CRLF translation utilities for Windows users
     1 # win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users
     2 #
     2 #
     3 # This software may be used and distributed according to the terms
     3 # This software may be used and distributed according to the terms
     4 # of the GNU General Public License, incorporated herein by reference.
     4 # of the GNU General Public License, incorporated herein by reference.
     5 #
     5 #
     6 # To perform automatic newline conversion, use:
     6 # To perform automatic newline conversion, use:
     7 #
     7 #
     8 # [extensions]
     8 # [extensions]
     9 # hgext.win32text =
     9 # hgext.win32text =
    10 # [encode]
    10 # [encode]
    11 # ** = cleverencode:
    11 # ** = cleverencode:
       
    12 # # or ** = macencode:
    12 # [decode]
    13 # [decode]
    13 # ** = cleverdecode:
    14 # ** = cleverdecode:
       
    15 # # or ** = macdecode:
    14 #
    16 #
    15 # If not doing conversion, to make sure you do not commit CRLF by accident:
    17 # If not doing conversion, to make sure you do not commit CRLF/CR by accident:
    16 #
    18 #
    17 # [hooks]
    19 # [hooks]
    18 # pretxncommit.crlf = python:hgext.win32text.forbidcrlf
    20 # pretxncommit.crlf = python:hgext.win32text.forbidcrlf
       
    21 # # or pretxncommit.cr = python:hgext.win32text.forbidcr
    19 #
    22 #
    20 # To do the same check on a server to prevent CRLF from being pushed or pulled:
    23 # To do the same check on a server to prevent CRLF/CR from being pushed or
       
    24 # pulled:
    21 #
    25 #
    22 # [hooks]
    26 # [hooks]
    23 # pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
    27 # pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
       
    28 # # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr
    24 
    29 
    25 from mercurial.i18n import gettext as _
    30 from mercurial.i18n import gettext as _
    26 from mercurial.node import bin, short
    31 from mercurial.node import bin, short
    27 import re
    32 import re
    28 
    33 
    29 # regexp for single LF without CR preceding.
    34 # regexp for single LF without CR preceding.
    30 re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
    35 re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
    31 
    36 
    32 def dumbdecode(s, cmd, ui=None, repo=None, filename=None, **kwargs):
    37 newlinestr = {'\r\n': 'CRLF', '\r': 'CR'}
    33     # warn if already has CRLF in repository.
    38 filterstr = {'\r\n': 'clever', '\r': 'mac'}
       
    39 
       
    40 def checknewline(s, newline, ui=None, repo=None, filename=None):
       
    41     # warn if already has 'newline' in repository.
    34     # it might cause unexpected eol conversion.
    42     # it might cause unexpected eol conversion.
    35     # see issue 302:
    43     # see issue 302:
    36     #   http://www.selenic.com/mercurial/bts/issue302
    44     #   http://www.selenic.com/mercurial/bts/issue302
    37     if '\r\n' in s and ui and filename and repo:
    45     if newline in s and ui and filename and repo:
    38         ui.warn(_('WARNING: %s already has CRLF line endings\n'
    46         ui.warn(_('WARNING: %s already has %s line endings\n'
    39                   'and does not need EOL conversion by the win32text plugin.\n'
    47                   'and does not need EOL conversion by the win32text plugin.\n'
    40                   'Before your next commit, please reconsider your '
    48                   'Before your next commit, please reconsider your '
    41                   'encode/decode settings in \nMercurial.ini or %s.\n') %
    49                   'encode/decode settings in \nMercurial.ini or %s.\n') %
    42                 (filename, repo.join('hgrc')))
    50                 (filename, newlinestr[newline], repo.join('hgrc')))
       
    51 
       
    52 def dumbdecode(s, cmd, **kwargs):
       
    53     checknewline(s, '\r\n', **kwargs)
    43     # replace single LF to CRLF
    54     # replace single LF to CRLF
    44     return re_single_lf.sub('\\1\r\n', s)
    55     return re_single_lf.sub('\\1\r\n', s)
    45 
    56 
    46 def dumbencode(s, cmd):
    57 def dumbencode(s, cmd):
    47     return s.replace('\r\n', '\n')
    58     return s.replace('\r\n', '\n')
       
    59 
       
    60 def macdumbdecode(s, cmd, **kwargs):
       
    61     checknewline(s, '\r', **kwargs)
       
    62     return s.replace('\n', '\r')
       
    63 
       
    64 def macdumbencode(s, cmd):
       
    65     return s.replace('\r', '\n')
    48 
    66 
    49 def clevertest(s, cmd):
    67 def clevertest(s, cmd):
    50     if '\0' in s: return False
    68     if '\0' in s: return False
    51     return True
    69     return True
    52 
    70 
    58 def cleverencode(s, cmd):
    76 def cleverencode(s, cmd):
    59     if clevertest(s, cmd):
    77     if clevertest(s, cmd):
    60         return dumbencode(s, cmd)
    78         return dumbencode(s, cmd)
    61     return s
    79     return s
    62 
    80 
       
    81 def macdecode(s, cmd, **kwargs):
       
    82     if clevertest(s, cmd):
       
    83         return macdumbdecode(s, cmd, **kwargs)
       
    84     return s
       
    85 
       
    86 def macencode(s, cmd):
       
    87     if clevertest(s, cmd):
       
    88         return macdumbencode(s, cmd)
       
    89     return s
       
    90 
    63 _filters = {
    91 _filters = {
    64     'dumbdecode:': dumbdecode,
    92     'dumbdecode:': dumbdecode,
    65     'dumbencode:': dumbencode,
    93     'dumbencode:': dumbencode,
    66     'cleverdecode:': cleverdecode,
    94     'cleverdecode:': cleverdecode,
    67     'cleverencode:': cleverencode,
    95     'cleverencode:': cleverencode,
       
    96     'macdumbdecode:': macdumbdecode,
       
    97     'macdumbencode:': macdumbencode,
       
    98     'macdecode:': macdecode,
       
    99     'macencode:': macencode,
    68     }
   100     }
    69 
   101 
    70 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
   102 def forbidnewline(ui, repo, hooktype, node, newline, **kwargs):
    71     halt = False
   103     halt = False
    72     for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()):
   104     for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()):
    73         c = repo.changectx(rev)
   105         c = repo.changectx(rev)
    74         for f in c.files():
   106         for f in c.files():
    75             if f not in c:
   107             if f not in c:
    76                 continue
   108                 continue
    77             data = c[f].data()
   109             data = c[f].data()
    78             if '\0' not in data and '\r\n' in data:
   110             if '\0' not in data and newline in data:
    79                 if not halt:
   111                 if not halt:
    80                     ui.warn(_('Attempt to commit or push text file(s) '
   112                     ui.warn(_('Attempt to commit or push text file(s) '
    81                               'using CRLF line endings\n'))
   113                               'using %s line endings\n') %
       
   114                               newlinestr[newline])
    82                 ui.warn(_('in %s: %s\n') % (short(c.node()), f))
   115                 ui.warn(_('in %s: %s\n') % (short(c.node()), f))
    83                 halt = True
   116                 halt = True
    84     if halt and hooktype == 'pretxnchangegroup':
   117     if halt and hooktype == 'pretxnchangegroup':
       
   118         crlf = newlinestr[newline].lower()
       
   119         filter = filterstr[newline]
    85         ui.warn(_('\nTo prevent this mistake in your local repository,\n'
   120         ui.warn(_('\nTo prevent this mistake in your local repository,\n'
    86                   'add to Mercurial.ini or .hg/hgrc:\n'
   121                   'add to Mercurial.ini or .hg/hgrc:\n'
    87                   '\n'
   122                   '\n'
    88                   '[hooks]\n'
   123                   '[hooks]\n'
    89                   'pretxncommit.crlf = python:hgext.win32text.forbidcrlf\n'
   124                   'pretxncommit.%s = python:hgext.win32text.forbid%s\n'
    90                   '\n'
   125                   '\n'
    91                   'and also consider adding:\n'
   126                   'and also consider adding:\n'
    92                   '\n'
   127                   '\n'
    93                   '[extensions]\n'
   128                   '[extensions]\n'
    94                   'hgext.win32text =\n'
   129                   'hgext.win32text =\n'
    95                   '[encode]\n'
   130                   '[encode]\n'
    96                   '** = cleverencode:\n'
   131                   '** = %sencode:\n'
    97                   '[decode]\n'
   132                   '[decode]\n'
    98                   '** = cleverdecode:\n'))
   133                   '** = %sdecode:\n') % (crlf, crlf, filter, filter))
    99     return halt
   134     return halt
       
   135 
       
   136 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
       
   137     return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs)
       
   138 
       
   139 def forbidcr(ui, repo, hooktype, node, **kwargs):
       
   140     return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs)
   100 
   141 
   101 def reposetup(ui, repo):
   142 def reposetup(ui, repo):
   102     if not repo.local():
   143     if not repo.local():
   103         return
   144         return
   104     for name, fn in _filters.iteritems():
   145     for name, fn in _filters.iteritems():