##// END OF EJS Templates
eol: cache needs update, also if it has same timestamp as the source...
Mads Kiilerich -
r43475:fd8c3f59 default
parent child Browse files
Show More
@@ -1,468 +1,468
1 """automatically manage newlines in repository files
1 """automatically manage newlines in repository files
2
2
3 This extension allows you to manage the type of line endings (CRLF or
3 This extension allows you to manage the type of line endings (CRLF or
4 LF) that are used in the repository and in the local working
4 LF) that are used in the repository and in the local working
5 directory. That way you can get CRLF line endings on Windows and LF on
5 directory. That way you can get CRLF line endings on Windows and LF on
6 Unix/Mac, thereby letting everybody use their OS native line endings.
6 Unix/Mac, thereby letting everybody use their OS native line endings.
7
7
8 The extension reads its configuration from a versioned ``.hgeol``
8 The extension reads its configuration from a versioned ``.hgeol``
9 configuration file found in the root of the working directory. The
9 configuration file found in the root of the working directory. The
10 ``.hgeol`` file use the same syntax as all other Mercurial
10 ``.hgeol`` file use the same syntax as all other Mercurial
11 configuration files. It uses two sections, ``[patterns]`` and
11 configuration files. It uses two sections, ``[patterns]`` and
12 ``[repository]``.
12 ``[repository]``.
13
13
14 The ``[patterns]`` section specifies how line endings should be
14 The ``[patterns]`` section specifies how line endings should be
15 converted between the working directory and the repository. The format is
15 converted between the working directory and the repository. The format is
16 specified by a file pattern. The first match is used, so put more
16 specified by a file pattern. The first match is used, so put more
17 specific patterns first. The available line endings are ``LF``,
17 specific patterns first. The available line endings are ``LF``,
18 ``CRLF``, and ``BIN``.
18 ``CRLF``, and ``BIN``.
19
19
20 Files with the declared format of ``CRLF`` or ``LF`` are always
20 Files with the declared format of ``CRLF`` or ``LF`` are always
21 checked out and stored in the repository in that format and files
21 checked out and stored in the repository in that format and files
22 declared to be binary (``BIN``) are left unchanged. Additionally,
22 declared to be binary (``BIN``) are left unchanged. Additionally,
23 ``native`` is an alias for checking out in the platform's default line
23 ``native`` is an alias for checking out in the platform's default line
24 ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on
24 ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on
25 Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's
25 Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's
26 default behavior; it is only needed if you need to override a later,
26 default behavior; it is only needed if you need to override a later,
27 more general pattern.
27 more general pattern.
28
28
29 The optional ``[repository]`` section specifies the line endings to
29 The optional ``[repository]`` section specifies the line endings to
30 use for files stored in the repository. It has a single setting,
30 use for files stored in the repository. It has a single setting,
31 ``native``, which determines the storage line endings for files
31 ``native``, which determines the storage line endings for files
32 declared as ``native`` in the ``[patterns]`` section. It can be set to
32 declared as ``native`` in the ``[patterns]`` section. It can be set to
33 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
33 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
34 that on Windows, files configured as ``native`` (``CRLF`` by default)
34 that on Windows, files configured as ``native`` (``CRLF`` by default)
35 will be converted to ``LF`` when stored in the repository. Files
35 will be converted to ``LF`` when stored in the repository. Files
36 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
36 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
37 are always stored as-is in the repository.
37 are always stored as-is in the repository.
38
38
39 Example versioned ``.hgeol`` file::
39 Example versioned ``.hgeol`` file::
40
40
41 [patterns]
41 [patterns]
42 **.py = native
42 **.py = native
43 **.vcproj = CRLF
43 **.vcproj = CRLF
44 **.txt = native
44 **.txt = native
45 Makefile = LF
45 Makefile = LF
46 **.jpg = BIN
46 **.jpg = BIN
47
47
48 [repository]
48 [repository]
49 native = LF
49 native = LF
50
50
51 .. note::
51 .. note::
52
52
53 The rules will first apply when files are touched in the working
53 The rules will first apply when files are touched in the working
54 directory, e.g. by updating to null and back to tip to touch all files.
54 directory, e.g. by updating to null and back to tip to touch all files.
55
55
56 The extension uses an optional ``[eol]`` section read from both the
56 The extension uses an optional ``[eol]`` section read from both the
57 normal Mercurial configuration files and the ``.hgeol`` file, with the
57 normal Mercurial configuration files and the ``.hgeol`` file, with the
58 latter overriding the former. You can use that section to control the
58 latter overriding the former. You can use that section to control the
59 overall behavior. There are three settings:
59 overall behavior. There are three settings:
60
60
61 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
61 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
62 ``CRLF`` to override the default interpretation of ``native`` for
62 ``CRLF`` to override the default interpretation of ``native`` for
63 checkout. This can be used with :hg:`archive` on Unix, say, to
63 checkout. This can be used with :hg:`archive` on Unix, say, to
64 generate an archive where files have line endings for Windows.
64 generate an archive where files have line endings for Windows.
65
65
66 - ``eol.only-consistent`` (default True) can be set to False to make
66 - ``eol.only-consistent`` (default True) can be set to False to make
67 the extension convert files with inconsistent EOLs. Inconsistent
67 the extension convert files with inconsistent EOLs. Inconsistent
68 means that there is both ``CRLF`` and ``LF`` present in the file.
68 means that there is both ``CRLF`` and ``LF`` present in the file.
69 Such files are normally not touched under the assumption that they
69 Such files are normally not touched under the assumption that they
70 have mixed EOLs on purpose.
70 have mixed EOLs on purpose.
71
71
72 - ``eol.fix-trailing-newline`` (default False) can be set to True to
72 - ``eol.fix-trailing-newline`` (default False) can be set to True to
73 ensure that converted files end with a EOL character (either ``\\n``
73 ensure that converted files end with a EOL character (either ``\\n``
74 or ``\\r\\n`` as per the configured patterns).
74 or ``\\r\\n`` as per the configured patterns).
75
75
76 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
76 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
77 like the deprecated win32text extension does. This means that you can
77 like the deprecated win32text extension does. This means that you can
78 disable win32text and enable eol and your filters will still work. You
78 disable win32text and enable eol and your filters will still work. You
79 only need to these filters until you have prepared a ``.hgeol`` file.
79 only need to these filters until you have prepared a ``.hgeol`` file.
80
80
81 The ``win32text.forbid*`` hooks provided by the win32text extension
81 The ``win32text.forbid*`` hooks provided by the win32text extension
82 have been unified into a single hook named ``eol.checkheadshook``. The
82 have been unified into a single hook named ``eol.checkheadshook``. The
83 hook will lookup the expected line endings from the ``.hgeol`` file,
83 hook will lookup the expected line endings from the ``.hgeol`` file,
84 which means you must migrate to a ``.hgeol`` file first before using
84 which means you must migrate to a ``.hgeol`` file first before using
85 the hook. ``eol.checkheadshook`` only checks heads, intermediate
85 the hook. ``eol.checkheadshook`` only checks heads, intermediate
86 invalid revisions will be pushed. To forbid them completely, use the
86 invalid revisions will be pushed. To forbid them completely, use the
87 ``eol.checkallhook`` hook. These hooks are best used as
87 ``eol.checkallhook`` hook. These hooks are best used as
88 ``pretxnchangegroup`` hooks.
88 ``pretxnchangegroup`` hooks.
89
89
90 See :hg:`help patterns` for more information about the glob patterns
90 See :hg:`help patterns` for more information about the glob patterns
91 used.
91 used.
92 """
92 """
93
93
94 from __future__ import absolute_import
94 from __future__ import absolute_import
95
95
96 import os
96 import os
97 import re
97 import re
98 from mercurial.i18n import _
98 from mercurial.i18n import _
99 from mercurial import (
99 from mercurial import (
100 config,
100 config,
101 error as errormod,
101 error as errormod,
102 extensions,
102 extensions,
103 match,
103 match,
104 pycompat,
104 pycompat,
105 registrar,
105 registrar,
106 scmutil,
106 scmutil,
107 util,
107 util,
108 )
108 )
109 from mercurial.utils import stringutil
109 from mercurial.utils import stringutil
110
110
111 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
111 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
112 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
112 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
113 # be specifying the version(s) of Mercurial they are tested with, or
113 # be specifying the version(s) of Mercurial they are tested with, or
114 # leave the attribute unspecified.
114 # leave the attribute unspecified.
115 testedwith = b'ships-with-hg-core'
115 testedwith = b'ships-with-hg-core'
116
116
117 configtable = {}
117 configtable = {}
118 configitem = registrar.configitem(configtable)
118 configitem = registrar.configitem(configtable)
119
119
120 configitem(
120 configitem(
121 b'eol', b'fix-trailing-newline', default=False,
121 b'eol', b'fix-trailing-newline', default=False,
122 )
122 )
123 configitem(
123 configitem(
124 b'eol', b'native', default=pycompat.oslinesep,
124 b'eol', b'native', default=pycompat.oslinesep,
125 )
125 )
126 configitem(
126 configitem(
127 b'eol', b'only-consistent', default=True,
127 b'eol', b'only-consistent', default=True,
128 )
128 )
129
129
130 # Matches a lone LF, i.e., one that is not part of CRLF.
130 # Matches a lone LF, i.e., one that is not part of CRLF.
131 singlelf = re.compile(b'(^|[^\r])\n')
131 singlelf = re.compile(b'(^|[^\r])\n')
132
132
133
133
134 def inconsistenteol(data):
134 def inconsistenteol(data):
135 return b'\r\n' in data and singlelf.search(data)
135 return b'\r\n' in data and singlelf.search(data)
136
136
137
137
138 def tolf(s, params, ui, **kwargs):
138 def tolf(s, params, ui, **kwargs):
139 """Filter to convert to LF EOLs."""
139 """Filter to convert to LF EOLs."""
140 if stringutil.binary(s):
140 if stringutil.binary(s):
141 return s
141 return s
142 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
142 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
143 return s
143 return s
144 if (
144 if (
145 ui.configbool(b'eol', b'fix-trailing-newline')
145 ui.configbool(b'eol', b'fix-trailing-newline')
146 and s
146 and s
147 and not s.endswith(b'\n')
147 and not s.endswith(b'\n')
148 ):
148 ):
149 s = s + b'\n'
149 s = s + b'\n'
150 return util.tolf(s)
150 return util.tolf(s)
151
151
152
152
153 def tocrlf(s, params, ui, **kwargs):
153 def tocrlf(s, params, ui, **kwargs):
154 """Filter to convert to CRLF EOLs."""
154 """Filter to convert to CRLF EOLs."""
155 if stringutil.binary(s):
155 if stringutil.binary(s):
156 return s
156 return s
157 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
157 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
158 return s
158 return s
159 if (
159 if (
160 ui.configbool(b'eol', b'fix-trailing-newline')
160 ui.configbool(b'eol', b'fix-trailing-newline')
161 and s
161 and s
162 and not s.endswith(b'\n')
162 and not s.endswith(b'\n')
163 ):
163 ):
164 s = s + b'\n'
164 s = s + b'\n'
165 return util.tocrlf(s)
165 return util.tocrlf(s)
166
166
167
167
168 def isbinary(s, params, ui, **kwargs):
168 def isbinary(s, params, ui, **kwargs):
169 """Filter to do nothing with the file."""
169 """Filter to do nothing with the file."""
170 return s
170 return s
171
171
172
172
173 filters = {
173 filters = {
174 b'to-lf': tolf,
174 b'to-lf': tolf,
175 b'to-crlf': tocrlf,
175 b'to-crlf': tocrlf,
176 b'is-binary': isbinary,
176 b'is-binary': isbinary,
177 # The following provide backwards compatibility with win32text
177 # The following provide backwards compatibility with win32text
178 b'cleverencode:': tolf,
178 b'cleverencode:': tolf,
179 b'cleverdecode:': tocrlf,
179 b'cleverdecode:': tocrlf,
180 }
180 }
181
181
182
182
183 class eolfile(object):
183 class eolfile(object):
184 def __init__(self, ui, root, data):
184 def __init__(self, ui, root, data):
185 self._decode = {
185 self._decode = {
186 b'LF': b'to-lf',
186 b'LF': b'to-lf',
187 b'CRLF': b'to-crlf',
187 b'CRLF': b'to-crlf',
188 b'BIN': b'is-binary',
188 b'BIN': b'is-binary',
189 }
189 }
190 self._encode = {
190 self._encode = {
191 b'LF': b'to-lf',
191 b'LF': b'to-lf',
192 b'CRLF': b'to-crlf',
192 b'CRLF': b'to-crlf',
193 b'BIN': b'is-binary',
193 b'BIN': b'is-binary',
194 }
194 }
195
195
196 self.cfg = config.config()
196 self.cfg = config.config()
197 # Our files should not be touched. The pattern must be
197 # Our files should not be touched. The pattern must be
198 # inserted first override a '** = native' pattern.
198 # inserted first override a '** = native' pattern.
199 self.cfg.set(b'patterns', b'.hg*', b'BIN', b'eol')
199 self.cfg.set(b'patterns', b'.hg*', b'BIN', b'eol')
200 # We can then parse the user's patterns.
200 # We can then parse the user's patterns.
201 self.cfg.parse(b'.hgeol', data)
201 self.cfg.parse(b'.hgeol', data)
202
202
203 isrepolf = self.cfg.get(b'repository', b'native') != b'CRLF'
203 isrepolf = self.cfg.get(b'repository', b'native') != b'CRLF'
204 self._encode[b'NATIVE'] = isrepolf and b'to-lf' or b'to-crlf'
204 self._encode[b'NATIVE'] = isrepolf and b'to-lf' or b'to-crlf'
205 iswdlf = ui.config(b'eol', b'native') in (b'LF', b'\n')
205 iswdlf = ui.config(b'eol', b'native') in (b'LF', b'\n')
206 self._decode[b'NATIVE'] = iswdlf and b'to-lf' or b'to-crlf'
206 self._decode[b'NATIVE'] = iswdlf and b'to-lf' or b'to-crlf'
207
207
208 include = []
208 include = []
209 exclude = []
209 exclude = []
210 self.patterns = []
210 self.patterns = []
211 for pattern, style in self.cfg.items(b'patterns'):
211 for pattern, style in self.cfg.items(b'patterns'):
212 key = style.upper()
212 key = style.upper()
213 if key == b'BIN':
213 if key == b'BIN':
214 exclude.append(pattern)
214 exclude.append(pattern)
215 else:
215 else:
216 include.append(pattern)
216 include.append(pattern)
217 m = match.match(root, b'', [pattern])
217 m = match.match(root, b'', [pattern])
218 self.patterns.append((pattern, key, m))
218 self.patterns.append((pattern, key, m))
219 # This will match the files for which we need to care
219 # This will match the files for which we need to care
220 # about inconsistent newlines.
220 # about inconsistent newlines.
221 self.match = match.match(root, b'', [], include, exclude)
221 self.match = match.match(root, b'', [], include, exclude)
222
222
223 def copytoui(self, ui):
223 def copytoui(self, ui):
224 for pattern, key, m in self.patterns:
224 for pattern, key, m in self.patterns:
225 try:
225 try:
226 ui.setconfig(b'decode', pattern, self._decode[key], b'eol')
226 ui.setconfig(b'decode', pattern, self._decode[key], b'eol')
227 ui.setconfig(b'encode', pattern, self._encode[key], b'eol')
227 ui.setconfig(b'encode', pattern, self._encode[key], b'eol')
228 except KeyError:
228 except KeyError:
229 ui.warn(
229 ui.warn(
230 _(b"ignoring unknown EOL style '%s' from %s\n")
230 _(b"ignoring unknown EOL style '%s' from %s\n")
231 % (key, self.cfg.source(b'patterns', pattern))
231 % (key, self.cfg.source(b'patterns', pattern))
232 )
232 )
233 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
233 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
234 for k, v in self.cfg.items(b'eol'):
234 for k, v in self.cfg.items(b'eol'):
235 ui.setconfig(b'eol', k, v, b'eol')
235 ui.setconfig(b'eol', k, v, b'eol')
236
236
237 def checkrev(self, repo, ctx, files):
237 def checkrev(self, repo, ctx, files):
238 failed = []
238 failed = []
239 for f in files or ctx.files():
239 for f in files or ctx.files():
240 if f not in ctx:
240 if f not in ctx:
241 continue
241 continue
242 for pattern, key, m in self.patterns:
242 for pattern, key, m in self.patterns:
243 if not m(f):
243 if not m(f):
244 continue
244 continue
245 target = self._encode[key]
245 target = self._encode[key]
246 data = ctx[f].data()
246 data = ctx[f].data()
247 if (
247 if (
248 target == b"to-lf"
248 target == b"to-lf"
249 and b"\r\n" in data
249 and b"\r\n" in data
250 or target == b"to-crlf"
250 or target == b"to-crlf"
251 and singlelf.search(data)
251 and singlelf.search(data)
252 ):
252 ):
253 failed.append((f, target, bytes(ctx)))
253 failed.append((f, target, bytes(ctx)))
254 break
254 break
255 return failed
255 return failed
256
256
257
257
258 def parseeol(ui, repo, nodes):
258 def parseeol(ui, repo, nodes):
259 try:
259 try:
260 for node in nodes:
260 for node in nodes:
261 try:
261 try:
262 if node is None:
262 if node is None:
263 # Cannot use workingctx.data() since it would load
263 # Cannot use workingctx.data() since it would load
264 # and cache the filters before we configure them.
264 # and cache the filters before we configure them.
265 data = repo.wvfs(b'.hgeol').read()
265 data = repo.wvfs(b'.hgeol').read()
266 else:
266 else:
267 data = repo[node][b'.hgeol'].data()
267 data = repo[node][b'.hgeol'].data()
268 return eolfile(ui, repo.root, data)
268 return eolfile(ui, repo.root, data)
269 except (IOError, LookupError):
269 except (IOError, LookupError):
270 pass
270 pass
271 except errormod.ParseError as inst:
271 except errormod.ParseError as inst:
272 ui.warn(
272 ui.warn(
273 _(
273 _(
274 b"warning: ignoring .hgeol file due to parse error "
274 b"warning: ignoring .hgeol file due to parse error "
275 b"at %s: %s\n"
275 b"at %s: %s\n"
276 )
276 )
277 % (inst.args[1], inst.args[0])
277 % (inst.args[1], inst.args[0])
278 )
278 )
279 return None
279 return None
280
280
281
281
282 def ensureenabled(ui):
282 def ensureenabled(ui):
283 """make sure the extension is enabled when used as hook
283 """make sure the extension is enabled when used as hook
284
284
285 When eol is used through hooks, the extension is never formally loaded and
285 When eol is used through hooks, the extension is never formally loaded and
286 enabled. This has some side effect, for example the config declaration is
286 enabled. This has some side effect, for example the config declaration is
287 never loaded. This function ensure the extension is enabled when running
287 never loaded. This function ensure the extension is enabled when running
288 hooks.
288 hooks.
289 """
289 """
290 if b'eol' in ui._knownconfig:
290 if b'eol' in ui._knownconfig:
291 return
291 return
292 ui.setconfig(b'extensions', b'eol', b'', source=b'internal')
292 ui.setconfig(b'extensions', b'eol', b'', source=b'internal')
293 extensions.loadall(ui, [b'eol'])
293 extensions.loadall(ui, [b'eol'])
294
294
295
295
296 def _checkhook(ui, repo, node, headsonly):
296 def _checkhook(ui, repo, node, headsonly):
297 # Get revisions to check and touched files at the same time
297 # Get revisions to check and touched files at the same time
298 ensureenabled(ui)
298 ensureenabled(ui)
299 files = set()
299 files = set()
300 revs = set()
300 revs = set()
301 for rev in pycompat.xrange(repo[node].rev(), len(repo)):
301 for rev in pycompat.xrange(repo[node].rev(), len(repo)):
302 revs.add(rev)
302 revs.add(rev)
303 if headsonly:
303 if headsonly:
304 ctx = repo[rev]
304 ctx = repo[rev]
305 files.update(ctx.files())
305 files.update(ctx.files())
306 for pctx in ctx.parents():
306 for pctx in ctx.parents():
307 revs.discard(pctx.rev())
307 revs.discard(pctx.rev())
308 failed = []
308 failed = []
309 for rev in revs:
309 for rev in revs:
310 ctx = repo[rev]
310 ctx = repo[rev]
311 eol = parseeol(ui, repo, [ctx.node()])
311 eol = parseeol(ui, repo, [ctx.node()])
312 if eol:
312 if eol:
313 failed.extend(eol.checkrev(repo, ctx, files))
313 failed.extend(eol.checkrev(repo, ctx, files))
314
314
315 if failed:
315 if failed:
316 eols = {b'to-lf': b'CRLF', b'to-crlf': b'LF'}
316 eols = {b'to-lf': b'CRLF', b'to-crlf': b'LF'}
317 msgs = []
317 msgs = []
318 for f, target, node in sorted(failed):
318 for f, target, node in sorted(failed):
319 msgs.append(
319 msgs.append(
320 _(b" %s in %s should not have %s line endings")
320 _(b" %s in %s should not have %s line endings")
321 % (f, node, eols[target])
321 % (f, node, eols[target])
322 )
322 )
323 raise errormod.Abort(
323 raise errormod.Abort(
324 _(b"end-of-line check failed:\n") + b"\n".join(msgs)
324 _(b"end-of-line check failed:\n") + b"\n".join(msgs)
325 )
325 )
326
326
327
327
328 def checkallhook(ui, repo, node, hooktype, **kwargs):
328 def checkallhook(ui, repo, node, hooktype, **kwargs):
329 """verify that files have expected EOLs"""
329 """verify that files have expected EOLs"""
330 _checkhook(ui, repo, node, False)
330 _checkhook(ui, repo, node, False)
331
331
332
332
333 def checkheadshook(ui, repo, node, hooktype, **kwargs):
333 def checkheadshook(ui, repo, node, hooktype, **kwargs):
334 """verify that files have expected EOLs"""
334 """verify that files have expected EOLs"""
335 _checkhook(ui, repo, node, True)
335 _checkhook(ui, repo, node, True)
336
336
337
337
338 # "checkheadshook" used to be called "hook"
338 # "checkheadshook" used to be called "hook"
339 hook = checkheadshook
339 hook = checkheadshook
340
340
341
341
342 def preupdate(ui, repo, hooktype, parent1, parent2):
342 def preupdate(ui, repo, hooktype, parent1, parent2):
343 p1node = scmutil.resolvehexnodeidprefix(repo, parent1)
343 p1node = scmutil.resolvehexnodeidprefix(repo, parent1)
344 repo.loadeol([p1node])
344 repo.loadeol([p1node])
345 return False
345 return False
346
346
347
347
348 def uisetup(ui):
348 def uisetup(ui):
349 ui.setconfig(b'hooks', b'preupdate.eol', preupdate, b'eol')
349 ui.setconfig(b'hooks', b'preupdate.eol', preupdate, b'eol')
350
350
351
351
352 def extsetup(ui):
352 def extsetup(ui):
353 try:
353 try:
354 extensions.find(b'win32text')
354 extensions.find(b'win32text')
355 ui.warn(
355 ui.warn(
356 _(
356 _(
357 b"the eol extension is incompatible with the "
357 b"the eol extension is incompatible with the "
358 b"win32text extension\n"
358 b"win32text extension\n"
359 )
359 )
360 )
360 )
361 except KeyError:
361 except KeyError:
362 pass
362 pass
363
363
364
364
365 def reposetup(ui, repo):
365 def reposetup(ui, repo):
366 uisetup(repo.ui)
366 uisetup(repo.ui)
367
367
368 if not repo.local():
368 if not repo.local():
369 return
369 return
370 for name, fn in pycompat.iteritems(filters):
370 for name, fn in pycompat.iteritems(filters):
371 repo.adddatafilter(name, fn)
371 repo.adddatafilter(name, fn)
372
372
373 ui.setconfig(b'patch', b'eol', b'auto', b'eol')
373 ui.setconfig(b'patch', b'eol', b'auto', b'eol')
374
374
375 class eolrepo(repo.__class__):
375 class eolrepo(repo.__class__):
376 def loadeol(self, nodes):
376 def loadeol(self, nodes):
377 eol = parseeol(self.ui, self, nodes)
377 eol = parseeol(self.ui, self, nodes)
378 if eol is None:
378 if eol is None:
379 return None
379 return None
380 eol.copytoui(self.ui)
380 eol.copytoui(self.ui)
381 return eol.match
381 return eol.match
382
382
383 def _hgcleardirstate(self):
383 def _hgcleardirstate(self):
384 self._eolmatch = self.loadeol([None, b'tip'])
384 self._eolmatch = self.loadeol([None, b'tip'])
385 if not self._eolmatch:
385 if not self._eolmatch:
386 self._eolmatch = util.never
386 self._eolmatch = util.never
387 return
387 return
388
388
389 oldeol = None
389 oldeol = None
390 try:
390 try:
391 cachemtime = os.path.getmtime(self.vfs.join(b"eol.cache"))
391 cachemtime = os.path.getmtime(self.vfs.join(b"eol.cache"))
392 except OSError:
392 except OSError:
393 cachemtime = 0
393 cachemtime = 0
394 else:
394 else:
395 olddata = self.vfs.read(b"eol.cache")
395 olddata = self.vfs.read(b"eol.cache")
396 if olddata:
396 if olddata:
397 oldeol = eolfile(self.ui, self.root, olddata)
397 oldeol = eolfile(self.ui, self.root, olddata)
398
398
399 try:
399 try:
400 eolmtime = os.path.getmtime(self.wjoin(b".hgeol"))
400 eolmtime = os.path.getmtime(self.wjoin(b".hgeol"))
401 except OSError:
401 except OSError:
402 eolmtime = 0
402 eolmtime = 0
403
403
404 if eolmtime > cachemtime:
404 if eolmtime >= cachemtime and eolmtime > 0:
405 self.ui.debug(b"eol: detected change in .hgeol\n")
405 self.ui.debug(b"eol: detected change in .hgeol\n")
406
406
407 hgeoldata = self.wvfs.read(b'.hgeol')
407 hgeoldata = self.wvfs.read(b'.hgeol')
408 neweol = eolfile(self.ui, self.root, hgeoldata)
408 neweol = eolfile(self.ui, self.root, hgeoldata)
409
409
410 wlock = None
410 wlock = None
411 try:
411 try:
412 wlock = self.wlock()
412 wlock = self.wlock()
413 for f in self.dirstate:
413 for f in self.dirstate:
414 if self.dirstate[f] != b'n':
414 if self.dirstate[f] != b'n':
415 continue
415 continue
416 if oldeol is not None:
416 if oldeol is not None:
417 if not oldeol.match(f) and not neweol.match(f):
417 if not oldeol.match(f) and not neweol.match(f):
418 continue
418 continue
419 oldkey = None
419 oldkey = None
420 for pattern, key, m in oldeol.patterns:
420 for pattern, key, m in oldeol.patterns:
421 if m(f):
421 if m(f):
422 oldkey = key
422 oldkey = key
423 break
423 break
424 newkey = None
424 newkey = None
425 for pattern, key, m in neweol.patterns:
425 for pattern, key, m in neweol.patterns:
426 if m(f):
426 if m(f):
427 newkey = key
427 newkey = key
428 break
428 break
429 if oldkey == newkey:
429 if oldkey == newkey:
430 continue
430 continue
431 # all normal files need to be looked at again since
431 # all normal files need to be looked at again since
432 # the new .hgeol file specify a different filter
432 # the new .hgeol file specify a different filter
433 self.dirstate.normallookup(f)
433 self.dirstate.normallookup(f)
434 # Write the cache to update mtime and cache .hgeol
434 # Write the cache to update mtime and cache .hgeol
435 with self.vfs(b"eol.cache", b"w") as f:
435 with self.vfs(b"eol.cache", b"w") as f:
436 f.write(hgeoldata)
436 f.write(hgeoldata)
437 except errormod.LockUnavailable:
437 except errormod.LockUnavailable:
438 # If we cannot lock the repository and clear the
438 # If we cannot lock the repository and clear the
439 # dirstate, then a commit might not see all files
439 # dirstate, then a commit might not see all files
440 # as modified. But if we cannot lock the
440 # as modified. But if we cannot lock the
441 # repository, then we can also not make a commit,
441 # repository, then we can also not make a commit,
442 # so ignore the error.
442 # so ignore the error.
443 pass
443 pass
444 finally:
444 finally:
445 if wlock is not None:
445 if wlock is not None:
446 wlock.release()
446 wlock.release()
447
447
448 def commitctx(self, ctx, error=False, origctx=None):
448 def commitctx(self, ctx, error=False, origctx=None):
449 for f in sorted(ctx.added() + ctx.modified()):
449 for f in sorted(ctx.added() + ctx.modified()):
450 if not self._eolmatch(f):
450 if not self._eolmatch(f):
451 continue
451 continue
452 fctx = ctx[f]
452 fctx = ctx[f]
453 if fctx is None:
453 if fctx is None:
454 continue
454 continue
455 data = fctx.data()
455 data = fctx.data()
456 if stringutil.binary(data):
456 if stringutil.binary(data):
457 # We should not abort here, since the user should
457 # We should not abort here, since the user should
458 # be able to say "** = native" to automatically
458 # be able to say "** = native" to automatically
459 # have all non-binary files taken care of.
459 # have all non-binary files taken care of.
460 continue
460 continue
461 if inconsistenteol(data):
461 if inconsistenteol(data):
462 raise errormod.Abort(
462 raise errormod.Abort(
463 _(b"inconsistent newline style in %s\n") % f
463 _(b"inconsistent newline style in %s\n") % f
464 )
464 )
465 return super(eolrepo, self).commitctx(ctx, error, origctx)
465 return super(eolrepo, self).commitctx(ctx, error, origctx)
466
466
467 repo.__class__ = eolrepo
467 repo.__class__ = eolrepo
468 repo._hgcleardirstate()
468 repo._hgcleardirstate()
General Comments 0
You need to be logged in to leave comments. Login now