hgext/win32text.py
changeset 6481 e837dded56c7
parent 6247 7f4257b5cbfc
child 6483 0a803195bb29
equal deleted inserted replaced
6472:8c4cd80afd3e 6481:e837dded56c7
     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 import util
    30 from mercurial import util
    26 from mercurial.i18n import gettext as _
    31 from mercurial.i18n import gettext as _
    27 from mercurial.node import bin, short
    32 from mercurial.node import bin, short
    28 import re
    33 import re
    29 
    34 
    30 # regexp for single LF without CR preceding.
    35 # regexp for single LF without CR preceding.
    31 re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
    36 re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
    32 
    37 
    33 def dumbdecode(s, cmd, ui=None, repo=None, filename=None, **kwargs):
    38 newlinestr = {'\r\n': 'CRLF', '\r': 'CR'}
    34     # warn if already has CRLF in repository.
    39 filterstr = {'\r\n': 'clever', '\r': 'mac'}
       
    40 
       
    41 def checknewline(s, newline, ui=None, repo=None, filename=None):
       
    42     # warn if already has 'newline' in repository.
    35     # it might cause unexpected eol conversion.
    43     # it might cause unexpected eol conversion.
    36     # see issue 302:
    44     # see issue 302:
    37     #   http://www.selenic.com/mercurial/bts/issue302
    45     #   http://www.selenic.com/mercurial/bts/issue302
    38     if '\r\n' in s and ui and filename and repo:
    46     if newline in s and ui and filename and repo:
    39         ui.warn(_('WARNING: %s already has CRLF line endings\n'
    47         ui.warn(_('WARNING: %s already has %s line endings\n'
    40                   'and does not need EOL conversion by the win32text plugin.\n'
    48                   'and does not need EOL conversion by the win32text plugin.\n'
    41                   'Before your next commit, please reconsider your '
    49                   'Before your next commit, please reconsider your '
    42                   'encode/decode settings in \nMercurial.ini or %s.\n') %
    50                   'encode/decode settings in \nMercurial.ini or %s.\n') %
    43                 (filename, repo.join('hgrc')))
    51                 (filename, newlinestr[newline], repo.join('hgrc')))
       
    52 
       
    53 def dumbdecode(s, cmd, **kwargs):
       
    54     checknewline(s, '\r\n', **kwargs)
    44     # replace single LF to CRLF
    55     # replace single LF to CRLF
    45     return re_single_lf.sub('\\1\r\n', s)
    56     return re_single_lf.sub('\\1\r\n', s)
    46 
    57 
    47 def dumbencode(s, cmd):
    58 def dumbencode(s, cmd):
    48     return s.replace('\r\n', '\n')
    59     return s.replace('\r\n', '\n')
       
    60 
       
    61 def macdumbdecode(s, cmd, **kwargs):
       
    62     checknewline(s, '\r', **kwargs)
       
    63     return s.replace('\n', '\r')
       
    64 
       
    65 def macdumbencode(s, cmd):
       
    66     return s.replace('\r', '\n')
    49 
    67 
    50 def cleverdecode(s, cmd, **kwargs):
    68 def cleverdecode(s, cmd, **kwargs):
    51     if util.binary(s):
    69     if util.binary(s):
    52         return s
    70         return s
    53     return dumbdecode(s, cmd, **kwargs)
    71     return dumbdecode(s, cmd, **kwargs)
    55 def cleverencode(s, cmd):
    73 def cleverencode(s, cmd):
    56     if util.binary(s):
    74     if util.binary(s):
    57         return s
    75         return s
    58     return dumbencode(s, cmd)
    76     return dumbencode(s, cmd)
    59 
    77 
       
    78 def macdecode(s, cmd, **kwargs):
       
    79     if util.binary(s):
       
    80         return s
       
    81     return macdumbdecode(s, cmd, **kwargs)
       
    82 
       
    83 def macencode(s, cmd):
       
    84     if util.binary(s):
       
    85         return s
       
    86     return macdumbencode(s, cmd)
       
    87 
    60 _filters = {
    88 _filters = {
    61     'dumbdecode:': dumbdecode,
    89     'dumbdecode:': dumbdecode,
    62     'dumbencode:': dumbencode,
    90     'dumbencode:': dumbencode,
    63     'cleverdecode:': cleverdecode,
    91     'cleverdecode:': cleverdecode,
    64     'cleverencode:': cleverencode,
    92     'cleverencode:': cleverencode,
       
    93     'macdumbdecode:': macdumbdecode,
       
    94     'macdumbencode:': macdumbencode,
       
    95     'macdecode:': macdecode,
       
    96     'macencode:': macencode,
    65     }
    97     }
    66 
    98 
    67 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
    99 def forbidcrlforcr(ui, repo, hooktype, node, newline, **kwargs):
    68     halt = False
   100     halt = False
    69     for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()):
   101     for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()):
    70         c = repo.changectx(rev)
   102         c = repo.changectx(rev)
    71         for f in c.files():
   103         for f in c.files():
    72             if f not in c:
   104             if f not in c:
    73                 continue
   105                 continue
    74             data = c[f].data()
   106             data = c[f].data()
    75             if not util.binary(data) and '\r\n' in data:
   107             if not util.binary(data) and newline in data:
    76                 if not halt:
   108                 if not halt:
    77                     ui.warn(_('Attempt to commit or push text file(s) '
   109                     ui.warn(_('Attempt to commit or push text file(s) '
    78                               'using CRLF line endings\n'))
   110                               'using %s line endings\n') %
       
   111                               newlinestr[newline])
    79                 ui.warn(_('in %s: %s\n') % (short(c.node()), f))
   112                 ui.warn(_('in %s: %s\n') % (short(c.node()), f))
    80                 halt = True
   113                 halt = True
    81     if halt and hooktype == 'pretxnchangegroup':
   114     if halt and hooktype == 'pretxnchangegroup':
       
   115         crlf = newlinestr[newline].lower()
       
   116         filter = filterstr[newline]
    82         ui.warn(_('\nTo prevent this mistake in your local repository,\n'
   117         ui.warn(_('\nTo prevent this mistake in your local repository,\n'
    83                   'add to Mercurial.ini or .hg/hgrc:\n'
   118                   'add to Mercurial.ini or .hg/hgrc:\n'
    84                   '\n'
   119                   '\n'
    85                   '[hooks]\n'
   120                   '[hooks]\n'
    86                   'pretxncommit.crlf = python:hgext.win32text.forbidcrlf\n'
   121                   'pretxncommit.%s = python:hgext.win32text.forbid%s\n'
    87                   '\n'
   122                   '\n'
    88                   'and also consider adding:\n'
   123                   'and also consider adding:\n'
    89                   '\n'
   124                   '\n'
    90                   '[extensions]\n'
   125                   '[extensions]\n'
    91                   'hgext.win32text =\n'
   126                   'hgext.win32text =\n'
    92                   '[encode]\n'
   127                   '[encode]\n'
    93                   '** = cleverencode:\n'
   128                   '** = %sencode:\n'
    94                   '[decode]\n'
   129                   '[decode]\n'
    95                   '** = cleverdecode:\n'))
   130                   '** = %sdecode:\n') % (crlf, crlf, filter, filter))
    96     return halt
   131     return halt
       
   132 
       
   133 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
       
   134     return forbidcrlforcr(ui, repo, hooktype, node, '\r\n', **kwargs)
       
   135 
       
   136 def forbidcr(ui, repo, hooktype, node, **kwargs):
       
   137     return forbidcrlforcr(ui, repo, hooktype, node, '\r', **kwargs)
    97 
   138 
    98 def reposetup(ui, repo):
   139 def reposetup(ui, repo):
    99     if not repo.local():
   140     if not repo.local():
   100         return
   141         return
   101     for name, fn in _filters.iteritems():
   142     for name, fn in _filters.iteritems():