##// END OF EJS Templates
eol: use `set_possibly_dirty` instead of `normallookup`...
marmoute -
r48534:936f26b9 default
parent child Browse files
Show More
@@ -1,480 +1,480 b''
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',
121 b'eol',
122 b'fix-trailing-newline',
122 b'fix-trailing-newline',
123 default=False,
123 default=False,
124 )
124 )
125 configitem(
125 configitem(
126 b'eol',
126 b'eol',
127 b'native',
127 b'native',
128 default=pycompat.oslinesep,
128 default=pycompat.oslinesep,
129 )
129 )
130 configitem(
130 configitem(
131 b'eol',
131 b'eol',
132 b'only-consistent',
132 b'only-consistent',
133 default=True,
133 default=True,
134 )
134 )
135
135
136 # Matches a lone LF, i.e., one that is not part of CRLF.
136 # Matches a lone LF, i.e., one that is not part of CRLF.
137 singlelf = re.compile(b'(^|[^\r])\n')
137 singlelf = re.compile(b'(^|[^\r])\n')
138
138
139
139
140 def inconsistenteol(data):
140 def inconsistenteol(data):
141 return b'\r\n' in data and singlelf.search(data)
141 return b'\r\n' in data and singlelf.search(data)
142
142
143
143
144 def tolf(s, params, ui, **kwargs):
144 def tolf(s, params, ui, **kwargs):
145 """Filter to convert to LF EOLs."""
145 """Filter to convert to LF EOLs."""
146 if stringutil.binary(s):
146 if stringutil.binary(s):
147 return s
147 return s
148 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
148 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
149 return s
149 return s
150 if (
150 if (
151 ui.configbool(b'eol', b'fix-trailing-newline')
151 ui.configbool(b'eol', b'fix-trailing-newline')
152 and s
152 and s
153 and not s.endswith(b'\n')
153 and not s.endswith(b'\n')
154 ):
154 ):
155 s = s + b'\n'
155 s = s + b'\n'
156 return util.tolf(s)
156 return util.tolf(s)
157
157
158
158
159 def tocrlf(s, params, ui, **kwargs):
159 def tocrlf(s, params, ui, **kwargs):
160 """Filter to convert to CRLF EOLs."""
160 """Filter to convert to CRLF EOLs."""
161 if stringutil.binary(s):
161 if stringutil.binary(s):
162 return s
162 return s
163 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
163 if ui.configbool(b'eol', b'only-consistent') and inconsistenteol(s):
164 return s
164 return s
165 if (
165 if (
166 ui.configbool(b'eol', b'fix-trailing-newline')
166 ui.configbool(b'eol', b'fix-trailing-newline')
167 and s
167 and s
168 and not s.endswith(b'\n')
168 and not s.endswith(b'\n')
169 ):
169 ):
170 s = s + b'\n'
170 s = s + b'\n'
171 return util.tocrlf(s)
171 return util.tocrlf(s)
172
172
173
173
174 def isbinary(s, params, ui, **kwargs):
174 def isbinary(s, params, ui, **kwargs):
175 """Filter to do nothing with the file."""
175 """Filter to do nothing with the file."""
176 return s
176 return s
177
177
178
178
179 filters = {
179 filters = {
180 b'to-lf': tolf,
180 b'to-lf': tolf,
181 b'to-crlf': tocrlf,
181 b'to-crlf': tocrlf,
182 b'is-binary': isbinary,
182 b'is-binary': isbinary,
183 # The following provide backwards compatibility with win32text
183 # The following provide backwards compatibility with win32text
184 b'cleverencode:': tolf,
184 b'cleverencode:': tolf,
185 b'cleverdecode:': tocrlf,
185 b'cleverdecode:': tocrlf,
186 }
186 }
187
187
188
188
189 class eolfile(object):
189 class eolfile(object):
190 def __init__(self, ui, root, data):
190 def __init__(self, ui, root, data):
191 self._decode = {
191 self._decode = {
192 b'LF': b'to-lf',
192 b'LF': b'to-lf',
193 b'CRLF': b'to-crlf',
193 b'CRLF': b'to-crlf',
194 b'BIN': b'is-binary',
194 b'BIN': b'is-binary',
195 }
195 }
196 self._encode = {
196 self._encode = {
197 b'LF': b'to-lf',
197 b'LF': b'to-lf',
198 b'CRLF': b'to-crlf',
198 b'CRLF': b'to-crlf',
199 b'BIN': b'is-binary',
199 b'BIN': b'is-binary',
200 }
200 }
201
201
202 self.cfg = config.config()
202 self.cfg = config.config()
203 # Our files should not be touched. The pattern must be
203 # Our files should not be touched. The pattern must be
204 # inserted first override a '** = native' pattern.
204 # inserted first override a '** = native' pattern.
205 self.cfg.set(b'patterns', b'.hg*', b'BIN', b'eol')
205 self.cfg.set(b'patterns', b'.hg*', b'BIN', b'eol')
206 # We can then parse the user's patterns.
206 # We can then parse the user's patterns.
207 self.cfg.parse(b'.hgeol', data)
207 self.cfg.parse(b'.hgeol', data)
208
208
209 isrepolf = self.cfg.get(b'repository', b'native') != b'CRLF'
209 isrepolf = self.cfg.get(b'repository', b'native') != b'CRLF'
210 self._encode[b'NATIVE'] = isrepolf and b'to-lf' or b'to-crlf'
210 self._encode[b'NATIVE'] = isrepolf and b'to-lf' or b'to-crlf'
211 iswdlf = ui.config(b'eol', b'native') in (b'LF', b'\n')
211 iswdlf = ui.config(b'eol', b'native') in (b'LF', b'\n')
212 self._decode[b'NATIVE'] = iswdlf and b'to-lf' or b'to-crlf'
212 self._decode[b'NATIVE'] = iswdlf and b'to-lf' or b'to-crlf'
213
213
214 include = []
214 include = []
215 exclude = []
215 exclude = []
216 self.patterns = []
216 self.patterns = []
217 for pattern, style in self.cfg.items(b'patterns'):
217 for pattern, style in self.cfg.items(b'patterns'):
218 key = style.upper()
218 key = style.upper()
219 if key == b'BIN':
219 if key == b'BIN':
220 exclude.append(pattern)
220 exclude.append(pattern)
221 else:
221 else:
222 include.append(pattern)
222 include.append(pattern)
223 m = match.match(root, b'', [pattern])
223 m = match.match(root, b'', [pattern])
224 self.patterns.append((pattern, key, m))
224 self.patterns.append((pattern, key, m))
225 # This will match the files for which we need to care
225 # This will match the files for which we need to care
226 # about inconsistent newlines.
226 # about inconsistent newlines.
227 self.match = match.match(root, b'', [], include, exclude)
227 self.match = match.match(root, b'', [], include, exclude)
228
228
229 def copytoui(self, ui):
229 def copytoui(self, ui):
230 newpatterns = {pattern for pattern, key, m in self.patterns}
230 newpatterns = {pattern for pattern, key, m in self.patterns}
231 for section in (b'decode', b'encode'):
231 for section in (b'decode', b'encode'):
232 for oldpattern, _filter in ui.configitems(section):
232 for oldpattern, _filter in ui.configitems(section):
233 if oldpattern not in newpatterns:
233 if oldpattern not in newpatterns:
234 if ui.configsource(section, oldpattern) == b'eol':
234 if ui.configsource(section, oldpattern) == b'eol':
235 ui.setconfig(section, oldpattern, b'!', b'eol')
235 ui.setconfig(section, oldpattern, b'!', b'eol')
236 for pattern, key, m in self.patterns:
236 for pattern, key, m in self.patterns:
237 try:
237 try:
238 ui.setconfig(b'decode', pattern, self._decode[key], b'eol')
238 ui.setconfig(b'decode', pattern, self._decode[key], b'eol')
239 ui.setconfig(b'encode', pattern, self._encode[key], b'eol')
239 ui.setconfig(b'encode', pattern, self._encode[key], b'eol')
240 except KeyError:
240 except KeyError:
241 ui.warn(
241 ui.warn(
242 _(b"ignoring unknown EOL style '%s' from %s\n")
242 _(b"ignoring unknown EOL style '%s' from %s\n")
243 % (key, self.cfg.source(b'patterns', pattern))
243 % (key, self.cfg.source(b'patterns', pattern))
244 )
244 )
245 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
245 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
246 for k, v in self.cfg.items(b'eol'):
246 for k, v in self.cfg.items(b'eol'):
247 ui.setconfig(b'eol', k, v, b'eol')
247 ui.setconfig(b'eol', k, v, b'eol')
248
248
249 def checkrev(self, repo, ctx, files):
249 def checkrev(self, repo, ctx, files):
250 failed = []
250 failed = []
251 for f in files or ctx.files():
251 for f in files or ctx.files():
252 if f not in ctx:
252 if f not in ctx:
253 continue
253 continue
254 for pattern, key, m in self.patterns:
254 for pattern, key, m in self.patterns:
255 if not m(f):
255 if not m(f):
256 continue
256 continue
257 target = self._encode[key]
257 target = self._encode[key]
258 data = ctx[f].data()
258 data = ctx[f].data()
259 if (
259 if (
260 target == b"to-lf"
260 target == b"to-lf"
261 and b"\r\n" in data
261 and b"\r\n" in data
262 or target == b"to-crlf"
262 or target == b"to-crlf"
263 and singlelf.search(data)
263 and singlelf.search(data)
264 ):
264 ):
265 failed.append((f, target, bytes(ctx)))
265 failed.append((f, target, bytes(ctx)))
266 break
266 break
267 return failed
267 return failed
268
268
269
269
270 def parseeol(ui, repo, nodes):
270 def parseeol(ui, repo, nodes):
271 try:
271 try:
272 for node in nodes:
272 for node in nodes:
273 try:
273 try:
274 if node is None:
274 if node is None:
275 # Cannot use workingctx.data() since it would load
275 # Cannot use workingctx.data() since it would load
276 # and cache the filters before we configure them.
276 # and cache the filters before we configure them.
277 data = repo.wvfs(b'.hgeol').read()
277 data = repo.wvfs(b'.hgeol').read()
278 else:
278 else:
279 data = repo[node][b'.hgeol'].data()
279 data = repo[node][b'.hgeol'].data()
280 return eolfile(ui, repo.root, data)
280 return eolfile(ui, repo.root, data)
281 except (IOError, LookupError):
281 except (IOError, LookupError):
282 pass
282 pass
283 except errormod.ConfigError as inst:
283 except errormod.ConfigError as inst:
284 ui.warn(
284 ui.warn(
285 _(
285 _(
286 b"warning: ignoring .hgeol file due to parse error "
286 b"warning: ignoring .hgeol file due to parse error "
287 b"at %s: %s\n"
287 b"at %s: %s\n"
288 )
288 )
289 % (inst.location, inst.message)
289 % (inst.location, inst.message)
290 )
290 )
291 return None
291 return None
292
292
293
293
294 def ensureenabled(ui):
294 def ensureenabled(ui):
295 """make sure the extension is enabled when used as hook
295 """make sure the extension is enabled when used as hook
296
296
297 When eol is used through hooks, the extension is never formally loaded and
297 When eol is used through hooks, the extension is never formally loaded and
298 enabled. This has some side effect, for example the config declaration is
298 enabled. This has some side effect, for example the config declaration is
299 never loaded. This function ensure the extension is enabled when running
299 never loaded. This function ensure the extension is enabled when running
300 hooks.
300 hooks.
301 """
301 """
302 if b'eol' in ui._knownconfig:
302 if b'eol' in ui._knownconfig:
303 return
303 return
304 ui.setconfig(b'extensions', b'eol', b'', source=b'internal')
304 ui.setconfig(b'extensions', b'eol', b'', source=b'internal')
305 extensions.loadall(ui, [b'eol'])
305 extensions.loadall(ui, [b'eol'])
306
306
307
307
308 def _checkhook(ui, repo, node, headsonly):
308 def _checkhook(ui, repo, node, headsonly):
309 # Get revisions to check and touched files at the same time
309 # Get revisions to check and touched files at the same time
310 ensureenabled(ui)
310 ensureenabled(ui)
311 files = set()
311 files = set()
312 revs = set()
312 revs = set()
313 for rev in pycompat.xrange(repo[node].rev(), len(repo)):
313 for rev in pycompat.xrange(repo[node].rev(), len(repo)):
314 revs.add(rev)
314 revs.add(rev)
315 if headsonly:
315 if headsonly:
316 ctx = repo[rev]
316 ctx = repo[rev]
317 files.update(ctx.files())
317 files.update(ctx.files())
318 for pctx in ctx.parents():
318 for pctx in ctx.parents():
319 revs.discard(pctx.rev())
319 revs.discard(pctx.rev())
320 failed = []
320 failed = []
321 for rev in revs:
321 for rev in revs:
322 ctx = repo[rev]
322 ctx = repo[rev]
323 eol = parseeol(ui, repo, [ctx.node()])
323 eol = parseeol(ui, repo, [ctx.node()])
324 if eol:
324 if eol:
325 failed.extend(eol.checkrev(repo, ctx, files))
325 failed.extend(eol.checkrev(repo, ctx, files))
326
326
327 if failed:
327 if failed:
328 eols = {b'to-lf': b'CRLF', b'to-crlf': b'LF'}
328 eols = {b'to-lf': b'CRLF', b'to-crlf': b'LF'}
329 msgs = []
329 msgs = []
330 for f, target, node in sorted(failed):
330 for f, target, node in sorted(failed):
331 msgs.append(
331 msgs.append(
332 _(b" %s in %s should not have %s line endings")
332 _(b" %s in %s should not have %s line endings")
333 % (f, node, eols[target])
333 % (f, node, eols[target])
334 )
334 )
335 raise errormod.Abort(
335 raise errormod.Abort(
336 _(b"end-of-line check failed:\n") + b"\n".join(msgs)
336 _(b"end-of-line check failed:\n") + b"\n".join(msgs)
337 )
337 )
338
338
339
339
340 def checkallhook(ui, repo, node, hooktype, **kwargs):
340 def checkallhook(ui, repo, node, hooktype, **kwargs):
341 """verify that files have expected EOLs"""
341 """verify that files have expected EOLs"""
342 _checkhook(ui, repo, node, False)
342 _checkhook(ui, repo, node, False)
343
343
344
344
345 def checkheadshook(ui, repo, node, hooktype, **kwargs):
345 def checkheadshook(ui, repo, node, hooktype, **kwargs):
346 """verify that files have expected EOLs"""
346 """verify that files have expected EOLs"""
347 _checkhook(ui, repo, node, True)
347 _checkhook(ui, repo, node, True)
348
348
349
349
350 # "checkheadshook" used to be called "hook"
350 # "checkheadshook" used to be called "hook"
351 hook = checkheadshook
351 hook = checkheadshook
352
352
353
353
354 def preupdate(ui, repo, hooktype, parent1, parent2):
354 def preupdate(ui, repo, hooktype, parent1, parent2):
355 p1node = scmutil.resolvehexnodeidprefix(repo, parent1)
355 p1node = scmutil.resolvehexnodeidprefix(repo, parent1)
356 repo.loadeol([p1node])
356 repo.loadeol([p1node])
357 return False
357 return False
358
358
359
359
360 def uisetup(ui):
360 def uisetup(ui):
361 ui.setconfig(b'hooks', b'preupdate.eol', preupdate, b'eol')
361 ui.setconfig(b'hooks', b'preupdate.eol', preupdate, b'eol')
362
362
363
363
364 def extsetup(ui):
364 def extsetup(ui):
365 try:
365 try:
366 extensions.find(b'win32text')
366 extensions.find(b'win32text')
367 ui.warn(
367 ui.warn(
368 _(
368 _(
369 b"the eol extension is incompatible with the "
369 b"the eol extension is incompatible with the "
370 b"win32text extension\n"
370 b"win32text extension\n"
371 )
371 )
372 )
372 )
373 except KeyError:
373 except KeyError:
374 pass
374 pass
375
375
376
376
377 def reposetup(ui, repo):
377 def reposetup(ui, repo):
378 uisetup(repo.ui)
378 uisetup(repo.ui)
379
379
380 if not repo.local():
380 if not repo.local():
381 return
381 return
382 for name, fn in pycompat.iteritems(filters):
382 for name, fn in pycompat.iteritems(filters):
383 repo.adddatafilter(name, fn)
383 repo.adddatafilter(name, fn)
384
384
385 ui.setconfig(b'patch', b'eol', b'auto', b'eol')
385 ui.setconfig(b'patch', b'eol', b'auto', b'eol')
386
386
387 class eolrepo(repo.__class__):
387 class eolrepo(repo.__class__):
388 def loadeol(self, nodes):
388 def loadeol(self, nodes):
389 eol = parseeol(self.ui, self, nodes)
389 eol = parseeol(self.ui, self, nodes)
390 if eol is None:
390 if eol is None:
391 return None
391 return None
392 eol.copytoui(self.ui)
392 eol.copytoui(self.ui)
393 return eol.match
393 return eol.match
394
394
395 def _hgcleardirstate(self):
395 def _hgcleardirstate(self):
396 self._eolmatch = self.loadeol([None])
396 self._eolmatch = self.loadeol([None])
397 if not self._eolmatch:
397 if not self._eolmatch:
398 self._eolmatch = util.never
398 self._eolmatch = util.never
399 return
399 return
400
400
401 oldeol = None
401 oldeol = None
402 try:
402 try:
403 cachemtime = os.path.getmtime(self.vfs.join(b"eol.cache"))
403 cachemtime = os.path.getmtime(self.vfs.join(b"eol.cache"))
404 except OSError:
404 except OSError:
405 cachemtime = 0
405 cachemtime = 0
406 else:
406 else:
407 olddata = self.vfs.read(b"eol.cache")
407 olddata = self.vfs.read(b"eol.cache")
408 if olddata:
408 if olddata:
409 oldeol = eolfile(self.ui, self.root, olddata)
409 oldeol = eolfile(self.ui, self.root, olddata)
410
410
411 try:
411 try:
412 eolmtime = os.path.getmtime(self.wjoin(b".hgeol"))
412 eolmtime = os.path.getmtime(self.wjoin(b".hgeol"))
413 except OSError:
413 except OSError:
414 eolmtime = 0
414 eolmtime = 0
415
415
416 if eolmtime >= cachemtime and eolmtime > 0:
416 if eolmtime >= cachemtime and eolmtime > 0:
417 self.ui.debug(b"eol: detected change in .hgeol\n")
417 self.ui.debug(b"eol: detected change in .hgeol\n")
418
418
419 hgeoldata = self.wvfs.read(b'.hgeol')
419 hgeoldata = self.wvfs.read(b'.hgeol')
420 neweol = eolfile(self.ui, self.root, hgeoldata)
420 neweol = eolfile(self.ui, self.root, hgeoldata)
421
421
422 wlock = None
422 wlock = None
423 try:
423 try:
424 wlock = self.wlock()
424 wlock = self.wlock()
425 for f in self.dirstate:
425 for f in self.dirstate:
426 if self.dirstate[f] != b'n':
426 if self.dirstate[f] != b'n':
427 continue
427 continue
428 if oldeol is not None:
428 if oldeol is not None:
429 if not oldeol.match(f) and not neweol.match(f):
429 if not oldeol.match(f) and not neweol.match(f):
430 continue
430 continue
431 oldkey = None
431 oldkey = None
432 for pattern, key, m in oldeol.patterns:
432 for pattern, key, m in oldeol.patterns:
433 if m(f):
433 if m(f):
434 oldkey = key
434 oldkey = key
435 break
435 break
436 newkey = None
436 newkey = None
437 for pattern, key, m in neweol.patterns:
437 for pattern, key, m in neweol.patterns:
438 if m(f):
438 if m(f):
439 newkey = key
439 newkey = key
440 break
440 break
441 if oldkey == newkey:
441 if oldkey == newkey:
442 continue
442 continue
443 # all normal files need to be looked at again since
443 # all normal files need to be looked at again since
444 # the new .hgeol file specify a different filter
444 # the new .hgeol file specify a different filter
445 self.dirstate.normallookup(f)
445 self.dirstate.set_possibly_dirty(f)
446 # Write the cache to update mtime and cache .hgeol
446 # Write the cache to update mtime and cache .hgeol
447 with self.vfs(b"eol.cache", b"w") as f:
447 with self.vfs(b"eol.cache", b"w") as f:
448 f.write(hgeoldata)
448 f.write(hgeoldata)
449 except errormod.LockUnavailable:
449 except errormod.LockUnavailable:
450 # If we cannot lock the repository and clear the
450 # If we cannot lock the repository and clear the
451 # dirstate, then a commit might not see all files
451 # dirstate, then a commit might not see all files
452 # as modified. But if we cannot lock the
452 # as modified. But if we cannot lock the
453 # repository, then we can also not make a commit,
453 # repository, then we can also not make a commit,
454 # so ignore the error.
454 # so ignore the error.
455 pass
455 pass
456 finally:
456 finally:
457 if wlock is not None:
457 if wlock is not None:
458 wlock.release()
458 wlock.release()
459
459
460 def commitctx(self, ctx, error=False, origctx=None):
460 def commitctx(self, ctx, error=False, origctx=None):
461 for f in sorted(ctx.added() + ctx.modified()):
461 for f in sorted(ctx.added() + ctx.modified()):
462 if not self._eolmatch(f):
462 if not self._eolmatch(f):
463 continue
463 continue
464 fctx = ctx[f]
464 fctx = ctx[f]
465 if fctx is None:
465 if fctx is None:
466 continue
466 continue
467 data = fctx.data()
467 data = fctx.data()
468 if stringutil.binary(data):
468 if stringutil.binary(data):
469 # We should not abort here, since the user should
469 # We should not abort here, since the user should
470 # be able to say "** = native" to automatically
470 # be able to say "** = native" to automatically
471 # have all non-binary files taken care of.
471 # have all non-binary files taken care of.
472 continue
472 continue
473 if inconsistenteol(data):
473 if inconsistenteol(data):
474 raise errormod.Abort(
474 raise errormod.Abort(
475 _(b"inconsistent newline style in %s\n") % f
475 _(b"inconsistent newline style in %s\n") % f
476 )
476 )
477 return super(eolrepo, self).commitctx(ctx, error, origctx)
477 return super(eolrepo, self).commitctx(ctx, error, origctx)
478
478
479 repo.__class__ = eolrepo
479 repo.__class__ = eolrepo
480 repo._hgcleardirstate()
480 repo._hgcleardirstate()
General Comments 0
You need to be logged in to leave comments. Login now