]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/python/hgext/win32text.py
hgwebfs: simplify retry loop construction
[plan9front.git] / sys / lib / python / hgext / win32text.py
1 # win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users
2 #
3 #  Copyright 2005, 2007-2009 Matt Mackall <mpm@selenic.com> and others
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
7
8 '''perform automatic newline conversion
9
10 To perform automatic newline conversion, use::
11
12   [extensions]
13   hgext.win32text =
14   [encode]
15   ** = cleverencode:
16   # or ** = macencode:
17
18   [decode]
19   ** = cleverdecode:
20   # or ** = macdecode:
21
22 If not doing conversion, to make sure you do not commit CRLF/CR by accident::
23
24   [hooks]
25   pretxncommit.crlf = python:hgext.win32text.forbidcrlf
26   # or pretxncommit.cr = python:hgext.win32text.forbidcr
27
28 To do the same check on a server to prevent CRLF/CR from being
29 pushed or pulled::
30
31   [hooks]
32   pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
33   # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr
34 '''
35
36 from mercurial.i18n import _
37 from mercurial.node import short
38 from mercurial import util
39 import re
40
41 # regexp for single LF without CR preceding.
42 re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
43
44 newlinestr = {'\r\n': 'CRLF', '\r': 'CR'}
45 filterstr = {'\r\n': 'clever', '\r': 'mac'}
46
47 def checknewline(s, newline, ui=None, repo=None, filename=None):
48     # warn if already has 'newline' in repository.
49     # it might cause unexpected eol conversion.
50     # see issue 302:
51     #   http://mercurial.selenic.com/bts/issue302
52     if newline in s and ui and filename and repo:
53         ui.warn(_('WARNING: %s already has %s line endings\n'
54                   'and does not need EOL conversion by the win32text plugin.\n'
55                   'Before your next commit, please reconsider your '
56                   'encode/decode settings in \nMercurial.ini or %s.\n') %
57                 (filename, newlinestr[newline], repo.join('hgrc')))
58
59 def dumbdecode(s, cmd, **kwargs):
60     checknewline(s, '\r\n', **kwargs)
61     # replace single LF to CRLF
62     return re_single_lf.sub('\\1\r\n', s)
63
64 def dumbencode(s, cmd):
65     return s.replace('\r\n', '\n')
66
67 def macdumbdecode(s, cmd, **kwargs):
68     checknewline(s, '\r', **kwargs)
69     return s.replace('\n', '\r')
70
71 def macdumbencode(s, cmd):
72     return s.replace('\r', '\n')
73
74 def cleverdecode(s, cmd, **kwargs):
75     if not util.binary(s):
76         return dumbdecode(s, cmd, **kwargs)
77     return s
78
79 def cleverencode(s, cmd):
80     if not util.binary(s):
81         return dumbencode(s, cmd)
82     return s
83
84 def macdecode(s, cmd, **kwargs):
85     if not util.binary(s):
86         return macdumbdecode(s, cmd, **kwargs)
87     return s
88
89 def macencode(s, cmd):
90     if not util.binary(s):
91         return macdumbencode(s, cmd)
92     return s
93
94 _filters = {
95     'dumbdecode:': dumbdecode,
96     'dumbencode:': dumbencode,
97     'cleverdecode:': cleverdecode,
98     'cleverencode:': cleverencode,
99     'macdumbdecode:': macdumbdecode,
100     'macdumbencode:': macdumbencode,
101     'macdecode:': macdecode,
102     'macencode:': macencode,
103     }
104
105 def forbidnewline(ui, repo, hooktype, node, newline, **kwargs):
106     halt = False
107     seen = set()
108     # we try to walk changesets in reverse order from newest to
109     # oldest, so that if we see a file multiple times, we take the
110     # newest version as canonical. this prevents us from blocking a
111     # changegroup that contains an unacceptable commit followed later
112     # by a commit that fixes the problem.
113     tip = repo['tip']
114     for rev in xrange(len(repo)-1, repo[node].rev()-1, -1):
115         c = repo[rev]
116         for f in c.files():
117             if f in seen or f not in tip or f not in c:
118                 continue
119             seen.add(f)
120             data = c[f].data()
121             if not util.binary(data) and newline in data:
122                 if not halt:
123                     ui.warn(_('Attempt to commit or push text file(s) '
124                               'using %s line endings\n') %
125                               newlinestr[newline])
126                 ui.warn(_('in %s: %s\n') % (short(c.node()), f))
127                 halt = True
128     if halt and hooktype == 'pretxnchangegroup':
129         crlf = newlinestr[newline].lower()
130         filter = filterstr[newline]
131         ui.warn(_('\nTo prevent this mistake in your local repository,\n'
132                   'add to Mercurial.ini or .hg/hgrc:\n'
133                   '\n'
134                   '[hooks]\n'
135                   'pretxncommit.%s = python:hgext.win32text.forbid%s\n'
136                   '\n'
137                   'and also consider adding:\n'
138                   '\n'
139                   '[extensions]\n'
140                   'hgext.win32text =\n'
141                   '[encode]\n'
142                   '** = %sencode:\n'
143                   '[decode]\n'
144                   '** = %sdecode:\n') % (crlf, crlf, filter, filter))
145     return halt
146
147 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
148     return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs)
149
150 def forbidcr(ui, repo, hooktype, node, **kwargs):
151     return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs)
152
153 def reposetup(ui, repo):
154     if not repo.local():
155         return
156     for name, fn in _filters.iteritems():
157         repo.adddatafilter(name, fn)
158