##// END OF EJS Templates
eol: don't fallback to use .hgeol from tip (BC)...
Mads Kiilerich -
r43478:4aa9f3a1 default
parent child Browse files
Show More
@@ -1,474 +1,474 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', 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 newpatterns = set(pattern for pattern, key, m in self.patterns)
224 newpatterns = set(pattern for pattern, key, m in self.patterns)
225 for section in (b'decode', b'encode'):
225 for section in (b'decode', b'encode'):
226 for oldpattern, _filter in ui.configitems(section):
226 for oldpattern, _filter in ui.configitems(section):
227 if oldpattern not in newpatterns:
227 if oldpattern not in newpatterns:
228 if ui.configsource(section, oldpattern) == b'eol':
228 if ui.configsource(section, oldpattern) == b'eol':
229 ui.setconfig(section, oldpattern, b'!', b'eol')
229 ui.setconfig(section, oldpattern, b'!', b'eol')
230 for pattern, key, m in self.patterns:
230 for pattern, key, m in self.patterns:
231 try:
231 try:
232 ui.setconfig(b'decode', pattern, self._decode[key], b'eol')
232 ui.setconfig(b'decode', pattern, self._decode[key], b'eol')
233 ui.setconfig(b'encode', pattern, self._encode[key], b'eol')
233 ui.setconfig(b'encode', pattern, self._encode[key], b'eol')
234 except KeyError:
234 except KeyError:
235 ui.warn(
235 ui.warn(
236 _(b"ignoring unknown EOL style '%s' from %s\n")
236 _(b"ignoring unknown EOL style '%s' from %s\n")
237 % (key, self.cfg.source(b'patterns', pattern))
237 % (key, self.cfg.source(b'patterns', pattern))
238 )
238 )
239 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
239 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
240 for k, v in self.cfg.items(b'eol'):
240 for k, v in self.cfg.items(b'eol'):
241 ui.setconfig(b'eol', k, v, b'eol')
241 ui.setconfig(b'eol', k, v, b'eol')
242
242
243 def checkrev(self, repo, ctx, files):
243 def checkrev(self, repo, ctx, files):
244 failed = []
244 failed = []
245 for f in files or ctx.files():
245 for f in files or ctx.files():
246 if f not in ctx:
246 if f not in ctx:
247 continue
247 continue
248 for pattern, key, m in self.patterns:
248 for pattern, key, m in self.patterns:
249 if not m(f):
249 if not m(f):
250 continue
250 continue
251 target = self._encode[key]
251 target = self._encode[key]
252 data = ctx[f].data()
252 data = ctx[f].data()
253 if (
253 if (
254 target == b"to-lf"
254 target == b"to-lf"
255 and b"\r\n" in data
255 and b"\r\n" in data
256 or target == b"to-crlf"
256 or target == b"to-crlf"
257 and singlelf.search(data)
257 and singlelf.search(data)
258 ):
258 ):
259 failed.append((f, target, bytes(ctx)))
259 failed.append((f, target, bytes(ctx)))
260 break
260 break
261 return failed
261 return failed
262
262
263
263
264 def parseeol(ui, repo, nodes):
264 def parseeol(ui, repo, nodes):
265 try:
265 try:
266 for node in nodes:
266 for node in nodes:
267 try:
267 try:
268 if node is None:
268 if node is None:
269 # Cannot use workingctx.data() since it would load
269 # Cannot use workingctx.data() since it would load
270 # and cache the filters before we configure them.
270 # and cache the filters before we configure them.
271 data = repo.wvfs(b'.hgeol').read()
271 data = repo.wvfs(b'.hgeol').read()
272 else:
272 else:
273 data = repo[node][b'.hgeol'].data()
273 data = repo[node][b'.hgeol'].data()
274 return eolfile(ui, repo.root, data)
274 return eolfile(ui, repo.root, data)
275 except (IOError, LookupError):
275 except (IOError, LookupError):
276 pass
276 pass
277 except errormod.ParseError as inst:
277 except errormod.ParseError as inst:
278 ui.warn(
278 ui.warn(
279 _(
279 _(
280 b"warning: ignoring .hgeol file due to parse error "
280 b"warning: ignoring .hgeol file due to parse error "
281 b"at %s: %s\n"
281 b"at %s: %s\n"
282 )
282 )
283 % (inst.args[1], inst.args[0])
283 % (inst.args[1], inst.args[0])
284 )
284 )
285 return None
285 return None
286
286
287
287
288 def ensureenabled(ui):
288 def ensureenabled(ui):
289 """make sure the extension is enabled when used as hook
289 """make sure the extension is enabled when used as hook
290
290
291 When eol is used through hooks, the extension is never formally loaded and
291 When eol is used through hooks, the extension is never formally loaded and
292 enabled. This has some side effect, for example the config declaration is
292 enabled. This has some side effect, for example the config declaration is
293 never loaded. This function ensure the extension is enabled when running
293 never loaded. This function ensure the extension is enabled when running
294 hooks.
294 hooks.
295 """
295 """
296 if b'eol' in ui._knownconfig:
296 if b'eol' in ui._knownconfig:
297 return
297 return
298 ui.setconfig(b'extensions', b'eol', b'', source=b'internal')
298 ui.setconfig(b'extensions', b'eol', b'', source=b'internal')
299 extensions.loadall(ui, [b'eol'])
299 extensions.loadall(ui, [b'eol'])
300
300
301
301
302 def _checkhook(ui, repo, node, headsonly):
302 def _checkhook(ui, repo, node, headsonly):
303 # Get revisions to check and touched files at the same time
303 # Get revisions to check and touched files at the same time
304 ensureenabled(ui)
304 ensureenabled(ui)
305 files = set()
305 files = set()
306 revs = set()
306 revs = set()
307 for rev in pycompat.xrange(repo[node].rev(), len(repo)):
307 for rev in pycompat.xrange(repo[node].rev(), len(repo)):
308 revs.add(rev)
308 revs.add(rev)
309 if headsonly:
309 if headsonly:
310 ctx = repo[rev]
310 ctx = repo[rev]
311 files.update(ctx.files())
311 files.update(ctx.files())
312 for pctx in ctx.parents():
312 for pctx in ctx.parents():
313 revs.discard(pctx.rev())
313 revs.discard(pctx.rev())
314 failed = []
314 failed = []
315 for rev in revs:
315 for rev in revs:
316 ctx = repo[rev]
316 ctx = repo[rev]
317 eol = parseeol(ui, repo, [ctx.node()])
317 eol = parseeol(ui, repo, [ctx.node()])
318 if eol:
318 if eol:
319 failed.extend(eol.checkrev(repo, ctx, files))
319 failed.extend(eol.checkrev(repo, ctx, files))
320
320
321 if failed:
321 if failed:
322 eols = {b'to-lf': b'CRLF', b'to-crlf': b'LF'}
322 eols = {b'to-lf': b'CRLF', b'to-crlf': b'LF'}
323 msgs = []
323 msgs = []
324 for f, target, node in sorted(failed):
324 for f, target, node in sorted(failed):
325 msgs.append(
325 msgs.append(
326 _(b" %s in %s should not have %s line endings")
326 _(b" %s in %s should not have %s line endings")
327 % (f, node, eols[target])
327 % (f, node, eols[target])
328 )
328 )
329 raise errormod.Abort(
329 raise errormod.Abort(
330 _(b"end-of-line check failed:\n") + b"\n".join(msgs)
330 _(b"end-of-line check failed:\n") + b"\n".join(msgs)
331 )
331 )
332
332
333
333
334 def checkallhook(ui, repo, node, hooktype, **kwargs):
334 def checkallhook(ui, repo, node, hooktype, **kwargs):
335 """verify that files have expected EOLs"""
335 """verify that files have expected EOLs"""
336 _checkhook(ui, repo, node, False)
336 _checkhook(ui, repo, node, False)
337
337
338
338
339 def checkheadshook(ui, repo, node, hooktype, **kwargs):
339 def checkheadshook(ui, repo, node, hooktype, **kwargs):
340 """verify that files have expected EOLs"""
340 """verify that files have expected EOLs"""
341 _checkhook(ui, repo, node, True)
341 _checkhook(ui, repo, node, True)
342
342
343
343
344 # "checkheadshook" used to be called "hook"
344 # "checkheadshook" used to be called "hook"
345 hook = checkheadshook
345 hook = checkheadshook
346
346
347
347
348 def preupdate(ui, repo, hooktype, parent1, parent2):
348 def preupdate(ui, repo, hooktype, parent1, parent2):
349 p1node = scmutil.resolvehexnodeidprefix(repo, parent1)
349 p1node = scmutil.resolvehexnodeidprefix(repo, parent1)
350 repo.loadeol([p1node])
350 repo.loadeol([p1node])
351 return False
351 return False
352
352
353
353
354 def uisetup(ui):
354 def uisetup(ui):
355 ui.setconfig(b'hooks', b'preupdate.eol', preupdate, b'eol')
355 ui.setconfig(b'hooks', b'preupdate.eol', preupdate, b'eol')
356
356
357
357
358 def extsetup(ui):
358 def extsetup(ui):
359 try:
359 try:
360 extensions.find(b'win32text')
360 extensions.find(b'win32text')
361 ui.warn(
361 ui.warn(
362 _(
362 _(
363 b"the eol extension is incompatible with the "
363 b"the eol extension is incompatible with the "
364 b"win32text extension\n"
364 b"win32text extension\n"
365 )
365 )
366 )
366 )
367 except KeyError:
367 except KeyError:
368 pass
368 pass
369
369
370
370
371 def reposetup(ui, repo):
371 def reposetup(ui, repo):
372 uisetup(repo.ui)
372 uisetup(repo.ui)
373
373
374 if not repo.local():
374 if not repo.local():
375 return
375 return
376 for name, fn in pycompat.iteritems(filters):
376 for name, fn in pycompat.iteritems(filters):
377 repo.adddatafilter(name, fn)
377 repo.adddatafilter(name, fn)
378
378
379 ui.setconfig(b'patch', b'eol', b'auto', b'eol')
379 ui.setconfig(b'patch', b'eol', b'auto', b'eol')
380
380
381 class eolrepo(repo.__class__):
381 class eolrepo(repo.__class__):
382 def loadeol(self, nodes):
382 def loadeol(self, nodes):
383 eol = parseeol(self.ui, self, nodes)
383 eol = parseeol(self.ui, self, nodes)
384 if eol is None:
384 if eol is None:
385 return None
385 return None
386 eol.copytoui(self.ui)
386 eol.copytoui(self.ui)
387 return eol.match
387 return eol.match
388
388
389 def _hgcleardirstate(self):
389 def _hgcleardirstate(self):
390 self._eolmatch = self.loadeol([None, b'tip'])
390 self._eolmatch = self.loadeol([None])
391 if not self._eolmatch:
391 if not self._eolmatch:
392 self._eolmatch = util.never
392 self._eolmatch = util.never
393 return
393 return
394
394
395 oldeol = None
395 oldeol = None
396 try:
396 try:
397 cachemtime = os.path.getmtime(self.vfs.join(b"eol.cache"))
397 cachemtime = os.path.getmtime(self.vfs.join(b"eol.cache"))
398 except OSError:
398 except OSError:
399 cachemtime = 0
399 cachemtime = 0
400 else:
400 else:
401 olddata = self.vfs.read(b"eol.cache")
401 olddata = self.vfs.read(b"eol.cache")
402 if olddata:
402 if olddata:
403 oldeol = eolfile(self.ui, self.root, olddata)
403 oldeol = eolfile(self.ui, self.root, olddata)
404
404
405 try:
405 try:
406 eolmtime = os.path.getmtime(self.wjoin(b".hgeol"))
406 eolmtime = os.path.getmtime(self.wjoin(b".hgeol"))
407 except OSError:
407 except OSError:
408 eolmtime = 0
408 eolmtime = 0
409
409
410 if eolmtime >= cachemtime and eolmtime > 0:
410 if eolmtime >= cachemtime and eolmtime > 0:
411 self.ui.debug(b"eol: detected change in .hgeol\n")
411 self.ui.debug(b"eol: detected change in .hgeol\n")
412
412
413 hgeoldata = self.wvfs.read(b'.hgeol')
413 hgeoldata = self.wvfs.read(b'.hgeol')
414 neweol = eolfile(self.ui, self.root, hgeoldata)
414 neweol = eolfile(self.ui, self.root, hgeoldata)
415
415
416 wlock = None
416 wlock = None
417 try:
417 try:
418 wlock = self.wlock()
418 wlock = self.wlock()
419 for f in self.dirstate:
419 for f in self.dirstate:
420 if self.dirstate[f] != b'n':
420 if self.dirstate[f] != b'n':
421 continue
421 continue
422 if oldeol is not None:
422 if oldeol is not None:
423 if not oldeol.match(f) and not neweol.match(f):
423 if not oldeol.match(f) and not neweol.match(f):
424 continue
424 continue
425 oldkey = None
425 oldkey = None
426 for pattern, key, m in oldeol.patterns:
426 for pattern, key, m in oldeol.patterns:
427 if m(f):
427 if m(f):
428 oldkey = key
428 oldkey = key
429 break
429 break
430 newkey = None
430 newkey = None
431 for pattern, key, m in neweol.patterns:
431 for pattern, key, m in neweol.patterns:
432 if m(f):
432 if m(f):
433 newkey = key
433 newkey = key
434 break
434 break
435 if oldkey == newkey:
435 if oldkey == newkey:
436 continue
436 continue
437 # all normal files need to be looked at again since
437 # all normal files need to be looked at again since
438 # the new .hgeol file specify a different filter
438 # the new .hgeol file specify a different filter
439 self.dirstate.normallookup(f)
439 self.dirstate.normallookup(f)
440 # Write the cache to update mtime and cache .hgeol
440 # Write the cache to update mtime and cache .hgeol
441 with self.vfs(b"eol.cache", b"w") as f:
441 with self.vfs(b"eol.cache", b"w") as f:
442 f.write(hgeoldata)
442 f.write(hgeoldata)
443 except errormod.LockUnavailable:
443 except errormod.LockUnavailable:
444 # If we cannot lock the repository and clear the
444 # If we cannot lock the repository and clear the
445 # dirstate, then a commit might not see all files
445 # dirstate, then a commit might not see all files
446 # as modified. But if we cannot lock the
446 # as modified. But if we cannot lock the
447 # repository, then we can also not make a commit,
447 # repository, then we can also not make a commit,
448 # so ignore the error.
448 # so ignore the error.
449 pass
449 pass
450 finally:
450 finally:
451 if wlock is not None:
451 if wlock is not None:
452 wlock.release()
452 wlock.release()
453
453
454 def commitctx(self, ctx, error=False, origctx=None):
454 def commitctx(self, ctx, error=False, origctx=None):
455 for f in sorted(ctx.added() + ctx.modified()):
455 for f in sorted(ctx.added() + ctx.modified()):
456 if not self._eolmatch(f):
456 if not self._eolmatch(f):
457 continue
457 continue
458 fctx = ctx[f]
458 fctx = ctx[f]
459 if fctx is None:
459 if fctx is None:
460 continue
460 continue
461 data = fctx.data()
461 data = fctx.data()
462 if stringutil.binary(data):
462 if stringutil.binary(data):
463 # We should not abort here, since the user should
463 # We should not abort here, since the user should
464 # be able to say "** = native" to automatically
464 # be able to say "** = native" to automatically
465 # have all non-binary files taken care of.
465 # have all non-binary files taken care of.
466 continue
466 continue
467 if inconsistenteol(data):
467 if inconsistenteol(data):
468 raise errormod.Abort(
468 raise errormod.Abort(
469 _(b"inconsistent newline style in %s\n") % f
469 _(b"inconsistent newline style in %s\n") % f
470 )
470 )
471 return super(eolrepo, self).commitctx(ctx, error, origctx)
471 return super(eolrepo, self).commitctx(ctx, error, origctx)
472
472
473 repo.__class__ = eolrepo
473 repo.__class__ = eolrepo
474 repo._hgcleardirstate()
474 repo._hgcleardirstate()
@@ -1,111 +1,120 b''
1 Testing cloning with the EOL extension
1 Testing cloning with the EOL extension
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
4 > [extensions]
5 > eol =
5 > eol =
6 >
6 >
7 > [eol]
7 > [eol]
8 > native = CRLF
8 > native = CRLF
9 > EOF
9 > EOF
10
10
11 setup repository
11 setup repository
12
12
13 $ hg init repo
13 $ hg init repo
14 $ cd repo
14 $ cd repo
15 $ cat > .hgeol <<EOF
15 $ cat > .hgeol <<EOF
16 > [patterns]
16 > [patterns]
17 > **.txt = native
17 > **.txt = native
18 > EOF
18 > EOF
19 $ printf "first\r\nsecond\r\nthird\r\n" > a.txt
19 $ printf "first\r\nsecond\r\nthird\r\n" > a.txt
20 $ hg commit --addremove -m 'checkin'
20 $ hg commit --addremove -m 'checkin'
21 adding .hgeol
21 adding .hgeol
22 adding a.txt
22 adding a.txt
23
23
24 Test commit of removed .hgeol - currently it seems to live on as zombie
24 Test commit of removed .hgeol and how it immediately makes the automatic
25 (causing "filtering a.txt through tolf") after being removed ... but actually
25 changes explicit and committable.
26 it is just confusing use of tip revision.
27
26
28 $ cd ..
27 $ cd ..
29 $ hg clone repo repo-2
28 $ hg clone repo repo-2
30 updating to branch default
29 updating to branch default
31 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 $ cd repo-2
31 $ cd repo-2
33 $ cat a.txt
32 $ cat a.txt
34 first\r (esc)
33 first\r (esc)
35 second\r (esc)
34 second\r (esc)
36 third\r (esc)
35 third\r (esc)
37 $ hg cat a.txt
36 $ hg cat a.txt
38 first
37 first
39 second
38 second
40 third
39 third
41 $ hg remove .hgeol
40 $ hg remove .hgeol
42 $ touch a.txt * # ensure consistent st dirtyness checks, ignoring dirstate timing
41 $ touch a.txt * # ensure consistent st dirtyness checks, ignoring dirstate timing
43 $ hg st -v --debug
42 $ hg st -v --debug
44 filtering a.txt through tolf
43 M a.txt
45 R .hgeol
44 R .hgeol
46 $ hg commit -m 'remove eol'
45 $ hg commit -m 'remove eol'
47 $ hg exp
46 $ hg exp
48 # HG changeset patch
47 # HG changeset patch
49 # User test
48 # User test
50 # Date 0 0
49 # Date 0 0
51 # Thu Jan 01 00:00:00 1970 +0000
50 # Thu Jan 01 00:00:00 1970 +0000
52 # Node ID c60b96c20c7de8c821127b548c34e5b170bcf9fe
51 # Node ID 3c20c2d90333b6ecdc8f7aa8f9b73223c7c7a608
53 # Parent 90f94e2cf4e24628afddd641688dfe4cd476d6e4
52 # Parent 90f94e2cf4e24628afddd641688dfe4cd476d6e4
54 remove eol
53 remove eol
55
54
56 diff -r 90f94e2cf4e2 -r c60b96c20c7d .hgeol
55 diff -r 90f94e2cf4e2 -r 3c20c2d90333 .hgeol
57 --- a/.hgeol Thu Jan 01 00:00:00 1970 +0000
56 --- a/.hgeol Thu Jan 01 00:00:00 1970 +0000
58 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
57 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
59 @@ -1,2 +0,0 @@
58 @@ -1,2 +0,0 @@
60 -[patterns]
59 -[patterns]
61 -**.txt = native
60 -**.txt = native
61 diff -r 90f94e2cf4e2 -r 3c20c2d90333 a.txt
62 --- a/a.txt Thu Jan 01 00:00:00 1970 +0000
63 +++ b/a.txt Thu Jan 01 00:00:00 1970 +0000
64 @@ -1,3 +1,3 @@
65 -first
66 -second
67 -third
68 +first\r (esc)
69 +second\r (esc)
70 +third\r (esc)
62 $ hg push --quiet
71 $ hg push --quiet
63 $ cd ..
72 $ cd ..
64
73
65 Test clone of repo with .hgeol in working dir, but no .hgeol in default
74 Test clone of repo with .hgeol in working dir, but no .hgeol in default
66 checkout revision tip. The repo is correctly updated to be consistent and have
75 checkout revision tip. The repo is correctly updated to be consistent and have
67 the exact content checked out without filtering, ignoring the current .hgeol in
76 the exact content checked out without filtering, ignoring the current .hgeol in
68 the source repo:
77 the source repo:
69
78
70 $ cat repo/.hgeol
79 $ cat repo/.hgeol
71 [patterns]
80 [patterns]
72 **.txt = native
81 **.txt = native
73 $ hg clone repo repo-3 -v --debug
82 $ hg clone repo repo-3 -v --debug
74 linked 7 files
83 linked 7 files
75 updating to branch default
84 updating to branch default
76 resolving manifests
85 resolving manifests
77 branchmerge: False, force: False, partial: False
86 branchmerge: False, force: False, partial: False
78 ancestor: 000000000000, local: 000000000000+, remote: c60b96c20c7d
87 ancestor: 000000000000, local: 000000000000+, remote: 3c20c2d90333
79 calling hook preupdate.eol: hgext.eol.preupdate
88 calling hook preupdate.eol: hgext.eol.preupdate
80 a.txt: remote created -> g
89 a.txt: remote created -> g
81 getting a.txt
90 getting a.txt
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ cd repo-3
92 $ cd repo-3
84
93
85 $ cat a.txt
94 $ cat a.txt
86 first
95 first\r (esc)
87 second
96 second\r (esc)
88 third
97 third\r (esc)
89
98
90 Test clone of revision with .hgeol
99 Test clone of revision with .hgeol
91
100
92 $ cd ..
101 $ cd ..
93 $ hg clone -r 0 repo repo-4
102 $ hg clone -r 0 repo repo-4
94 adding changesets
103 adding changesets
95 adding manifests
104 adding manifests
96 adding file changes
105 adding file changes
97 added 1 changesets with 2 changes to 2 files
106 added 1 changesets with 2 changes to 2 files
98 new changesets 90f94e2cf4e2
107 new changesets 90f94e2cf4e2
99 updating to branch default
108 updating to branch default
100 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 $ cd repo-4
110 $ cd repo-4
102 $ cat .hgeol
111 $ cat .hgeol
103 [patterns]
112 [patterns]
104 **.txt = native
113 **.txt = native
105
114
106 $ cat a.txt
115 $ cat a.txt
107 first\r (esc)
116 first\r (esc)
108 second\r (esc)
117 second\r (esc)
109 third\r (esc)
118 third\r (esc)
110
119
111 $ cd ..
120 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now