##// END OF EJS Templates
eol: mention the hook in the module docstring
Martin Geisler -
r12980:20974e51 stable
parent child Browse files
Show More
@@ -1,261 +1,266
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 ``win32text.forbid*`` hooks provided by the win32text extension
65 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
67 you must migrate to a ``.hgeol`` file first before using the hook.
68
64 See :hg:`help patterns` for more information about the glob patterns
69 See :hg:`help patterns` for more information about the glob patterns
65 used.
70 used.
66 """
71 """
67
72
68 from mercurial.i18n import _
73 from mercurial.i18n import _
69 from mercurial import util, config, extensions, match
74 from mercurial import util, config, extensions, match
70 import re, os
75 import re, os
71
76
72 # Matches a lone LF, i.e., one that is not part of CRLF.
77 # Matches a lone LF, i.e., one that is not part of CRLF.
73 singlelf = re.compile('(^|[^\r])\n')
78 singlelf = re.compile('(^|[^\r])\n')
74 # Matches a single EOL which can either be a CRLF where repeated CR
79 # Matches a single EOL which can either be a CRLF where repeated CR
75 # are removed or a LF. We do not care about old Machintosh files, so a
80 # are removed or a LF. We do not care about old Machintosh files, so a
76 # stray CR is an error.
81 # stray CR is an error.
77 eolre = re.compile('\r*\n')
82 eolre = re.compile('\r*\n')
78
83
79
84
80 def inconsistenteol(data):
85 def inconsistenteol(data):
81 return '\r\n' in data and singlelf.search(data)
86 return '\r\n' in data and singlelf.search(data)
82
87
83 def tolf(s, params, ui, **kwargs):
88 def tolf(s, params, ui, **kwargs):
84 """Filter to convert to LF EOLs."""
89 """Filter to convert to LF EOLs."""
85 if util.binary(s):
90 if util.binary(s):
86 return s
91 return s
87 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
92 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
88 return s
93 return s
89 return eolre.sub('\n', s)
94 return eolre.sub('\n', s)
90
95
91 def tocrlf(s, params, ui, **kwargs):
96 def tocrlf(s, params, ui, **kwargs):
92 """Filter to convert to CRLF EOLs."""
97 """Filter to convert to CRLF EOLs."""
93 if util.binary(s):
98 if util.binary(s):
94 return s
99 return s
95 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
100 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
96 return s
101 return s
97 return eolre.sub('\r\n', s)
102 return eolre.sub('\r\n', s)
98
103
99 def isbinary(s, params):
104 def isbinary(s, params):
100 """Filter to do nothing with the file."""
105 """Filter to do nothing with the file."""
101 return s
106 return s
102
107
103 filters = {
108 filters = {
104 'to-lf': tolf,
109 'to-lf': tolf,
105 'to-crlf': tocrlf,
110 'to-crlf': tocrlf,
106 'is-binary': isbinary,
111 'is-binary': isbinary,
107 }
112 }
108
113
109
114
110 def hook(ui, repo, node, hooktype, **kwargs):
115 def hook(ui, repo, node, hooktype, **kwargs):
111 """verify that files have expected EOLs"""
116 """verify that files have expected EOLs"""
112 files = set()
117 files = set()
113 for rev in xrange(repo[node].rev(), len(repo)):
118 for rev in xrange(repo[node].rev(), len(repo)):
114 files.update(repo[rev].files())
119 files.update(repo[rev].files())
115 tip = repo['tip']
120 tip = repo['tip']
116 for f in files:
121 for f in files:
117 if f not in tip:
122 if f not in tip:
118 continue
123 continue
119 for pattern, target in ui.configitems('encode'):
124 for pattern, target in ui.configitems('encode'):
120 if match.match(repo.root, '', [pattern])(f):
125 if match.match(repo.root, '', [pattern])(f):
121 data = tip[f].data()
126 data = tip[f].data()
122 if target == "to-lf" and "\r\n" in data:
127 if target == "to-lf" and "\r\n" in data:
123 raise util.Abort(_("%s should not have CRLF line endings")
128 raise util.Abort(_("%s should not have CRLF line endings")
124 % f)
129 % f)
125 elif target == "to-crlf" and singlelf.search(data):
130 elif target == "to-crlf" and singlelf.search(data):
126 raise util.Abort(_("%s should not have LF line endings")
131 raise util.Abort(_("%s should not have LF line endings")
127 % f)
132 % f)
128
133
129
134
130 def preupdate(ui, repo, hooktype, parent1, parent2):
135 def preupdate(ui, repo, hooktype, parent1, parent2):
131 #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
136 #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
132 repo.readhgeol(parent1)
137 repo.readhgeol(parent1)
133 return False
138 return False
134
139
135 def uisetup(ui):
140 def uisetup(ui):
136 ui.setconfig('hooks', 'preupdate.eol', preupdate)
141 ui.setconfig('hooks', 'preupdate.eol', preupdate)
137
142
138 def extsetup(ui):
143 def extsetup(ui):
139 try:
144 try:
140 extensions.find('win32text')
145 extensions.find('win32text')
141 raise util.Abort(_("the eol extension is incompatible with the "
146 raise util.Abort(_("the eol extension is incompatible with the "
142 "win32text extension"))
147 "win32text extension"))
143 except KeyError:
148 except KeyError:
144 pass
149 pass
145
150
146
151
147 def reposetup(ui, repo):
152 def reposetup(ui, repo):
148 uisetup(repo.ui)
153 uisetup(repo.ui)
149 #print "reposetup for", repo.root
154 #print "reposetup for", repo.root
150
155
151 if not repo.local():
156 if not repo.local():
152 return
157 return
153 for name, fn in filters.iteritems():
158 for name, fn in filters.iteritems():
154 repo.adddatafilter(name, fn)
159 repo.adddatafilter(name, fn)
155
160
156 ui.setconfig('patch', 'eol', 'auto')
161 ui.setconfig('patch', 'eol', 'auto')
157
162
158 class eolrepo(repo.__class__):
163 class eolrepo(repo.__class__):
159
164
160 _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
165 _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
161 _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
166 _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
162
167
163 def readhgeol(self, node=None, data=None):
168 def readhgeol(self, node=None, data=None):
164 if data is None:
169 if data is None:
165 try:
170 try:
166 if node is None:
171 if node is None:
167 data = self.wfile('.hgeol').read()
172 data = self.wfile('.hgeol').read()
168 else:
173 else:
169 data = self[node]['.hgeol'].data()
174 data = self[node]['.hgeol'].data()
170 except (IOError, LookupError):
175 except (IOError, LookupError):
171 return None
176 return None
172
177
173 if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
178 if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
174 self._decode['NATIVE'] = 'to-lf'
179 self._decode['NATIVE'] = 'to-lf'
175 else:
180 else:
176 self._decode['NATIVE'] = 'to-crlf'
181 self._decode['NATIVE'] = 'to-crlf'
177
182
178 eol = config.config()
183 eol = config.config()
179 # Our files should not be touched. The pattern must be
184 # Our files should not be touched. The pattern must be
180 # inserted first override a '** = native' pattern.
185 # inserted first override a '** = native' pattern.
181 eol.set('patterns', '.hg*', 'BIN')
186 eol.set('patterns', '.hg*', 'BIN')
182 # We can then parse the user's patterns.
187 # We can then parse the user's patterns.
183 eol.parse('.hgeol', data)
188 eol.parse('.hgeol', data)
184
189
185 if eol.get('repository', 'native') == 'CRLF':
190 if eol.get('repository', 'native') == 'CRLF':
186 self._encode['NATIVE'] = 'to-crlf'
191 self._encode['NATIVE'] = 'to-crlf'
187 else:
192 else:
188 self._encode['NATIVE'] = 'to-lf'
193 self._encode['NATIVE'] = 'to-lf'
189
194
190 for pattern, style in eol.items('patterns'):
195 for pattern, style in eol.items('patterns'):
191 key = style.upper()
196 key = style.upper()
192 try:
197 try:
193 self.ui.setconfig('decode', pattern, self._decode[key])
198 self.ui.setconfig('decode', pattern, self._decode[key])
194 self.ui.setconfig('encode', pattern, self._encode[key])
199 self.ui.setconfig('encode', pattern, self._encode[key])
195 except KeyError:
200 except KeyError:
196 self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
201 self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
197 % (style, eol.source('patterns', pattern)))
202 % (style, eol.source('patterns', pattern)))
198
203
199 include = []
204 include = []
200 exclude = []
205 exclude = []
201 for pattern, style in eol.items('patterns'):
206 for pattern, style in eol.items('patterns'):
202 key = style.upper()
207 key = style.upper()
203 if key == 'BIN':
208 if key == 'BIN':
204 exclude.append(pattern)
209 exclude.append(pattern)
205 else:
210 else:
206 include.append(pattern)
211 include.append(pattern)
207
212
208 # This will match the files for which we need to care
213 # This will match the files for which we need to care
209 # about inconsistent newlines.
214 # about inconsistent newlines.
210 return match.match(self.root, '', [], include, exclude)
215 return match.match(self.root, '', [], include, exclude)
211
216
212 def _hgcleardirstate(self):
217 def _hgcleardirstate(self):
213 self._eolfile = self.readhgeol() or self.readhgeol('tip')
218 self._eolfile = self.readhgeol() or self.readhgeol('tip')
214
219
215 if not self._eolfile:
220 if not self._eolfile:
216 self._eolfile = util.never
221 self._eolfile = util.never
217 return
222 return
218
223
219 try:
224 try:
220 cachemtime = os.path.getmtime(self.join("eol.cache"))
225 cachemtime = os.path.getmtime(self.join("eol.cache"))
221 except OSError:
226 except OSError:
222 cachemtime = 0
227 cachemtime = 0
223
228
224 try:
229 try:
225 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
230 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
226 except OSError:
231 except OSError:
227 eolmtime = 0
232 eolmtime = 0
228
233
229 if eolmtime > cachemtime:
234 if eolmtime > cachemtime:
230 ui.debug("eol: detected change in .hgeol\n")
235 ui.debug("eol: detected change in .hgeol\n")
231 # TODO: we could introduce a method for this in dirstate.
236 # TODO: we could introduce a method for this in dirstate.
232 wlock = None
237 wlock = None
233 try:
238 try:
234 wlock = self.wlock()
239 wlock = self.wlock()
235 for f, e in self.dirstate._map.iteritems():
240 for f, e in self.dirstate._map.iteritems():
236 self.dirstate._map[f] = (e[0], e[1], -1, 0)
241 self.dirstate._map[f] = (e[0], e[1], -1, 0)
237 self.dirstate._dirty = True
242 self.dirstate._dirty = True
238 # Touch the cache to update mtime. TODO: are we sure this
243 # Touch the cache to update mtime. TODO: are we sure this
239 # always enought to update the mtime, or should we write a
244 # always enought to update the mtime, or should we write a
240 # bit to the file?
245 # bit to the file?
241 self.opener("eol.cache", "w").close()
246 self.opener("eol.cache", "w").close()
242 finally:
247 finally:
243 if wlock is not None:
248 if wlock is not None:
244 wlock.release()
249 wlock.release()
245
250
246 def commitctx(self, ctx, error=False):
251 def commitctx(self, ctx, error=False):
247 for f in sorted(ctx.added() + ctx.modified()):
252 for f in sorted(ctx.added() + ctx.modified()):
248 if not self._eolfile(f):
253 if not self._eolfile(f):
249 continue
254 continue
250 data = ctx[f].data()
255 data = ctx[f].data()
251 if util.binary(data):
256 if util.binary(data):
252 # We should not abort here, since the user should
257 # We should not abort here, since the user should
253 # be able to say "** = native" to automatically
258 # be able to say "** = native" to automatically
254 # have all non-binary files taken care of.
259 # have all non-binary files taken care of.
255 continue
260 continue
256 if inconsistenteol(data):
261 if inconsistenteol(data):
257 raise util.Abort(_("inconsistent newline style "
262 raise util.Abort(_("inconsistent newline style "
258 "in %s\n" % f))
263 "in %s\n" % f))
259 return super(eolrepo, self).commitctx(ctx, error)
264 return super(eolrepo, self).commitctx(ctx, error)
260 repo.__class__ = eolrepo
265 repo.__class__ = eolrepo
261 repo._hgcleardirstate()
266 repo._hgcleardirstate()
General Comments 0
You need to be logged in to leave comments. Login now