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