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