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 = '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 'eol', 'fix-trailing-newline', default=False, |
121 b'eol', b'fix-trailing-newline', default=False, |
122 ) |
122 ) |
123 configitem( |
123 configitem( |
124 'eol', 'native', default=pycompat.oslinesep, |
124 b'eol', b'native', default=pycompat.oslinesep, |
125 ) |
125 ) |
126 configitem( |
126 configitem( |
127 'eol', '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('(^|[^\r])\n') |
131 singlelf = re.compile(b'(^|[^\r])\n') |
132 |
132 |
133 |
133 |
134 def inconsistenteol(data): |
134 def inconsistenteol(data): |
135 return '\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('eol', '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('eol', 'fix-trailing-newline') |
145 ui.configbool(b'eol', b'fix-trailing-newline') |
146 and s |
146 and s |
147 and not s.endswith('\n') |
147 and not s.endswith(b'\n') |
148 ): |
148 ): |
149 s = s + '\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('eol', '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('eol', 'fix-trailing-newline') |
160 ui.configbool(b'eol', b'fix-trailing-newline') |
161 and s |
161 and s |
162 and not s.endswith('\n') |
162 and not s.endswith(b'\n') |
163 ): |
163 ): |
164 s = s + '\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): |
168 def isbinary(s, params): |
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 'to-lf': tolf, |
174 b'to-lf': tolf, |
175 'to-crlf': tocrlf, |
175 b'to-crlf': tocrlf, |
176 '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 'cleverencode:': tolf, |
178 b'cleverencode:': tolf, |
179 '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 = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} |
185 self._decode = { |
186 self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} |
186 b'LF': b'to-lf', |
|
187 b'CRLF': b'to-crlf', |
|
188 b'BIN': b'is-binary', |
|
189 } |
|
190 self._encode = { |
|
191 b'LF': b'to-lf', |
|
192 b'CRLF': b'to-crlf', |
|
193 b'BIN': b'is-binary', |
|
194 } |
187 |
195 |
188 self.cfg = config.config() |
196 self.cfg = config.config() |
189 # Our files should not be touched. The pattern must be |
197 # Our files should not be touched. The pattern must be |
190 # inserted first override a '** = native' pattern. |
198 # inserted first override a '** = native' pattern. |
191 self.cfg.set('patterns', '.hg*', 'BIN', 'eol') |
199 self.cfg.set(b'patterns', b'.hg*', b'BIN', b'eol') |
192 # We can then parse the user's patterns. |
200 # We can then parse the user's patterns. |
193 self.cfg.parse('.hgeol', data) |
201 self.cfg.parse(b'.hgeol', data) |
194 |
202 |
195 isrepolf = self.cfg.get('repository', 'native') != 'CRLF' |
203 isrepolf = self.cfg.get(b'repository', b'native') != b'CRLF' |
196 self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf' |
204 self._encode[b'NATIVE'] = isrepolf and b'to-lf' or b'to-crlf' |
197 iswdlf = ui.config('eol', 'native') in ('LF', '\n') |
205 iswdlf = ui.config(b'eol', b'native') in (b'LF', b'\n') |
198 self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf' |
206 self._decode[b'NATIVE'] = iswdlf and b'to-lf' or b'to-crlf' |
199 |
207 |
200 include = [] |
208 include = [] |
201 exclude = [] |
209 exclude = [] |
202 self.patterns = [] |
210 self.patterns = [] |
203 for pattern, style in self.cfg.items('patterns'): |
211 for pattern, style in self.cfg.items(b'patterns'): |
204 key = style.upper() |
212 key = style.upper() |
205 if key == 'BIN': |
213 if key == b'BIN': |
206 exclude.append(pattern) |
214 exclude.append(pattern) |
207 else: |
215 else: |
208 include.append(pattern) |
216 include.append(pattern) |
209 m = match.match(root, '', [pattern]) |
217 m = match.match(root, b'', [pattern]) |
210 self.patterns.append((pattern, key, m)) |
218 self.patterns.append((pattern, key, m)) |
211 # This will match the files for which we need to care |
219 # This will match the files for which we need to care |
212 # about inconsistent newlines. |
220 # about inconsistent newlines. |
213 self.match = match.match(root, '', [], include, exclude) |
221 self.match = match.match(root, b'', [], include, exclude) |
214 |
222 |
215 def copytoui(self, ui): |
223 def copytoui(self, ui): |
216 for pattern, key, m in self.patterns: |
224 for pattern, key, m in self.patterns: |
217 try: |
225 try: |
218 ui.setconfig('decode', pattern, self._decode[key], 'eol') |
226 ui.setconfig(b'decode', pattern, self._decode[key], b'eol') |
219 ui.setconfig('encode', pattern, self._encode[key], 'eol') |
227 ui.setconfig(b'encode', pattern, self._encode[key], b'eol') |
220 except KeyError: |
228 except KeyError: |
221 ui.warn( |
229 ui.warn( |
222 _("ignoring unknown EOL style '%s' from %s\n") |
230 _(b"ignoring unknown EOL style '%s' from %s\n") |
223 % (key, self.cfg.source('patterns', pattern)) |
231 % (key, self.cfg.source(b'patterns', pattern)) |
224 ) |
232 ) |
225 # eol.only-consistent can be specified in ~/.hgrc or .hgeol |
233 # eol.only-consistent can be specified in ~/.hgrc or .hgeol |
226 for k, v in self.cfg.items('eol'): |
234 for k, v in self.cfg.items(b'eol'): |
227 ui.setconfig('eol', k, v, 'eol') |
235 ui.setconfig(b'eol', k, v, b'eol') |
228 |
236 |
229 def checkrev(self, repo, ctx, files): |
237 def checkrev(self, repo, ctx, files): |
230 failed = [] |
238 failed = [] |
231 for f in files or ctx.files(): |
239 for f in files or ctx.files(): |
232 if f not in ctx: |
240 if f not in ctx: |
355 if not repo.local(): |
368 if not repo.local(): |
356 return |
369 return |
357 for name, fn in filters.iteritems(): |
370 for name, fn in filters.iteritems(): |
358 repo.adddatafilter(name, fn) |
371 repo.adddatafilter(name, fn) |
359 |
372 |
360 ui.setconfig('patch', 'eol', 'auto', 'eol') |
373 ui.setconfig(b'patch', b'eol', b'auto', b'eol') |
361 |
374 |
362 class eolrepo(repo.__class__): |
375 class eolrepo(repo.__class__): |
363 def loadeol(self, nodes): |
376 def loadeol(self, nodes): |
364 eol = parseeol(self.ui, self, nodes) |
377 eol = parseeol(self.ui, self, nodes) |
365 if eol is None: |
378 if eol is None: |
366 return None |
379 return None |
367 eol.copytoui(self.ui) |
380 eol.copytoui(self.ui) |
368 return eol.match |
381 return eol.match |
369 |
382 |
370 def _hgcleardirstate(self): |
383 def _hgcleardirstate(self): |
371 self._eolmatch = self.loadeol([None, 'tip']) |
384 self._eolmatch = self.loadeol([None, b'tip']) |
372 if not self._eolmatch: |
385 if not self._eolmatch: |
373 self._eolmatch = util.never |
386 self._eolmatch = util.never |
374 return |
387 return |
375 |
388 |
376 oldeol = None |
389 oldeol = None |
377 try: |
390 try: |
378 cachemtime = os.path.getmtime(self.vfs.join("eol.cache")) |
391 cachemtime = os.path.getmtime(self.vfs.join(b"eol.cache")) |
379 except OSError: |
392 except OSError: |
380 cachemtime = 0 |
393 cachemtime = 0 |
381 else: |
394 else: |
382 olddata = self.vfs.read("eol.cache") |
395 olddata = self.vfs.read(b"eol.cache") |
383 if olddata: |
396 if olddata: |
384 oldeol = eolfile(self.ui, self.root, olddata) |
397 oldeol = eolfile(self.ui, self.root, olddata) |
385 |
398 |
386 try: |
399 try: |
387 eolmtime = os.path.getmtime(self.wjoin(".hgeol")) |
400 eolmtime = os.path.getmtime(self.wjoin(b".hgeol")) |
388 except OSError: |
401 except OSError: |
389 eolmtime = 0 |
402 eolmtime = 0 |
390 |
403 |
391 if eolmtime > cachemtime: |
404 if eolmtime > cachemtime: |
392 self.ui.debug("eol: detected change in .hgeol\n") |
405 self.ui.debug(b"eol: detected change in .hgeol\n") |
393 |
406 |
394 hgeoldata = self.wvfs.read('.hgeol') |
407 hgeoldata = self.wvfs.read(b'.hgeol') |
395 neweol = eolfile(self.ui, self.root, hgeoldata) |
408 neweol = eolfile(self.ui, self.root, hgeoldata) |
396 |
409 |
397 wlock = None |
410 wlock = None |
398 try: |
411 try: |
399 wlock = self.wlock() |
412 wlock = self.wlock() |
400 for f in self.dirstate: |
413 for f in self.dirstate: |
401 if self.dirstate[f] != 'n': |
414 if self.dirstate[f] != b'n': |
402 continue |
415 continue |
403 if oldeol is not None: |
416 if oldeol is not None: |
404 if not oldeol.match(f) and not neweol.match(f): |
417 if not oldeol.match(f) and not neweol.match(f): |
405 continue |
418 continue |
406 oldkey = None |
419 oldkey = None |