##// END OF EJS Templates
merge with stable
Martin Geisler -
r12981:df7c2f81 merge default
parent child Browse files
Show More
@@ -1,269 +1,274 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 every time you run an ``hg`` command. The
9 configuration file every time you run an ``hg`` command. 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 the line endings used in the
14 The ``[patterns]`` section specifies the line endings used in the
15 working directory. The format is specified by a file pattern. The
15 working directory. The format is specified by a file pattern. The
16 first match is used, so put more specific patterns first. The
16 first match is used, so put more specific patterns first. The
17 available line endings are ``LF``, ``CRLF``, and ``BIN``.
17 available line endings are ``LF``, ``CRLF``, and ``BIN``.
18
18
19 Files with the declared format of ``CRLF`` or ``LF`` are always
19 Files with the declared format of ``CRLF`` or ``LF`` are always
20 checked out in that format and files declared to be binary (``BIN``)
20 checked out in that format and files declared to be binary (``BIN``)
21 are left unchanged. Additionally, ``native`` is an alias for the
21 are left unchanged. Additionally, ``native`` is an alias for the
22 platform's default line ending: ``LF`` on Unix (including Mac OS X)
22 platform's default line ending: ``LF`` on Unix (including Mac OS X)
23 and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line
23 and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line
24 endings) is Mercurial's default behaviour; it is only needed if you
24 endings) is Mercurial's default behaviour; it is only needed if you
25 need to override a later, more general pattern.
25 need to override a later, more general pattern.
26
26
27 The optional ``[repository]`` section specifies the line endings to
27 The optional ``[repository]`` section specifies the line endings to
28 use for files stored in the repository. It has a single setting,
28 use for files stored in the repository. It has a single setting,
29 ``native``, which determines the storage line endings for files
29 ``native``, which determines the storage line endings for files
30 declared as ``native`` in the ``[patterns]`` section. It can be set to
30 declared as ``native`` in the ``[patterns]`` section. It can be set to
31 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
31 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
32 that on Windows, files configured as ``native`` (``CRLF`` by default)
32 that on Windows, files configured as ``native`` (``CRLF`` by default)
33 will be converted to ``LF`` when stored in the repository. Files
33 will be converted to ``LF`` when stored in the repository. Files
34 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
34 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
35 are always stored as-is in the repository.
35 are always stored as-is in the repository.
36
36
37 Example versioned ``.hgeol`` file::
37 Example versioned ``.hgeol`` file::
38
38
39 [patterns]
39 [patterns]
40 **.py = native
40 **.py = native
41 **.vcproj = CRLF
41 **.vcproj = CRLF
42 **.txt = native
42 **.txt = native
43 Makefile = LF
43 Makefile = LF
44 **.jpg = BIN
44 **.jpg = BIN
45
45
46 [repository]
46 [repository]
47 native = LF
47 native = LF
48
48
49 The extension uses an optional ``[eol]`` section in your hgrc file
49 The extension uses an optional ``[eol]`` section in your hgrc file
50 (not the ``.hgeol`` file) for settings that control the overall
50 (not the ``.hgeol`` file) for settings that control the overall
51 behavior. There are two settings:
51 behavior. There are two settings:
52
52
53 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
53 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
54 ``CRLF`` to override the default interpretation of ``native`` for
54 ``CRLF`` to override the default interpretation of ``native`` for
55 checkout. This can be used with :hg:`archive` on Unix, say, to
55 checkout. This can be used with :hg:`archive` on Unix, say, to
56 generate an archive where files have line endings for Windows.
56 generate an archive where files have line endings for Windows.
57
57
58 - ``eol.only-consistent`` (default True) can be set to False to make
58 - ``eol.only-consistent`` (default True) can be set to False to make
59 the extension convert files with inconsistent EOLs. Inconsistent
59 the extension convert files with inconsistent EOLs. Inconsistent
60 means that there is both ``CRLF`` and ``LF`` present in the file.
60 means that there is both ``CRLF`` and ``LF`` present in the file.
61 Such files are normally not touched under the assumption that they
61 Such files are normally not touched under the assumption that they
62 have mixed EOLs on purpose.
62 have mixed EOLs on purpose.
63
63
64 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
64 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
65 like the deprecated win32text extension does. This means that you can
65 like the deprecated win32text extension does. This means that you can
66 disable win32text and enable eol and your filters will still work. You
66 disable win32text and enable eol and your filters will still work. You
67 only need to these filters until you have prepared a ``.hgeol`` file.
67 only need to these filters until you have prepared a ``.hgeol`` file.
68
68
69 The ``win32text.forbid*`` hooks provided by the win32text extension
70 have been unified into a single hook named ``eol.hook``. The hook will
71 lookup the expected line endings from the ``.hgeol`` file, which means
72 you must migrate to a ``.hgeol`` file first before using the hook.
73
69 See :hg:`help patterns` for more information about the glob patterns
74 See :hg:`help patterns` for more information about the glob patterns
70 used.
75 used.
71 """
76 """
72
77
73 from mercurial.i18n import _
78 from mercurial.i18n import _
74 from mercurial import util, config, extensions, match
79 from mercurial import util, config, extensions, match
75 import re, os
80 import re, os
76
81
77 # Matches a lone LF, i.e., one that is not part of CRLF.
82 # Matches a lone LF, i.e., one that is not part of CRLF.
78 singlelf = re.compile('(^|[^\r])\n')
83 singlelf = re.compile('(^|[^\r])\n')
79 # Matches a single EOL which can either be a CRLF where repeated CR
84 # Matches a single EOL which can either be a CRLF where repeated CR
80 # are removed or a LF. We do not care about old Machintosh files, so a
85 # are removed or a LF. We do not care about old Machintosh files, so a
81 # stray CR is an error.
86 # stray CR is an error.
82 eolre = re.compile('\r*\n')
87 eolre = re.compile('\r*\n')
83
88
84
89
85 def inconsistenteol(data):
90 def inconsistenteol(data):
86 return '\r\n' in data and singlelf.search(data)
91 return '\r\n' in data and singlelf.search(data)
87
92
88 def tolf(s, params, ui, **kwargs):
93 def tolf(s, params, ui, **kwargs):
89 """Filter to convert to LF EOLs."""
94 """Filter to convert to LF EOLs."""
90 if util.binary(s):
95 if util.binary(s):
91 return s
96 return s
92 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
97 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
93 return s
98 return s
94 return eolre.sub('\n', s)
99 return eolre.sub('\n', s)
95
100
96 def tocrlf(s, params, ui, **kwargs):
101 def tocrlf(s, params, ui, **kwargs):
97 """Filter to convert to CRLF EOLs."""
102 """Filter to convert to CRLF EOLs."""
98 if util.binary(s):
103 if util.binary(s):
99 return s
104 return s
100 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
105 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
101 return s
106 return s
102 return eolre.sub('\r\n', s)
107 return eolre.sub('\r\n', s)
103
108
104 def isbinary(s, params):
109 def isbinary(s, params):
105 """Filter to do nothing with the file."""
110 """Filter to do nothing with the file."""
106 return s
111 return s
107
112
108 filters = {
113 filters = {
109 'to-lf': tolf,
114 'to-lf': tolf,
110 'to-crlf': tocrlf,
115 'to-crlf': tocrlf,
111 'is-binary': isbinary,
116 'is-binary': isbinary,
112 # The following provide backwards compatibility with win32text
117 # The following provide backwards compatibility with win32text
113 'cleverencode:': tolf,
118 'cleverencode:': tolf,
114 'cleverdecode:': tocrlf
119 'cleverdecode:': tocrlf
115 }
120 }
116
121
117
122
118 def hook(ui, repo, node, hooktype, **kwargs):
123 def hook(ui, repo, node, hooktype, **kwargs):
119 """verify that files have expected EOLs"""
124 """verify that files have expected EOLs"""
120 files = set()
125 files = set()
121 for rev in xrange(repo[node].rev(), len(repo)):
126 for rev in xrange(repo[node].rev(), len(repo)):
122 files.update(repo[rev].files())
127 files.update(repo[rev].files())
123 tip = repo['tip']
128 tip = repo['tip']
124 for f in files:
129 for f in files:
125 if f not in tip:
130 if f not in tip:
126 continue
131 continue
127 for pattern, target in ui.configitems('encode'):
132 for pattern, target in ui.configitems('encode'):
128 if match.match(repo.root, '', [pattern])(f):
133 if match.match(repo.root, '', [pattern])(f):
129 data = tip[f].data()
134 data = tip[f].data()
130 if target == "to-lf" and "\r\n" in data:
135 if target == "to-lf" and "\r\n" in data:
131 raise util.Abort(_("%s should not have CRLF line endings")
136 raise util.Abort(_("%s should not have CRLF line endings")
132 % f)
137 % f)
133 elif target == "to-crlf" and singlelf.search(data):
138 elif target == "to-crlf" and singlelf.search(data):
134 raise util.Abort(_("%s should not have LF line endings")
139 raise util.Abort(_("%s should not have LF line endings")
135 % f)
140 % f)
136
141
137
142
138 def preupdate(ui, repo, hooktype, parent1, parent2):
143 def preupdate(ui, repo, hooktype, parent1, parent2):
139 #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
144 #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
140 repo.readhgeol(parent1)
145 repo.readhgeol(parent1)
141 return False
146 return False
142
147
143 def uisetup(ui):
148 def uisetup(ui):
144 ui.setconfig('hooks', 'preupdate.eol', preupdate)
149 ui.setconfig('hooks', 'preupdate.eol', preupdate)
145
150
146 def extsetup(ui):
151 def extsetup(ui):
147 try:
152 try:
148 extensions.find('win32text')
153 extensions.find('win32text')
149 raise util.Abort(_("the eol extension is incompatible with the "
154 raise util.Abort(_("the eol extension is incompatible with the "
150 "win32text extension"))
155 "win32text extension"))
151 except KeyError:
156 except KeyError:
152 pass
157 pass
153
158
154
159
155 def reposetup(ui, repo):
160 def reposetup(ui, repo):
156 uisetup(repo.ui)
161 uisetup(repo.ui)
157 #print "reposetup for", repo.root
162 #print "reposetup for", repo.root
158
163
159 if not repo.local():
164 if not repo.local():
160 return
165 return
161 for name, fn in filters.iteritems():
166 for name, fn in filters.iteritems():
162 repo.adddatafilter(name, fn)
167 repo.adddatafilter(name, fn)
163
168
164 ui.setconfig('patch', 'eol', 'auto')
169 ui.setconfig('patch', 'eol', 'auto')
165
170
166 class eolrepo(repo.__class__):
171 class eolrepo(repo.__class__):
167
172
168 _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
173 _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
169 _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
174 _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
170
175
171 def readhgeol(self, node=None, data=None):
176 def readhgeol(self, node=None, data=None):
172 if data is None:
177 if data is None:
173 try:
178 try:
174 if node is None:
179 if node is None:
175 data = self.wfile('.hgeol').read()
180 data = self.wfile('.hgeol').read()
176 else:
181 else:
177 data = self[node]['.hgeol'].data()
182 data = self[node]['.hgeol'].data()
178 except (IOError, LookupError):
183 except (IOError, LookupError):
179 return None
184 return None
180
185
181 if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
186 if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
182 self._decode['NATIVE'] = 'to-lf'
187 self._decode['NATIVE'] = 'to-lf'
183 else:
188 else:
184 self._decode['NATIVE'] = 'to-crlf'
189 self._decode['NATIVE'] = 'to-crlf'
185
190
186 eol = config.config()
191 eol = config.config()
187 # Our files should not be touched. The pattern must be
192 # Our files should not be touched. The pattern must be
188 # inserted first override a '** = native' pattern.
193 # inserted first override a '** = native' pattern.
189 eol.set('patterns', '.hg*', 'BIN')
194 eol.set('patterns', '.hg*', 'BIN')
190 # We can then parse the user's patterns.
195 # We can then parse the user's patterns.
191 eol.parse('.hgeol', data)
196 eol.parse('.hgeol', data)
192
197
193 if eol.get('repository', 'native') == 'CRLF':
198 if eol.get('repository', 'native') == 'CRLF':
194 self._encode['NATIVE'] = 'to-crlf'
199 self._encode['NATIVE'] = 'to-crlf'
195 else:
200 else:
196 self._encode['NATIVE'] = 'to-lf'
201 self._encode['NATIVE'] = 'to-lf'
197
202
198 for pattern, style in eol.items('patterns'):
203 for pattern, style in eol.items('patterns'):
199 key = style.upper()
204 key = style.upper()
200 try:
205 try:
201 self.ui.setconfig('decode', pattern, self._decode[key])
206 self.ui.setconfig('decode', pattern, self._decode[key])
202 self.ui.setconfig('encode', pattern, self._encode[key])
207 self.ui.setconfig('encode', pattern, self._encode[key])
203 except KeyError:
208 except KeyError:
204 self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
209 self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
205 % (style, eol.source('patterns', pattern)))
210 % (style, eol.source('patterns', pattern)))
206
211
207 include = []
212 include = []
208 exclude = []
213 exclude = []
209 for pattern, style in eol.items('patterns'):
214 for pattern, style in eol.items('patterns'):
210 key = style.upper()
215 key = style.upper()
211 if key == 'BIN':
216 if key == 'BIN':
212 exclude.append(pattern)
217 exclude.append(pattern)
213 else:
218 else:
214 include.append(pattern)
219 include.append(pattern)
215
220
216 # This will match the files for which we need to care
221 # This will match the files for which we need to care
217 # about inconsistent newlines.
222 # about inconsistent newlines.
218 return match.match(self.root, '', [], include, exclude)
223 return match.match(self.root, '', [], include, exclude)
219
224
220 def _hgcleardirstate(self):
225 def _hgcleardirstate(self):
221 self._eolfile = self.readhgeol() or self.readhgeol('tip')
226 self._eolfile = self.readhgeol() or self.readhgeol('tip')
222
227
223 if not self._eolfile:
228 if not self._eolfile:
224 self._eolfile = util.never
229 self._eolfile = util.never
225 return
230 return
226
231
227 try:
232 try:
228 cachemtime = os.path.getmtime(self.join("eol.cache"))
233 cachemtime = os.path.getmtime(self.join("eol.cache"))
229 except OSError:
234 except OSError:
230 cachemtime = 0
235 cachemtime = 0
231
236
232 try:
237 try:
233 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
238 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
234 except OSError:
239 except OSError:
235 eolmtime = 0
240 eolmtime = 0
236
241
237 if eolmtime > cachemtime:
242 if eolmtime > cachemtime:
238 ui.debug("eol: detected change in .hgeol\n")
243 ui.debug("eol: detected change in .hgeol\n")
239 # TODO: we could introduce a method for this in dirstate.
244 # TODO: we could introduce a method for this in dirstate.
240 wlock = None
245 wlock = None
241 try:
246 try:
242 wlock = self.wlock()
247 wlock = self.wlock()
243 for f, e in self.dirstate._map.iteritems():
248 for f, e in self.dirstate._map.iteritems():
244 self.dirstate._map[f] = (e[0], e[1], -1, 0)
249 self.dirstate._map[f] = (e[0], e[1], -1, 0)
245 self.dirstate._dirty = True
250 self.dirstate._dirty = True
246 # Touch the cache to update mtime. TODO: are we sure this
251 # Touch the cache to update mtime. TODO: are we sure this
247 # always enought to update the mtime, or should we write a
252 # always enought to update the mtime, or should we write a
248 # bit to the file?
253 # bit to the file?
249 self.opener("eol.cache", "w").close()
254 self.opener("eol.cache", "w").close()
250 finally:
255 finally:
251 if wlock is not None:
256 if wlock is not None:
252 wlock.release()
257 wlock.release()
253
258
254 def commitctx(self, ctx, error=False):
259 def commitctx(self, ctx, error=False):
255 for f in sorted(ctx.added() + ctx.modified()):
260 for f in sorted(ctx.added() + ctx.modified()):
256 if not self._eolfile(f):
261 if not self._eolfile(f):
257 continue
262 continue
258 data = ctx[f].data()
263 data = ctx[f].data()
259 if util.binary(data):
264 if util.binary(data):
260 # We should not abort here, since the user should
265 # We should not abort here, since the user should
261 # be able to say "** = native" to automatically
266 # be able to say "** = native" to automatically
262 # have all non-binary files taken care of.
267 # have all non-binary files taken care of.
263 continue
268 continue
264 if inconsistenteol(data):
269 if inconsistenteol(data):
265 raise util.Abort(_("inconsistent newline style "
270 raise util.Abort(_("inconsistent newline style "
266 "in %s\n" % f))
271 "in %s\n" % f))
267 return super(eolrepo, self).commitctx(ctx, error)
272 return super(eolrepo, self).commitctx(ctx, error)
268 repo.__class__ = eolrepo
273 repo.__class__ = eolrepo
269 repo._hgcleardirstate()
274 repo._hgcleardirstate()
General Comments 0
You need to be logged in to leave comments. Login now