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