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