##// END OF EJS Templates
win32text: make the hacky call cover more cases...
marmoute -
r50909:b7ddd9ae default
parent child Browse files
Show More
@@ -1,248 +1,248 b''
1 # win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users
1 # win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users
2 #
2 #
3 # Copyright 2005, 2007-2009 Olivia Mackall <olivia@selenic.com> and others
3 # Copyright 2005, 2007-2009 Olivia Mackall <olivia@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''perform automatic newline conversion (DEPRECATED)
8 '''perform automatic newline conversion (DEPRECATED)
9
9
10 Deprecation: The win32text extension requires each user to configure
10 Deprecation: The win32text extension requires each user to configure
11 the extension again and again for each clone since the configuration
11 the extension again and again for each clone since the configuration
12 is not copied when cloning.
12 is not copied when cloning.
13
13
14 We have therefore made the ``eol`` as an alternative. The ``eol``
14 We have therefore made the ``eol`` as an alternative. The ``eol``
15 uses a version controlled file for its configuration and each clone
15 uses a version controlled file for its configuration and each clone
16 will therefore use the right settings from the start.
16 will therefore use the right settings from the start.
17
17
18 To perform automatic newline conversion, use::
18 To perform automatic newline conversion, use::
19
19
20 [extensions]
20 [extensions]
21 win32text =
21 win32text =
22 [encode]
22 [encode]
23 ** = cleverencode:
23 ** = cleverencode:
24 # or ** = macencode:
24 # or ** = macencode:
25
25
26 [decode]
26 [decode]
27 ** = cleverdecode:
27 ** = cleverdecode:
28 # or ** = macdecode:
28 # or ** = macdecode:
29
29
30 If not doing conversion, to make sure you do not commit CRLF/CR by accident::
30 If not doing conversion, to make sure you do not commit CRLF/CR by accident::
31
31
32 [hooks]
32 [hooks]
33 pretxncommit.crlf = python:hgext.win32text.forbidcrlf
33 pretxncommit.crlf = python:hgext.win32text.forbidcrlf
34 # or pretxncommit.cr = python:hgext.win32text.forbidcr
34 # or pretxncommit.cr = python:hgext.win32text.forbidcr
35
35
36 To do the same check on a server to prevent CRLF/CR from being
36 To do the same check on a server to prevent CRLF/CR from being
37 pushed or pulled::
37 pushed or pulled::
38
38
39 [hooks]
39 [hooks]
40 pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
40 pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
41 # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr
41 # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr
42 '''
42 '''
43
43
44
44
45 import re
45 import re
46 from mercurial.i18n import _
46 from mercurial.i18n import _
47 from mercurial.node import short
47 from mercurial.node import short
48 from mercurial import (
48 from mercurial import (
49 cmdutil,
49 cmdutil,
50 extensions,
50 extensions,
51 registrar,
51 registrar,
52 )
52 )
53 from mercurial.utils import stringutil
53 from mercurial.utils import stringutil
54
54
55 # 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
56 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
56 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
57 # 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
58 # leave the attribute unspecified.
58 # leave the attribute unspecified.
59 testedwith = b'ships-with-hg-core'
59 testedwith = b'ships-with-hg-core'
60
60
61 configtable = {}
61 configtable = {}
62 configitem = registrar.configitem(configtable)
62 configitem = registrar.configitem(configtable)
63
63
64 configitem(
64 configitem(
65 b'win32text',
65 b'win32text',
66 b'warn',
66 b'warn',
67 default=True,
67 default=True,
68 )
68 )
69
69
70 # regexp for single LF without CR preceding.
70 # regexp for single LF without CR preceding.
71 re_single_lf = re.compile(b'(^|[^\r])\n', re.MULTILINE)
71 re_single_lf = re.compile(b'(^|[^\r])\n', re.MULTILINE)
72
72
73 newlinestr = {b'\r\n': b'CRLF', b'\r': b'CR'}
73 newlinestr = {b'\r\n': b'CRLF', b'\r': b'CR'}
74 filterstr = {b'\r\n': b'clever', b'\r': b'mac'}
74 filterstr = {b'\r\n': b'clever', b'\r': b'mac'}
75
75
76
76
77 def checknewline(s, newline, ui=None, repo=None, filename=None):
77 def checknewline(s, newline, ui=None, repo=None, filename=None):
78 # warn if already has 'newline' in repository.
78 # warn if already has 'newline' in repository.
79 # it might cause unexpected eol conversion.
79 # it might cause unexpected eol conversion.
80 # see issue 302:
80 # see issue 302:
81 # https://bz.mercurial-scm.org/302
81 # https://bz.mercurial-scm.org/302
82 if newline in s and ui and filename and repo:
82 if newline in s and ui and filename and repo:
83 ui.warn(
83 ui.warn(
84 _(
84 _(
85 b'WARNING: %s already has %s line endings\n'
85 b'WARNING: %s already has %s line endings\n'
86 b'and does not need EOL conversion by the win32text plugin.\n'
86 b'and does not need EOL conversion by the win32text plugin.\n'
87 b'Before your next commit, please reconsider your '
87 b'Before your next commit, please reconsider your '
88 b'encode/decode settings in \nMercurial.ini or %s.\n'
88 b'encode/decode settings in \nMercurial.ini or %s.\n'
89 )
89 )
90 % (filename, newlinestr[newline], repo.vfs.join(b'hgrc'))
90 % (filename, newlinestr[newline], repo.vfs.join(b'hgrc'))
91 )
91 )
92
92
93
93
94 def dumbdecode(s, cmd, **kwargs):
94 def dumbdecode(s, cmd, **kwargs):
95 checknewline(s, b'\r\n', **kwargs)
95 checknewline(s, b'\r\n', **kwargs)
96 # replace single LF to CRLF
96 # replace single LF to CRLF
97 return re_single_lf.sub(b'\\1\r\n', s)
97 return re_single_lf.sub(b'\\1\r\n', s)
98
98
99
99
100 def dumbencode(s, cmd):
100 def dumbencode(s, cmd):
101 return s.replace(b'\r\n', b'\n')
101 return s.replace(b'\r\n', b'\n')
102
102
103
103
104 def macdumbdecode(s, cmd, **kwargs):
104 def macdumbdecode(s, cmd, **kwargs):
105 checknewline(s, b'\r', **kwargs)
105 checknewline(s, b'\r', **kwargs)
106 return s.replace(b'\n', b'\r')
106 return s.replace(b'\n', b'\r')
107
107
108
108
109 def macdumbencode(s, cmd):
109 def macdumbencode(s, cmd):
110 return s.replace(b'\r', b'\n')
110 return s.replace(b'\r', b'\n')
111
111
112
112
113 def cleverdecode(s, cmd, **kwargs):
113 def cleverdecode(s, cmd, **kwargs):
114 if not stringutil.binary(s):
114 if not stringutil.binary(s):
115 return dumbdecode(s, cmd, **kwargs)
115 return dumbdecode(s, cmd, **kwargs)
116 return s
116 return s
117
117
118
118
119 def cleverencode(s, cmd):
119 def cleverencode(s, cmd):
120 if not stringutil.binary(s):
120 if not stringutil.binary(s):
121 return dumbencode(s, cmd)
121 return dumbencode(s, cmd)
122 return s
122 return s
123
123
124
124
125 def macdecode(s, cmd, **kwargs):
125 def macdecode(s, cmd, **kwargs):
126 if not stringutil.binary(s):
126 if not stringutil.binary(s):
127 return macdumbdecode(s, cmd, **kwargs)
127 return macdumbdecode(s, cmd, **kwargs)
128 return s
128 return s
129
129
130
130
131 def macencode(s, cmd):
131 def macencode(s, cmd):
132 if not stringutil.binary(s):
132 if not stringutil.binary(s):
133 return macdumbencode(s, cmd)
133 return macdumbencode(s, cmd)
134 return s
134 return s
135
135
136
136
137 _filters = {
137 _filters = {
138 b'dumbdecode:': dumbdecode,
138 b'dumbdecode:': dumbdecode,
139 b'dumbencode:': dumbencode,
139 b'dumbencode:': dumbencode,
140 b'cleverdecode:': cleverdecode,
140 b'cleverdecode:': cleverdecode,
141 b'cleverencode:': cleverencode,
141 b'cleverencode:': cleverencode,
142 b'macdumbdecode:': macdumbdecode,
142 b'macdumbdecode:': macdumbdecode,
143 b'macdumbencode:': macdumbencode,
143 b'macdumbencode:': macdumbencode,
144 b'macdecode:': macdecode,
144 b'macdecode:': macdecode,
145 b'macencode:': macencode,
145 b'macencode:': macencode,
146 }
146 }
147
147
148
148
149 def forbidnewline(ui, repo, hooktype, node, newline, **kwargs):
149 def forbidnewline(ui, repo, hooktype, node, newline, **kwargs):
150 halt = False
150 halt = False
151 seen = set()
151 seen = set()
152 # we try to walk changesets in reverse order from newest to
152 # we try to walk changesets in reverse order from newest to
153 # oldest, so that if we see a file multiple times, we take the
153 # oldest, so that if we see a file multiple times, we take the
154 # newest version as canonical. this prevents us from blocking a
154 # newest version as canonical. this prevents us from blocking a
155 # changegroup that contains an unacceptable commit followed later
155 # changegroup that contains an unacceptable commit followed later
156 # by a commit that fixes the problem.
156 # by a commit that fixes the problem.
157 tip = repo[b'tip']
157 tip = repo[b'tip']
158 for rev in range(repo.changelog.tiprev(), repo[node].rev() - 1, -1):
158 for rev in range(repo.changelog.tiprev(), repo[node].rev() - 1, -1):
159 c = repo[rev]
159 c = repo[rev]
160 for f in c.files():
160 for f in c.files():
161 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:
162 continue
162 continue
163 seen.add(f)
163 seen.add(f)
164 data = c[f].data()
164 data = c[f].data()
165 if not stringutil.binary(data) and newline in data:
165 if not stringutil.binary(data) and newline in data:
166 if not halt:
166 if not halt:
167 ui.warn(
167 ui.warn(
168 _(
168 _(
169 b'attempt to commit or push text file(s) '
169 b'attempt to commit or push text file(s) '
170 b'using %s line endings\n'
170 b'using %s line endings\n'
171 )
171 )
172 % newlinestr[newline]
172 % newlinestr[newline]
173 )
173 )
174 ui.warn(_(b'in %s: %s\n') % (short(c.node()), f))
174 ui.warn(_(b'in %s: %s\n') % (short(c.node()), f))
175 halt = True
175 halt = True
176 if halt and hooktype == b'pretxnchangegroup':
176 if halt and hooktype == b'pretxnchangegroup':
177 crlf = newlinestr[newline].lower()
177 crlf = newlinestr[newline].lower()
178 filter = filterstr[newline]
178 filter = filterstr[newline]
179 ui.warn(
179 ui.warn(
180 _(
180 _(
181 b'\nTo prevent this mistake in your local repository,\n'
181 b'\nTo prevent this mistake in your local repository,\n'
182 b'add to Mercurial.ini or .hg/hgrc:\n'
182 b'add to Mercurial.ini or .hg/hgrc:\n'
183 b'\n'
183 b'\n'
184 b'[hooks]\n'
184 b'[hooks]\n'
185 b'pretxncommit.%s = python:hgext.win32text.forbid%s\n'
185 b'pretxncommit.%s = python:hgext.win32text.forbid%s\n'
186 b'\n'
186 b'\n'
187 b'and also consider adding:\n'
187 b'and also consider adding:\n'
188 b'\n'
188 b'\n'
189 b'[extensions]\n'
189 b'[extensions]\n'
190 b'win32text =\n'
190 b'win32text =\n'
191 b'[encode]\n'
191 b'[encode]\n'
192 b'** = %sencode:\n'
192 b'** = %sencode:\n'
193 b'[decode]\n'
193 b'[decode]\n'
194 b'** = %sdecode:\n'
194 b'** = %sdecode:\n'
195 )
195 )
196 % (crlf, crlf, filter, filter)
196 % (crlf, crlf, filter, filter)
197 )
197 )
198 return halt
198 return halt
199
199
200
200
201 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
201 def forbidcrlf(ui, repo, hooktype, node, **kwargs):
202 return forbidnewline(ui, repo, hooktype, node, b'\r\n', **kwargs)
202 return forbidnewline(ui, repo, hooktype, node, b'\r\n', **kwargs)
203
203
204
204
205 def forbidcr(ui, repo, hooktype, node, **kwargs):
205 def forbidcr(ui, repo, hooktype, node, **kwargs):
206 return forbidnewline(ui, repo, hooktype, node, b'\r', **kwargs)
206 return forbidnewline(ui, repo, hooktype, node, b'\r', **kwargs)
207
207
208
208
209 def reposetup(ui, repo):
209 def reposetup(ui, repo):
210 if not repo.local():
210 if not repo.local():
211 return
211 return
212 for name, fn in _filters.items():
212 for name, fn in _filters.items():
213 repo.adddatafilter(name, fn)
213 repo.adddatafilter(name, fn)
214
214
215
215
216 def wrap_revert(orig, repo, ctx, names, uipathfn, actions, *args, **kwargs):
216 def wrap_revert(orig, repo, ctx, names, uipathfn, actions, *args, **kwargs):
217 # reset dirstate cache for file we touch
217 # reset dirstate cache for file we touch
218 ds = repo.dirstate
218 ds = repo.dirstate
219 for filename in actions[b'revert'][0]:
219 for filename in actions[b'revert'][0]:
220 entry = ds.get_entry(filename)
220 entry = ds.get_entry(filename)
221 if entry is not None:
221 if entry is not None:
222 if entry.p1_tracked:
222 if entry.p1_tracked:
223 # If we revert the file, it is possibly dirty. However,
223 # If we revert the file, it is possibly dirty. However,
224 # this extension meddle with the file content and therefore
224 # this extension meddle with the file content and therefore
225 # its size. As a result, we cannot simply call
225 # its size. As a result, we cannot simply call
226 # `dirstate.set_possibly_dirty` as it will not affet the
226 # `dirstate.set_possibly_dirty` as it will not affet the
227 # expected size of the file.
227 # expected size of the file.
228 #
228 #
229 # At least, now, the quirk is properly documented.
229 # At least, now, the quirk is properly documented.
230 ds.hacky_extension_update_file(
230 ds.hacky_extension_update_file(
231 filename,
231 filename,
232 entry.tracked,
232 entry.tracked,
233 p1_tracked=True,
233 p1_tracked=entry.p1_tracked,
234 p2_info=entry.p2_info,
234 p2_info=entry.p2_info,
235 )
235 )
236 return orig(repo, ctx, names, uipathfn, actions, *args, **kwargs)
236 return orig(repo, ctx, names, uipathfn, actions, *args, **kwargs)
237
237
238
238
239 def extsetup(ui):
239 def extsetup(ui):
240 # deprecated config: win32text.warn
240 # deprecated config: win32text.warn
241 if ui.configbool(b'win32text', b'warn'):
241 if ui.configbool(b'win32text', b'warn'):
242 ui.warn(
242 ui.warn(
243 _(
243 _(
244 b"win32text is deprecated: "
244 b"win32text is deprecated: "
245 b"https://mercurial-scm.org/wiki/Win32TextExtension\n"
245 b"https://mercurial-scm.org/wiki/Win32TextExtension\n"
246 )
246 )
247 )
247 )
248 extensions.wrapfunction(cmdutil, '_performrevert', wrap_revert)
248 extensions.wrapfunction(cmdutil, '_performrevert', wrap_revert)
General Comments 0
You need to be logged in to leave comments. Login now