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