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