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(): |