##// END OF EJS Templates
eol: fix silly test-gendoc breakage by escaping control characters
Matt Mackall -
r14857:5b46c16e default
parent child Browse files
Show More
@@ -1,344 +1,344
1 1 """automatically manage newlines in repository files
2 2
3 3 This extension allows you to manage the type of line endings (CRLF or
4 4 LF) that are used in the repository and in the local working
5 5 directory. That way you can get CRLF line endings on Windows and LF on
6 6 Unix/Mac, thereby letting everybody use their OS native line endings.
7 7
8 8 The extension reads its configuration from a versioned ``.hgeol``
9 9 configuration file found in the root of the working copy. The
10 10 ``.hgeol`` file use the same syntax as all other Mercurial
11 11 configuration files. It uses two sections, ``[patterns]`` and
12 12 ``[repository]``.
13 13
14 14 The ``[patterns]`` section specifies how line endings should be
15 15 converted between the working copy and the repository. The format is
16 16 specified by a file pattern. The first match is used, so put more
17 17 specific patterns first. The available line endings are ``LF``,
18 18 ``CRLF``, and ``BIN``.
19 19
20 20 Files with the declared format of ``CRLF`` or ``LF`` are always
21 21 checked out and stored in the repository in that format and files
22 22 declared to be binary (``BIN``) are left unchanged. Additionally,
23 23 ``native`` is an alias for checking out in the platform's default line
24 24 ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on
25 25 Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's
26 26 default behaviour; it is only needed if you need to override a later,
27 27 more general pattern.
28 28
29 29 The optional ``[repository]`` section specifies the line endings to
30 30 use for files stored in the repository. It has a single setting,
31 31 ``native``, which determines the storage line endings for files
32 32 declared as ``native`` in the ``[patterns]`` section. It can be set to
33 33 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
34 34 that on Windows, files configured as ``native`` (``CRLF`` by default)
35 35 will be converted to ``LF`` when stored in the repository. Files
36 36 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
37 37 are always stored as-is in the repository.
38 38
39 39 Example versioned ``.hgeol`` file::
40 40
41 41 [patterns]
42 42 **.py = native
43 43 **.vcproj = CRLF
44 44 **.txt = native
45 45 Makefile = LF
46 46 **.jpg = BIN
47 47
48 48 [repository]
49 49 native = LF
50 50
51 51 .. note::
52 52 The rules will first apply when files are touched in the working
53 53 copy, e.g. by updating to null and back to tip to touch all files.
54 54
55 55 The extension uses an optional ``[eol]`` section read from both the
56 56 normal Mercurial configuration files and the ``.hgeol`` file, with the
57 57 latter overriding the former. You can use that section to control the
58 58 overall behavior. There are three settings:
59 59
60 60 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
61 61 ``CRLF`` to override the default interpretation of ``native`` for
62 62 checkout. This can be used with :hg:`archive` on Unix, say, to
63 63 generate an archive where files have line endings for Windows.
64 64
65 65 - ``eol.only-consistent`` (default True) can be set to False to make
66 66 the extension convert files with inconsistent EOLs. Inconsistent
67 67 means that there is both ``CRLF`` and ``LF`` present in the file.
68 68 Such files are normally not touched under the assumption that they
69 69 have mixed EOLs on purpose.
70 70
71 71 - ``eol.fix-trailing-newline`` (default False) can be set to True to
72 ensure that converted files end with a EOL character (either ``\n``
73 or ``\r\n`` as per the configured patterns).
72 ensure that converted files end with a EOL character (either ``\\n``
73 or ``\\r\\n`` as per the configured patterns).
74 74
75 75 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
76 76 like the deprecated win32text extension does. This means that you can
77 77 disable win32text and enable eol and your filters will still work. You
78 78 only need to these filters until you have prepared a ``.hgeol`` file.
79 79
80 80 The ``win32text.forbid*`` hooks provided by the win32text extension
81 81 have been unified into a single hook named ``eol.checkheadshook``. The
82 82 hook will lookup the expected line endings from the ``.hgeol`` file,
83 83 which means you must migrate to a ``.hgeol`` file first before using
84 84 the hook. ``eol.checkheadshook`` only checks heads, intermediate
85 85 invalid revisions will be pushed. To forbid them completely, use the
86 86 ``eol.checkallhook`` hook. These hooks are best used as
87 87 ``pretxnchangegroup`` hooks.
88 88
89 89 See :hg:`help patterns` for more information about the glob patterns
90 90 used.
91 91 """
92 92
93 93 from mercurial.i18n import _
94 94 from mercurial import util, config, extensions, match, error
95 95 import re, os
96 96
97 97 # Matches a lone LF, i.e., one that is not part of CRLF.
98 98 singlelf = re.compile('(^|[^\r])\n')
99 99 # Matches a single EOL which can either be a CRLF where repeated CR
100 100 # are removed or a LF. We do not care about old Machintosh files, so a
101 101 # stray CR is an error.
102 102 eolre = re.compile('\r*\n')
103 103
104 104
105 105 def inconsistenteol(data):
106 106 return '\r\n' in data and singlelf.search(data)
107 107
108 108 def tolf(s, params, ui, **kwargs):
109 109 """Filter to convert to LF EOLs."""
110 110 if util.binary(s):
111 111 return s
112 112 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
113 113 return s
114 114 if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
115 115 s = s + '\n'
116 116 return eolre.sub('\n', s)
117 117
118 118 def tocrlf(s, params, ui, **kwargs):
119 119 """Filter to convert to CRLF EOLs."""
120 120 if util.binary(s):
121 121 return s
122 122 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
123 123 return s
124 124 if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
125 125 s = s + '\n'
126 126 return eolre.sub('\r\n', s)
127 127
128 128 def isbinary(s, params):
129 129 """Filter to do nothing with the file."""
130 130 return s
131 131
132 132 filters = {
133 133 'to-lf': tolf,
134 134 'to-crlf': tocrlf,
135 135 'is-binary': isbinary,
136 136 # The following provide backwards compatibility with win32text
137 137 'cleverencode:': tolf,
138 138 'cleverdecode:': tocrlf
139 139 }
140 140
141 141 class eolfile(object):
142 142 def __init__(self, ui, root, data):
143 143 self._decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
144 144 self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
145 145
146 146 self.cfg = config.config()
147 147 # Our files should not be touched. The pattern must be
148 148 # inserted first override a '** = native' pattern.
149 149 self.cfg.set('patterns', '.hg*', 'BIN')
150 150 # We can then parse the user's patterns.
151 151 self.cfg.parse('.hgeol', data)
152 152
153 153 isrepolf = self.cfg.get('repository', 'native') != 'CRLF'
154 154 self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf'
155 155 iswdlf = ui.config('eol', 'native', os.linesep) in ('LF', '\n')
156 156 self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf'
157 157
158 158 include = []
159 159 exclude = []
160 160 for pattern, style in self.cfg.items('patterns'):
161 161 key = style.upper()
162 162 if key == 'BIN':
163 163 exclude.append(pattern)
164 164 else:
165 165 include.append(pattern)
166 166 # This will match the files for which we need to care
167 167 # about inconsistent newlines.
168 168 self.match = match.match(root, '', [], include, exclude)
169 169
170 170 def copytoui(self, ui):
171 171 for pattern, style in self.cfg.items('patterns'):
172 172 key = style.upper()
173 173 try:
174 174 ui.setconfig('decode', pattern, self._decode[key])
175 175 ui.setconfig('encode', pattern, self._encode[key])
176 176 except KeyError:
177 177 ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
178 178 % (style, self.cfg.source('patterns', pattern)))
179 179 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
180 180 for k, v in self.cfg.items('eol'):
181 181 ui.setconfig('eol', k, v)
182 182
183 183 def checkrev(self, repo, ctx, files):
184 184 failed = []
185 185 for f in (files or ctx.files()):
186 186 if f not in ctx:
187 187 continue
188 188 for pattern, style in self.cfg.items('patterns'):
189 189 if not match.match(repo.root, '', [pattern])(f):
190 190 continue
191 191 target = self._encode[style.upper()]
192 192 data = ctx[f].data()
193 193 if (target == "to-lf" and "\r\n" in data
194 194 or target == "to-crlf" and singlelf.search(data)):
195 195 failed.append((str(ctx), target, f))
196 196 break
197 197 return failed
198 198
199 199 def parseeol(ui, repo, nodes):
200 200 try:
201 201 for node in nodes:
202 202 try:
203 203 if node is None:
204 204 # Cannot use workingctx.data() since it would load
205 205 # and cache the filters before we configure them.
206 206 data = repo.wfile('.hgeol').read()
207 207 else:
208 208 data = repo[node]['.hgeol'].data()
209 209 return eolfile(ui, repo.root, data)
210 210 except (IOError, LookupError):
211 211 pass
212 212 except error.ParseError, inst:
213 213 ui.warn(_("warning: ignoring .hgeol file due to parse error "
214 214 "at %s: %s\n") % (inst.args[1], inst.args[0]))
215 215 return None
216 216
217 217 def _checkhook(ui, repo, node, headsonly):
218 218 # Get revisions to check and touched files at the same time
219 219 files = set()
220 220 revs = set()
221 221 for rev in xrange(repo[node].rev(), len(repo)):
222 222 revs.add(rev)
223 223 if headsonly:
224 224 ctx = repo[rev]
225 225 files.update(ctx.files())
226 226 for pctx in ctx.parents():
227 227 revs.discard(pctx.rev())
228 228 failed = []
229 229 for rev in revs:
230 230 ctx = repo[rev]
231 231 eol = parseeol(ui, repo, [ctx.node()])
232 232 if eol:
233 233 failed.extend(eol.checkrev(repo, ctx, files))
234 234
235 235 if failed:
236 236 eols = {'to-lf': 'CRLF', 'to-crlf': 'LF'}
237 237 msgs = []
238 238 for node, target, f in failed:
239 239 msgs.append(_(" %s in %s should not have %s line endings") %
240 240 (f, node, eols[target]))
241 241 raise util.Abort(_("end-of-line check failed:\n") + "\n".join(msgs))
242 242
243 243 def checkallhook(ui, repo, node, hooktype, **kwargs):
244 244 """verify that files have expected EOLs"""
245 245 _checkhook(ui, repo, node, False)
246 246
247 247 def checkheadshook(ui, repo, node, hooktype, **kwargs):
248 248 """verify that files have expected EOLs"""
249 249 _checkhook(ui, repo, node, True)
250 250
251 251 # "checkheadshook" used to be called "hook"
252 252 hook = checkheadshook
253 253
254 254 def preupdate(ui, repo, hooktype, parent1, parent2):
255 255 #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
256 256 repo.loadeol([parent1])
257 257 return False
258 258
259 259 def uisetup(ui):
260 260 ui.setconfig('hooks', 'preupdate.eol', preupdate)
261 261
262 262 def extsetup(ui):
263 263 try:
264 264 extensions.find('win32text')
265 265 ui.warn(_("the eol extension is incompatible with the "
266 266 "win32text extension\n"))
267 267 except KeyError:
268 268 pass
269 269
270 270
271 271 def reposetup(ui, repo):
272 272 uisetup(repo.ui)
273 273 #print "reposetup for", repo.root
274 274
275 275 if not repo.local():
276 276 return
277 277 for name, fn in filters.iteritems():
278 278 repo.adddatafilter(name, fn)
279 279
280 280 ui.setconfig('patch', 'eol', 'auto')
281 281
282 282 class eolrepo(repo.__class__):
283 283
284 284 def loadeol(self, nodes):
285 285 eol = parseeol(self.ui, self, nodes)
286 286 if eol is None:
287 287 return None
288 288 eol.copytoui(self.ui)
289 289 return eol.match
290 290
291 291 def _hgcleardirstate(self):
292 292 self._eolfile = self.loadeol([None, 'tip'])
293 293 if not self._eolfile:
294 294 self._eolfile = util.never
295 295 return
296 296
297 297 try:
298 298 cachemtime = os.path.getmtime(self.join("eol.cache"))
299 299 except OSError:
300 300 cachemtime = 0
301 301
302 302 try:
303 303 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
304 304 except OSError:
305 305 eolmtime = 0
306 306
307 307 if eolmtime > cachemtime:
308 308 ui.debug("eol: detected change in .hgeol\n")
309 309 wlock = None
310 310 try:
311 311 wlock = self.wlock()
312 312 for f in self.dirstate:
313 313 if self.dirstate[f] == 'n':
314 314 # all normal files need to be looked at
315 315 # again since the new .hgeol file might no
316 316 # longer match a file it matched before
317 317 self.dirstate.normallookup(f)
318 318 # Touch the cache to update mtime.
319 319 self.opener("eol.cache", "w").close()
320 320 wlock.release()
321 321 except error.LockUnavailable:
322 322 # If we cannot lock the repository and clear the
323 323 # dirstate, then a commit might not see all files
324 324 # as modified. But if we cannot lock the
325 325 # repository, then we can also not make a commit,
326 326 # so ignore the error.
327 327 pass
328 328
329 329 def commitctx(self, ctx, error=False):
330 330 for f in sorted(ctx.added() + ctx.modified()):
331 331 if not self._eolfile(f):
332 332 continue
333 333 data = ctx[f].data()
334 334 if util.binary(data):
335 335 # We should not abort here, since the user should
336 336 # be able to say "** = native" to automatically
337 337 # have all non-binary files taken care of.
338 338 continue
339 339 if inconsistenteol(data):
340 340 raise util.Abort(_("inconsistent newline style "
341 341 "in %s\n" % f))
342 342 return super(eolrepo, self).commitctx(ctx, error)
343 343 repo.__class__ = eolrepo
344 344 repo._hgcleardirstate()
General Comments 0
You need to be logged in to leave comments. Login now