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