hgext/win32text.py
changeset 43076 2372284d9457
parent 38823 e7aa113b14f7
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    43 
    43 
    44 from __future__ import absolute_import
    44 from __future__ import absolute_import
    45 
    45 
    46 import re
    46 import re
    47 from mercurial.i18n import _
    47 from mercurial.i18n import _
    48 from mercurial.node import (
    48 from mercurial.node import short
    49     short,
       
    50 )
       
    51 from mercurial import (
    49 from mercurial import (
    52     pycompat,
    50     pycompat,
    53     registrar,
    51     registrar,
    54 )
    52 )
    55 from mercurial.utils import (
    53 from mercurial.utils import stringutil
    56     stringutil,
       
    57 )
       
    58 
    54 
    59 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    55 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    60 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    56 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    61 # be specifying the version(s) of Mercurial they are tested with, or
    57 # be specifying the version(s) of Mercurial they are tested with, or
    62 # leave the attribute unspecified.
    58 # leave the attribute unspecified.
    63 testedwith = 'ships-with-hg-core'
    59 testedwith = 'ships-with-hg-core'
    64 
    60 
    65 configtable = {}
    61 configtable = {}
    66 configitem = registrar.configitem(configtable)
    62 configitem = registrar.configitem(configtable)
    67 
    63 
    68 configitem('win32text', 'warn',
    64 configitem(
    69     default=True,
    65     'win32text', 'warn', default=True,
    70 )
    66 )
    71 
    67 
    72 # regexp for single LF without CR preceding.
    68 # regexp for single LF without CR preceding.
    73 re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
    69 re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
    74 
    70 
    75 newlinestr = {'\r\n': 'CRLF', '\r': 'CR'}
    71 newlinestr = {'\r\n': 'CRLF', '\r': 'CR'}
    76 filterstr = {'\r\n': 'clever', '\r': 'mac'}
    72 filterstr = {'\r\n': 'clever', '\r': 'mac'}
       
    73 
    77 
    74 
    78 def checknewline(s, newline, ui=None, repo=None, filename=None):
    75 def checknewline(s, newline, ui=None, repo=None, filename=None):
    79     # warn if already has 'newline' in repository.
    76     # warn if already has 'newline' in repository.
    80     # it might cause unexpected eol conversion.
    77     # it might cause unexpected eol conversion.
    81     # see issue 302:
    78     # see issue 302:
    82     #   https://bz.mercurial-scm.org/302
    79     #   https://bz.mercurial-scm.org/302
    83     if newline in s and ui and filename and repo:
    80     if newline in s and ui and filename and repo:
    84         ui.warn(_('WARNING: %s already has %s line endings\n'
    81         ui.warn(
    85                   'and does not need EOL conversion by the win32text plugin.\n'
    82             _(
    86                   'Before your next commit, please reconsider your '
    83                 'WARNING: %s already has %s line endings\n'
    87                   'encode/decode settings in \nMercurial.ini or %s.\n') %
    84                 'and does not need EOL conversion by the win32text plugin.\n'
    88                 (filename, newlinestr[newline], repo.vfs.join('hgrc')))
    85                 'Before your next commit, please reconsider your '
       
    86                 'encode/decode settings in \nMercurial.ini or %s.\n'
       
    87             )
       
    88             % (filename, newlinestr[newline], repo.vfs.join('hgrc'))
       
    89         )
       
    90 
    89 
    91 
    90 def dumbdecode(s, cmd, **kwargs):
    92 def dumbdecode(s, cmd, **kwargs):
    91     checknewline(s, '\r\n', **kwargs)
    93     checknewline(s, '\r\n', **kwargs)
    92     # replace single LF to CRLF
    94     # replace single LF to CRLF
    93     return re_single_lf.sub('\\1\r\n', s)
    95     return re_single_lf.sub('\\1\r\n', s)
    94 
    96 
       
    97 
    95 def dumbencode(s, cmd):
    98 def dumbencode(s, cmd):
    96     return s.replace('\r\n', '\n')
    99     return s.replace('\r\n', '\n')
       
   100 
    97 
   101 
    98 def macdumbdecode(s, cmd, **kwargs):
   102 def macdumbdecode(s, cmd, **kwargs):
    99     checknewline(s, '\r', **kwargs)
   103     checknewline(s, '\r', **kwargs)
   100     return s.replace('\n', '\r')
   104     return s.replace('\n', '\r')
   101 
   105 
       
   106 
   102 def macdumbencode(s, cmd):
   107 def macdumbencode(s, cmd):
   103     return s.replace('\r', '\n')
   108     return s.replace('\r', '\n')
   104 
   109 
       
   110 
   105 def cleverdecode(s, cmd, **kwargs):
   111 def cleverdecode(s, cmd, **kwargs):
   106     if not stringutil.binary(s):
   112     if not stringutil.binary(s):
   107         return dumbdecode(s, cmd, **kwargs)
   113         return dumbdecode(s, cmd, **kwargs)
   108     return s
   114     return s
   109 
   115 
       
   116 
   110 def cleverencode(s, cmd):
   117 def cleverencode(s, cmd):
   111     if not stringutil.binary(s):
   118     if not stringutil.binary(s):
   112         return dumbencode(s, cmd)
   119         return dumbencode(s, cmd)
   113     return s
   120     return s
   114 
   121 
       
   122 
   115 def macdecode(s, cmd, **kwargs):
   123 def macdecode(s, cmd, **kwargs):
   116     if not stringutil.binary(s):
   124     if not stringutil.binary(s):
   117         return macdumbdecode(s, cmd, **kwargs)
   125         return macdumbdecode(s, cmd, **kwargs)
   118     return s
   126     return s
   119 
   127 
       
   128 
   120 def macencode(s, cmd):
   129 def macencode(s, cmd):
   121     if not stringutil.binary(s):
   130     if not stringutil.binary(s):
   122         return macdumbencode(s, cmd)
   131         return macdumbencode(s, cmd)
   123     return s
   132     return s
       
   133 
   124 
   134 
   125 _filters = {
   135 _filters = {
   126     'dumbdecode:': dumbdecode,
   136     'dumbdecode:': dumbdecode,
   127     'dumbencode:': dumbencode,
   137     'dumbencode:': dumbencode,
   128     'cleverdecode:': cleverdecode,
   138     'cleverdecode:': cleverdecode,
   129     'cleverencode:': cleverencode,
   139     'cleverencode:': cleverencode,
   130     'macdumbdecode:': macdumbdecode,
   140     'macdumbdecode:': macdumbdecode,
   131     'macdumbencode:': macdumbencode,
   141     'macdumbencode:': macdumbencode,
   132     'macdecode:': macdecode,
   142     'macdecode:': macdecode,
   133     'macencode:': macencode,
   143     'macencode:': macencode,
   134     }
   144 }
       
   145 
   135 
   146 
   136 def forbidnewline(ui, repo, hooktype, node, newline, **kwargs):
   147 def forbidnewline(ui, repo, hooktype, node, newline, **kwargs):
   137     halt = False
   148     halt = False
   138     seen = set()
   149     seen = set()
   139     # we try to walk changesets in reverse order from newest to
   150     # we try to walk changesets in reverse order from newest to
   140     # oldest, so that if we see a file multiple times, we take the
   151     # oldest, so that if we see a file multiple times, we take the
   141     # newest version as canonical. this prevents us from blocking a
   152     # newest version as canonical. this prevents us from blocking a
   142     # changegroup that contains an unacceptable commit followed later
   153     # changegroup that contains an unacceptable commit followed later
   143     # by a commit that fixes the problem.
   154     # by a commit that fixes the problem.
   144     tip = repo['tip']
   155     tip = repo['tip']
   145     for rev in pycompat.xrange(repo.changelog.tiprev(),
   156     for rev in pycompat.xrange(
   146                                repo[node].rev() - 1, -1):
   157         repo.changelog.tiprev(), repo[node].rev() - 1, -1
       
   158     ):
   147         c = repo[rev]
   159         c = repo[rev]
   148         for f in c.files():
   160         for f in c.files():
   149             if f in seen or f not in tip or f not in c:
   161             if f in seen or f not in tip or f not in c:
   150                 continue
   162                 continue
   151             seen.add(f)
   163             seen.add(f)
   152             data = c[f].data()
   164             data = c[f].data()
   153             if not stringutil.binary(data) and newline in data:
   165             if not stringutil.binary(data) and newline in data:
   154                 if not halt:
   166                 if not halt:
   155                     ui.warn(_('attempt to commit or push text file(s) '
   167                     ui.warn(
   156                               'using %s line endings\n') %
   168                         _(
   157                               newlinestr[newline])
   169                             'attempt to commit or push text file(s) '
       
   170                             'using %s line endings\n'
       
   171                         )
       
   172                         % newlinestr[newline]
       
   173                     )
   158                 ui.warn(_('in %s: %s\n') % (short(c.node()), f))
   174                 ui.warn(_('in %s: %s\n') % (short(c.node()), f))
   159                 halt = True
   175                 halt = True
   160     if halt and hooktype == 'pretxnchangegroup':
   176     if halt and hooktype == 'pretxnchangegroup':
   161         crlf = newlinestr[newline].lower()
   177         crlf = newlinestr[newline].lower()
   162         filter = filterstr[newline]
   178         filter = filterstr[newline]
   163         ui.warn(_('\nTo prevent this mistake in your local repository,\n'
   179         ui.warn(
   164                   'add to Mercurial.ini or .hg/hgrc:\n'
   180             _(
   165                   '\n'
   181                 '\nTo prevent this mistake in your local repository,\n'
   166                   '[hooks]\n'
   182                 'add to Mercurial.ini or .hg/hgrc:\n'
   167                   'pretxncommit.%s = python:hgext.win32text.forbid%s\n'
   183                 '\n'
   168                   '\n'
   184                 '[hooks]\n'
   169                   'and also consider adding:\n'
   185                 'pretxncommit.%s = python:hgext.win32text.forbid%s\n'
   170                   '\n'
   186                 '\n'
   171                   '[extensions]\n'
   187                 'and also consider adding:\n'
   172                   'win32text =\n'
   188                 '\n'
   173                   '[encode]\n'
   189                 '[extensions]\n'
   174                   '** = %sencode:\n'
   190                 'win32text =\n'
   175                   '[decode]\n'
   191                 '[encode]\n'
   176                   '** = %sdecode:\n') % (crlf, crlf, filter, filter))
   192                 '** = %sencode:\n'
       
   193                 '[decode]\n'
       
   194                 '** = %sdecode:\n'
       
   195             )
       
   196             % (crlf, crlf, filter, filter)
       
   197         )
   177     return halt
   198     return halt
       
   199 
   178 
   200 
   179 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
   201 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
   180     return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs)
   202     return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs)
   181 
   203 
       
   204 
   182 def forbidcr(ui, repo, hooktype, node, **kwargs):
   205 def forbidcr(ui, repo, hooktype, node, **kwargs):
   183     return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs)
   206     return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs)
       
   207 
   184 
   208 
   185 def reposetup(ui, repo):
   209 def reposetup(ui, repo):
   186     if not repo.local():
   210     if not repo.local():
   187         return
   211         return
   188     for name, fn in _filters.iteritems():
   212     for name, fn in _filters.iteritems():
   189         repo.adddatafilter(name, fn)
   213         repo.adddatafilter(name, fn)
   190 
   214 
       
   215 
   191 def extsetup(ui):
   216 def extsetup(ui):
   192     # deprecated config: win32text.warn
   217     # deprecated config: win32text.warn
   193     if ui.configbool('win32text', 'warn'):
   218     if ui.configbool('win32text', 'warn'):
   194         ui.warn(_("win32text is deprecated: "
   219         ui.warn(
   195                   "https://mercurial-scm.org/wiki/Win32TextExtension\n"))
   220             _(
       
   221                 "win32text is deprecated: "
       
   222                 "https://mercurial-scm.org/wiki/Win32TextExtension\n"
       
   223             )
       
   224         )