##// END OF EJS Templates
dirstate: remove unused folding function
Matt Mackall -
r6745:ed01fa8c default
parent child Browse files
Show More
@@ -1,701 +1,698
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import nullid
10 from node import nullid
11 from i18n import _
11 from i18n import _
12 import struct, os, bisect, stat, strutil, util, errno, ignore
12 import struct, os, bisect, stat, strutil, util, errno, ignore
13 import cStringIO, osutil, sys
13 import cStringIO, osutil, sys
14
14
15 _unknown = ('?', 0, 0, 0)
15 _unknown = ('?', 0, 0, 0)
16 _format = ">cllll"
16 _format = ">cllll"
17
17
18 class dirstate(object):
18 class dirstate(object):
19
19
20 def __init__(self, opener, ui, root):
20 def __init__(self, opener, ui, root):
21 self._opener = opener
21 self._opener = opener
22 self._root = root
22 self._root = root
23 self._dirty = False
23 self._dirty = False
24 self._dirtypl = False
24 self._dirtypl = False
25 self._ui = ui
25 self._ui = ui
26
26
27 def __getattr__(self, name):
27 def __getattr__(self, name):
28 if name == '_map':
28 if name == '_map':
29 self._read()
29 self._read()
30 return self._map
30 return self._map
31 elif name == '_copymap':
31 elif name == '_copymap':
32 self._read()
32 self._read()
33 return self._copymap
33 return self._copymap
34 elif name == '_foldmap':
34 elif name == '_foldmap':
35 _foldmap = {}
35 _foldmap = {}
36 for name in self._map:
36 for name in self._map:
37 norm = os.path.normcase(os.path.normpath(name))
37 norm = os.path.normcase(os.path.normpath(name))
38 _foldmap[norm] = name
38 _foldmap[norm] = name
39 self._foldmap = _foldmap
39 self._foldmap = _foldmap
40 return self._foldmap
40 return self._foldmap
41 elif name == '_branch':
41 elif name == '_branch':
42 try:
42 try:
43 self._branch = (self._opener("branch").read().strip()
43 self._branch = (self._opener("branch").read().strip()
44 or "default")
44 or "default")
45 except IOError:
45 except IOError:
46 self._branch = "default"
46 self._branch = "default"
47 return self._branch
47 return self._branch
48 elif name == '_pl':
48 elif name == '_pl':
49 self._pl = [nullid, nullid]
49 self._pl = [nullid, nullid]
50 try:
50 try:
51 st = self._opener("dirstate").read(40)
51 st = self._opener("dirstate").read(40)
52 if len(st) == 40:
52 if len(st) == 40:
53 self._pl = st[:20], st[20:40]
53 self._pl = st[:20], st[20:40]
54 except IOError, err:
54 except IOError, err:
55 if err.errno != errno.ENOENT: raise
55 if err.errno != errno.ENOENT: raise
56 return self._pl
56 return self._pl
57 elif name == '_dirs':
57 elif name == '_dirs':
58 self._dirs = {}
58 self._dirs = {}
59 for f in self._map:
59 for f in self._map:
60 if self[f] != 'r':
60 if self[f] != 'r':
61 self._incpath(f)
61 self._incpath(f)
62 return self._dirs
62 return self._dirs
63 elif name == '_ignore':
63 elif name == '_ignore':
64 files = [self._join('.hgignore')]
64 files = [self._join('.hgignore')]
65 for name, path in self._ui.configitems("ui"):
65 for name, path in self._ui.configitems("ui"):
66 if name == 'ignore' or name.startswith('ignore.'):
66 if name == 'ignore' or name.startswith('ignore.'):
67 files.append(os.path.expanduser(path))
67 files.append(os.path.expanduser(path))
68 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
68 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
69 return self._ignore
69 return self._ignore
70 elif name == '_slash':
70 elif name == '_slash':
71 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
71 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
72 return self._slash
72 return self._slash
73 elif name == '_checklink':
73 elif name == '_checklink':
74 self._checklink = util.checklink(self._root)
74 self._checklink = util.checklink(self._root)
75 return self._checklink
75 return self._checklink
76 elif name == '_checkexec':
76 elif name == '_checkexec':
77 self._checkexec = util.checkexec(self._root)
77 self._checkexec = util.checkexec(self._root)
78 return self._checkexec
78 return self._checkexec
79 elif name == '_folding':
79 elif name == '_folding':
80 self._folding = not util.checkfolding(self._join('.hg'))
80 self._folding = not util.checkfolding(self._join('.hg'))
81 return self._folding
81 return self._folding
82 elif name == 'normalize':
82 elif name == 'normalize':
83 if self._folding:
83 if self._folding:
84 self.normalize = self._normalize
84 self.normalize = self._normalize
85 else:
85 else:
86 self.normalize = lambda x: x
86 self.normalize = lambda x: x
87 return self.normalize
87 return self.normalize
88 else:
88 else:
89 raise AttributeError, name
89 raise AttributeError, name
90
90
91 def _join(self, f):
91 def _join(self, f):
92 return os.path.join(self._root, f)
92 return os.path.join(self._root, f)
93
93
94 def folding(self):
95 return self._folding
96
97 def flagfunc(self, fallback):
94 def flagfunc(self, fallback):
98 if self._checklink:
95 if self._checklink:
99 if self._checkexec:
96 if self._checkexec:
100 def f(x):
97 def f(x):
101 p = os.path.join(self._root, x)
98 p = os.path.join(self._root, x)
102 if os.path.islink(p):
99 if os.path.islink(p):
103 return 'l'
100 return 'l'
104 if util.is_exec(p):
101 if util.is_exec(p):
105 return 'x'
102 return 'x'
106 return ''
103 return ''
107 return f
104 return f
108 def f(x):
105 def f(x):
109 if os.path.islink(os.path.join(self._root, x)):
106 if os.path.islink(os.path.join(self._root, x)):
110 return 'l'
107 return 'l'
111 if 'x' in fallback(x):
108 if 'x' in fallback(x):
112 return 'x'
109 return 'x'
113 return ''
110 return ''
114 return f
111 return f
115 if self._checkexec:
112 if self._checkexec:
116 def f(x):
113 def f(x):
117 if 'l' in fallback(x):
114 if 'l' in fallback(x):
118 return 'l'
115 return 'l'
119 if util.is_exec(os.path.join(self._root, x)):
116 if util.is_exec(os.path.join(self._root, x)):
120 return 'x'
117 return 'x'
121 return ''
118 return ''
122 return f
119 return f
123 return fallback
120 return fallback
124
121
125 def getcwd(self):
122 def getcwd(self):
126 cwd = os.getcwd()
123 cwd = os.getcwd()
127 if cwd == self._root: return ''
124 if cwd == self._root: return ''
128 # self._root ends with a path separator if self._root is '/' or 'C:\'
125 # self._root ends with a path separator if self._root is '/' or 'C:\'
129 rootsep = self._root
126 rootsep = self._root
130 if not util.endswithsep(rootsep):
127 if not util.endswithsep(rootsep):
131 rootsep += os.sep
128 rootsep += os.sep
132 if cwd.startswith(rootsep):
129 if cwd.startswith(rootsep):
133 return cwd[len(rootsep):]
130 return cwd[len(rootsep):]
134 else:
131 else:
135 # we're outside the repo. return an absolute path.
132 # we're outside the repo. return an absolute path.
136 return cwd
133 return cwd
137
134
138 def pathto(self, f, cwd=None):
135 def pathto(self, f, cwd=None):
139 if cwd is None:
136 if cwd is None:
140 cwd = self.getcwd()
137 cwd = self.getcwd()
141 path = util.pathto(self._root, cwd, f)
138 path = util.pathto(self._root, cwd, f)
142 if self._slash:
139 if self._slash:
143 return util.normpath(path)
140 return util.normpath(path)
144 return path
141 return path
145
142
146 def __getitem__(self, key):
143 def __getitem__(self, key):
147 ''' current states:
144 ''' current states:
148 n normal
145 n normal
149 m needs merging
146 m needs merging
150 r marked for removal
147 r marked for removal
151 a marked for addition
148 a marked for addition
152 ? not tracked'''
149 ? not tracked'''
153 return self._map.get(key, ("?",))[0]
150 return self._map.get(key, ("?",))[0]
154
151
155 def __contains__(self, key):
152 def __contains__(self, key):
156 return key in self._map
153 return key in self._map
157
154
158 def __iter__(self):
155 def __iter__(self):
159 a = self._map.keys()
156 a = self._map.keys()
160 a.sort()
157 a.sort()
161 for x in a:
158 for x in a:
162 yield x
159 yield x
163
160
164 def parents(self):
161 def parents(self):
165 return self._pl
162 return self._pl
166
163
167 def branch(self):
164 def branch(self):
168 return self._branch
165 return self._branch
169
166
170 def setparents(self, p1, p2=nullid):
167 def setparents(self, p1, p2=nullid):
171 self._dirty = self._dirtypl = True
168 self._dirty = self._dirtypl = True
172 self._pl = p1, p2
169 self._pl = p1, p2
173
170
174 def setbranch(self, branch):
171 def setbranch(self, branch):
175 self._branch = branch
172 self._branch = branch
176 self._opener("branch", "w").write(branch + '\n')
173 self._opener("branch", "w").write(branch + '\n')
177
174
178 def _read(self):
175 def _read(self):
179 self._map = {}
176 self._map = {}
180 self._copymap = {}
177 self._copymap = {}
181 if not self._dirtypl:
178 if not self._dirtypl:
182 self._pl = [nullid, nullid]
179 self._pl = [nullid, nullid]
183 try:
180 try:
184 st = self._opener("dirstate").read()
181 st = self._opener("dirstate").read()
185 except IOError, err:
182 except IOError, err:
186 if err.errno != errno.ENOENT: raise
183 if err.errno != errno.ENOENT: raise
187 return
184 return
188 if not st:
185 if not st:
189 return
186 return
190
187
191 if not self._dirtypl:
188 if not self._dirtypl:
192 self._pl = [st[:20], st[20: 40]]
189 self._pl = [st[:20], st[20: 40]]
193
190
194 # deref fields so they will be local in loop
191 # deref fields so they will be local in loop
195 dmap = self._map
192 dmap = self._map
196 copymap = self._copymap
193 copymap = self._copymap
197 unpack = struct.unpack
194 unpack = struct.unpack
198 e_size = struct.calcsize(_format)
195 e_size = struct.calcsize(_format)
199 pos1 = 40
196 pos1 = 40
200 l = len(st)
197 l = len(st)
201
198
202 # the inner loop
199 # the inner loop
203 while pos1 < l:
200 while pos1 < l:
204 pos2 = pos1 + e_size
201 pos2 = pos1 + e_size
205 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
202 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
206 pos1 = pos2 + e[4]
203 pos1 = pos2 + e[4]
207 f = st[pos2:pos1]
204 f = st[pos2:pos1]
208 if '\0' in f:
205 if '\0' in f:
209 f, c = f.split('\0')
206 f, c = f.split('\0')
210 copymap[f] = c
207 copymap[f] = c
211 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
208 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
212
209
213 def invalidate(self):
210 def invalidate(self):
214 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
211 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
215 if a in self.__dict__:
212 if a in self.__dict__:
216 delattr(self, a)
213 delattr(self, a)
217 self._dirty = False
214 self._dirty = False
218
215
219 def copy(self, source, dest):
216 def copy(self, source, dest):
220 if source == dest:
217 if source == dest:
221 return
218 return
222 self._dirty = True
219 self._dirty = True
223 self._copymap[dest] = source
220 self._copymap[dest] = source
224
221
225 def copied(self, file):
222 def copied(self, file):
226 return self._copymap.get(file, None)
223 return self._copymap.get(file, None)
227
224
228 def copies(self):
225 def copies(self):
229 return self._copymap
226 return self._copymap
230
227
231 def _incpath(self, path):
228 def _incpath(self, path):
232 c = path.rfind('/')
229 c = path.rfind('/')
233 if c >= 0:
230 if c >= 0:
234 dirs = self._dirs
231 dirs = self._dirs
235 base = path[:c]
232 base = path[:c]
236 if base not in dirs:
233 if base not in dirs:
237 self._incpath(base)
234 self._incpath(base)
238 dirs[base] = 1
235 dirs[base] = 1
239 else:
236 else:
240 dirs[base] += 1
237 dirs[base] += 1
241
238
242 def _decpath(self, path):
239 def _decpath(self, path):
243 c = path.rfind('/')
240 c = path.rfind('/')
244 if c >= 0:
241 if c >= 0:
245 base = path[:c]
242 base = path[:c]
246 dirs = self._dirs
243 dirs = self._dirs
247 if dirs[base] == 1:
244 if dirs[base] == 1:
248 del dirs[base]
245 del dirs[base]
249 self._decpath(base)
246 self._decpath(base)
250 else:
247 else:
251 dirs[base] -= 1
248 dirs[base] -= 1
252
249
253 def _incpathcheck(self, f):
250 def _incpathcheck(self, f):
254 if '\r' in f or '\n' in f:
251 if '\r' in f or '\n' in f:
255 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
252 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
256 % f)
253 % f)
257 # shadows
254 # shadows
258 if f in self._dirs:
255 if f in self._dirs:
259 raise util.Abort(_('directory %r already in dirstate') % f)
256 raise util.Abort(_('directory %r already in dirstate') % f)
260 for c in strutil.rfindall(f, '/'):
257 for c in strutil.rfindall(f, '/'):
261 d = f[:c]
258 d = f[:c]
262 if d in self._dirs:
259 if d in self._dirs:
263 break
260 break
264 if d in self._map and self[d] != 'r':
261 if d in self._map and self[d] != 'r':
265 raise util.Abort(_('file %r in dirstate clashes with %r') %
262 raise util.Abort(_('file %r in dirstate clashes with %r') %
266 (d, f))
263 (d, f))
267 self._incpath(f)
264 self._incpath(f)
268
265
269 def _changepath(self, f, newstate, relaxed=False):
266 def _changepath(self, f, newstate, relaxed=False):
270 # handle upcoming path changes
267 # handle upcoming path changes
271 oldstate = self[f]
268 oldstate = self[f]
272 if oldstate not in "?r" and newstate in "?r":
269 if oldstate not in "?r" and newstate in "?r":
273 if "_dirs" in self.__dict__:
270 if "_dirs" in self.__dict__:
274 self._decpath(f)
271 self._decpath(f)
275 return
272 return
276 if oldstate in "?r" and newstate not in "?r":
273 if oldstate in "?r" and newstate not in "?r":
277 if relaxed and oldstate == '?':
274 if relaxed and oldstate == '?':
278 # XXX
275 # XXX
279 # in relaxed mode we assume the caller knows
276 # in relaxed mode we assume the caller knows
280 # what it is doing, workaround for updating
277 # what it is doing, workaround for updating
281 # dir-to-file revisions
278 # dir-to-file revisions
282 if "_dirs" in self.__dict__:
279 if "_dirs" in self.__dict__:
283 self._incpath(f)
280 self._incpath(f)
284 return
281 return
285 self._incpathcheck(f)
282 self._incpathcheck(f)
286 return
283 return
287
284
288 def normal(self, f):
285 def normal(self, f):
289 'mark a file normal and clean'
286 'mark a file normal and clean'
290 self._dirty = True
287 self._dirty = True
291 self._changepath(f, 'n', True)
288 self._changepath(f, 'n', True)
292 s = os.lstat(self._join(f))
289 s = os.lstat(self._join(f))
293 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
290 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
294 if f in self._copymap:
291 if f in self._copymap:
295 del self._copymap[f]
292 del self._copymap[f]
296
293
297 def normallookup(self, f):
294 def normallookup(self, f):
298 'mark a file normal, but possibly dirty'
295 'mark a file normal, but possibly dirty'
299 if self._pl[1] != nullid and f in self._map:
296 if self._pl[1] != nullid and f in self._map:
300 # if there is a merge going on and the file was either
297 # if there is a merge going on and the file was either
301 # in state 'm' or dirty before being removed, restore that state.
298 # in state 'm' or dirty before being removed, restore that state.
302 entry = self._map[f]
299 entry = self._map[f]
303 if entry[0] == 'r' and entry[2] in (-1, -2):
300 if entry[0] == 'r' and entry[2] in (-1, -2):
304 source = self._copymap.get(f)
301 source = self._copymap.get(f)
305 if entry[2] == -1:
302 if entry[2] == -1:
306 self.merge(f)
303 self.merge(f)
307 elif entry[2] == -2:
304 elif entry[2] == -2:
308 self.normaldirty(f)
305 self.normaldirty(f)
309 if source:
306 if source:
310 self.copy(source, f)
307 self.copy(source, f)
311 return
308 return
312 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
309 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
313 return
310 return
314 self._dirty = True
311 self._dirty = True
315 self._changepath(f, 'n', True)
312 self._changepath(f, 'n', True)
316 self._map[f] = ('n', 0, -1, -1, 0)
313 self._map[f] = ('n', 0, -1, -1, 0)
317 if f in self._copymap:
314 if f in self._copymap:
318 del self._copymap[f]
315 del self._copymap[f]
319
316
320 def normaldirty(self, f):
317 def normaldirty(self, f):
321 'mark a file normal, but dirty'
318 'mark a file normal, but dirty'
322 self._dirty = True
319 self._dirty = True
323 self._changepath(f, 'n', True)
320 self._changepath(f, 'n', True)
324 self._map[f] = ('n', 0, -2, -1, 0)
321 self._map[f] = ('n', 0, -2, -1, 0)
325 if f in self._copymap:
322 if f in self._copymap:
326 del self._copymap[f]
323 del self._copymap[f]
327
324
328 def add(self, f):
325 def add(self, f):
329 'mark a file added'
326 'mark a file added'
330 self._dirty = True
327 self._dirty = True
331 self._changepath(f, 'a')
328 self._changepath(f, 'a')
332 self._map[f] = ('a', 0, -1, -1, 0)
329 self._map[f] = ('a', 0, -1, -1, 0)
333 if f in self._copymap:
330 if f in self._copymap:
334 del self._copymap[f]
331 del self._copymap[f]
335
332
336 def remove(self, f):
333 def remove(self, f):
337 'mark a file removed'
334 'mark a file removed'
338 self._dirty = True
335 self._dirty = True
339 self._changepath(f, 'r')
336 self._changepath(f, 'r')
340 size = 0
337 size = 0
341 if self._pl[1] != nullid and f in self._map:
338 if self._pl[1] != nullid and f in self._map:
342 entry = self._map[f]
339 entry = self._map[f]
343 if entry[0] == 'm':
340 if entry[0] == 'm':
344 size = -1
341 size = -1
345 elif entry[0] == 'n' and entry[2] == -2:
342 elif entry[0] == 'n' and entry[2] == -2:
346 size = -2
343 size = -2
347 self._map[f] = ('r', 0, size, 0, 0)
344 self._map[f] = ('r', 0, size, 0, 0)
348 if size == 0 and f in self._copymap:
345 if size == 0 and f in self._copymap:
349 del self._copymap[f]
346 del self._copymap[f]
350
347
351 def merge(self, f):
348 def merge(self, f):
352 'mark a file merged'
349 'mark a file merged'
353 self._dirty = True
350 self._dirty = True
354 s = os.lstat(self._join(f))
351 s = os.lstat(self._join(f))
355 self._changepath(f, 'm', True)
352 self._changepath(f, 'm', True)
356 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
353 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
357 if f in self._copymap:
354 if f in self._copymap:
358 del self._copymap[f]
355 del self._copymap[f]
359
356
360 def forget(self, f):
357 def forget(self, f):
361 'forget a file'
358 'forget a file'
362 self._dirty = True
359 self._dirty = True
363 try:
360 try:
364 self._changepath(f, '?')
361 self._changepath(f, '?')
365 del self._map[f]
362 del self._map[f]
366 except KeyError:
363 except KeyError:
367 self._ui.warn(_("not in dirstate: %s\n") % f)
364 self._ui.warn(_("not in dirstate: %s\n") % f)
368
365
369 def _normalize(self, path):
366 def _normalize(self, path):
370 normpath = os.path.normcase(os.path.normpath(path))
367 normpath = os.path.normcase(os.path.normpath(path))
371 if normpath in self._foldmap:
368 if normpath in self._foldmap:
372 return self._foldmap[normpath]
369 return self._foldmap[normpath]
373 elif os.path.exists(path):
370 elif os.path.exists(path):
374 self._foldmap[normpath] = util.fspath(path, self._root)
371 self._foldmap[normpath] = util.fspath(path, self._root)
375 return self._foldmap[normpath]
372 return self._foldmap[normpath]
376 else:
373 else:
377 return path
374 return path
378
375
379 def clear(self):
376 def clear(self):
380 self._map = {}
377 self._map = {}
381 if "_dirs" in self.__dict__:
378 if "_dirs" in self.__dict__:
382 delattr(self, "_dirs");
379 delattr(self, "_dirs");
383 self._copymap = {}
380 self._copymap = {}
384 self._pl = [nullid, nullid]
381 self._pl = [nullid, nullid]
385 self._dirty = True
382 self._dirty = True
386
383
387 def rebuild(self, parent, files):
384 def rebuild(self, parent, files):
388 self.clear()
385 self.clear()
389 for f in files:
386 for f in files:
390 if files.execf(f):
387 if files.execf(f):
391 self._map[f] = ('n', 0777, -1, 0, 0)
388 self._map[f] = ('n', 0777, -1, 0, 0)
392 else:
389 else:
393 self._map[f] = ('n', 0666, -1, 0, 0)
390 self._map[f] = ('n', 0666, -1, 0, 0)
394 self._pl = (parent, nullid)
391 self._pl = (parent, nullid)
395 self._dirty = True
392 self._dirty = True
396
393
397 def write(self):
394 def write(self):
398 if not self._dirty:
395 if not self._dirty:
399 return
396 return
400 st = self._opener("dirstate", "w", atomictemp=True)
397 st = self._opener("dirstate", "w", atomictemp=True)
401
398
402 try:
399 try:
403 gran = int(self._ui.config('dirstate', 'granularity', 1))
400 gran = int(self._ui.config('dirstate', 'granularity', 1))
404 except ValueError:
401 except ValueError:
405 gran = 1
402 gran = 1
406 limit = sys.maxint
403 limit = sys.maxint
407 if gran > 0:
404 if gran > 0:
408 limit = util.fstat(st).st_mtime - gran
405 limit = util.fstat(st).st_mtime - gran
409
406
410 cs = cStringIO.StringIO()
407 cs = cStringIO.StringIO()
411 copymap = self._copymap
408 copymap = self._copymap
412 pack = struct.pack
409 pack = struct.pack
413 write = cs.write
410 write = cs.write
414 write("".join(self._pl))
411 write("".join(self._pl))
415 for f, e in self._map.iteritems():
412 for f, e in self._map.iteritems():
416 if f in copymap:
413 if f in copymap:
417 f = "%s\0%s" % (f, copymap[f])
414 f = "%s\0%s" % (f, copymap[f])
418 if e[3] > limit and e[0] == 'n':
415 if e[3] > limit and e[0] == 'n':
419 e = (e[0], 0, -1, -1, 0)
416 e = (e[0], 0, -1, -1, 0)
420 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
417 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
421 write(e)
418 write(e)
422 write(f)
419 write(f)
423 st.write(cs.getvalue())
420 st.write(cs.getvalue())
424 st.rename()
421 st.rename()
425 self._dirty = self._dirtypl = False
422 self._dirty = self._dirtypl = False
426
423
427 def _filter(self, files):
424 def _filter(self, files):
428 ret = {}
425 ret = {}
429 unknown = []
426 unknown = []
430
427
431 for x in files:
428 for x in files:
432 if x == '.':
429 if x == '.':
433 return self._map.copy()
430 return self._map.copy()
434 if x not in self._map:
431 if x not in self._map:
435 unknown.append(x)
432 unknown.append(x)
436 else:
433 else:
437 ret[x] = self._map[x]
434 ret[x] = self._map[x]
438
435
439 if not unknown:
436 if not unknown:
440 return ret
437 return ret
441
438
442 b = self._map.keys()
439 b = self._map.keys()
443 b.sort()
440 b.sort()
444 blen = len(b)
441 blen = len(b)
445
442
446 for x in unknown:
443 for x in unknown:
447 bs = bisect.bisect(b, "%s%s" % (x, '/'))
444 bs = bisect.bisect(b, "%s%s" % (x, '/'))
448 while bs < blen:
445 while bs < blen:
449 s = b[bs]
446 s = b[bs]
450 if len(s) > len(x) and s.startswith(x):
447 if len(s) > len(x) and s.startswith(x):
451 ret[s] = self._map[s]
448 ret[s] = self._map[s]
452 else:
449 else:
453 break
450 break
454 bs += 1
451 bs += 1
455 return ret
452 return ret
456
453
457 def _supported(self, f, mode, verbose=False):
454 def _supported(self, f, mode, verbose=False):
458 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
455 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
459 return True
456 return True
460 if verbose:
457 if verbose:
461 kind = 'unknown'
458 kind = 'unknown'
462 if stat.S_ISCHR(mode): kind = _('character device')
459 if stat.S_ISCHR(mode): kind = _('character device')
463 elif stat.S_ISBLK(mode): kind = _('block device')
460 elif stat.S_ISBLK(mode): kind = _('block device')
464 elif stat.S_ISFIFO(mode): kind = _('fifo')
461 elif stat.S_ISFIFO(mode): kind = _('fifo')
465 elif stat.S_ISSOCK(mode): kind = _('socket')
462 elif stat.S_ISSOCK(mode): kind = _('socket')
466 elif stat.S_ISDIR(mode): kind = _('directory')
463 elif stat.S_ISDIR(mode): kind = _('directory')
467 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
464 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
468 % (self.pathto(f), kind))
465 % (self.pathto(f), kind))
469 return False
466 return False
470
467
471 def _dirignore(self, f):
468 def _dirignore(self, f):
472 if f == '.':
469 if f == '.':
473 return False
470 return False
474 if self._ignore(f):
471 if self._ignore(f):
475 return True
472 return True
476 for c in strutil.findall(f, '/'):
473 for c in strutil.findall(f, '/'):
477 if self._ignore(f[:c]):
474 if self._ignore(f[:c]):
478 return True
475 return True
479 return False
476 return False
480
477
481 def walk(self, match):
478 def walk(self, match):
482 # filter out the src and stat
479 # filter out the src and stat
483 for src, f, st in self.statwalk(match):
480 for src, f, st in self.statwalk(match):
484 yield f
481 yield f
485
482
486 def statwalk(self, match, unknown=True, ignored=False):
483 def statwalk(self, match, unknown=True, ignored=False):
487 '''
484 '''
488 walk recursively through the directory tree, finding all files
485 walk recursively through the directory tree, finding all files
489 matched by the match function
486 matched by the match function
490
487
491 results are yielded in a tuple (src, filename, st), where src
488 results are yielded in a tuple (src, filename, st), where src
492 is one of:
489 is one of:
493 'f' the file was found in the directory tree
490 'f' the file was found in the directory tree
494 'm' the file was only in the dirstate and not in the tree
491 'm' the file was only in the dirstate and not in the tree
495
492
496 and st is the stat result if the file was found in the directory.
493 and st is the stat result if the file was found in the directory.
497 '''
494 '''
498
495
499 def fwarn(f, msg):
496 def fwarn(f, msg):
500 self._ui.warn('%s: %s\n' % (self.pathto(ff), msg))
497 self._ui.warn('%s: %s\n' % (self.pathto(ff), msg))
501 return False
498 return False
502 badfn = fwarn
499 badfn = fwarn
503 if hasattr(match, 'bad'):
500 if hasattr(match, 'bad'):
504 badfn = match.bad
501 badfn = match.bad
505
502
506 # walk all files by default
503 # walk all files by default
507 files = match.files()
504 files = match.files()
508 if not files:
505 if not files:
509 files = ['.']
506 files = ['.']
510 dc = self._map.copy()
507 dc = self._map.copy()
511 else:
508 else:
512 files = util.unique(files)
509 files = util.unique(files)
513 dc = self._filter(files)
510 dc = self._filter(files)
514
511
515 def imatch(file_):
512 def imatch(file_):
516 if file_ not in dc and self._ignore(file_):
513 if file_ not in dc and self._ignore(file_):
517 return False
514 return False
518 return match(file_)
515 return match(file_)
519
516
520 # TODO: don't walk unknown directories if unknown and ignored are False
517 # TODO: don't walk unknown directories if unknown and ignored are False
521 ignore = self._ignore
518 ignore = self._ignore
522 dirignore = self._dirignore
519 dirignore = self._dirignore
523 if ignored:
520 if ignored:
524 imatch = match
521 imatch = match
525 ignore = util.never
522 ignore = util.never
526 dirignore = util.never
523 dirignore = util.never
527
524
528 # self._root may end with a path separator when self._root == '/'
525 # self._root may end with a path separator when self._root == '/'
529 common_prefix_len = len(self._root)
526 common_prefix_len = len(self._root)
530 if not util.endswithsep(self._root):
527 if not util.endswithsep(self._root):
531 common_prefix_len += 1
528 common_prefix_len += 1
532
529
533 normpath = util.normpath
530 normpath = util.normpath
534 listdir = osutil.listdir
531 listdir = osutil.listdir
535 lstat = os.lstat
532 lstat = os.lstat
536 bisect_left = bisect.bisect_left
533 bisect_left = bisect.bisect_left
537 isdir = os.path.isdir
534 isdir = os.path.isdir
538 pconvert = util.pconvert
535 pconvert = util.pconvert
539 join = os.path.join
536 join = os.path.join
540 s_isdir = stat.S_ISDIR
537 s_isdir = stat.S_ISDIR
541 supported = self._supported
538 supported = self._supported
542 _join = self._join
539 _join = self._join
543 known = {'.hg': 1}
540 known = {'.hg': 1}
544
541
545 # recursion free walker, faster than os.walk.
542 # recursion free walker, faster than os.walk.
546 def findfiles(s):
543 def findfiles(s):
547 work = [s]
544 work = [s]
548 wadd = work.append
545 wadd = work.append
549 found = []
546 found = []
550 add = found.append
547 add = found.append
551 if hasattr(match, 'dir'):
548 if hasattr(match, 'dir'):
552 match.dir(normpath(s[common_prefix_len:]))
549 match.dir(normpath(s[common_prefix_len:]))
553 while work:
550 while work:
554 top = work.pop()
551 top = work.pop()
555 entries = listdir(top, stat=True)
552 entries = listdir(top, stat=True)
556 # nd is the top of the repository dir tree
553 # nd is the top of the repository dir tree
557 nd = normpath(top[common_prefix_len:])
554 nd = normpath(top[common_prefix_len:])
558 if nd == '.':
555 if nd == '.':
559 nd = ''
556 nd = ''
560 else:
557 else:
561 # do not recurse into a repo contained in this
558 # do not recurse into a repo contained in this
562 # one. use bisect to find .hg directory so speed
559 # one. use bisect to find .hg directory so speed
563 # is good on big directory.
560 # is good on big directory.
564 names = [e[0] for e in entries]
561 names = [e[0] for e in entries]
565 hg = bisect_left(names, '.hg')
562 hg = bisect_left(names, '.hg')
566 if hg < len(names) and names[hg] == '.hg':
563 if hg < len(names) and names[hg] == '.hg':
567 if isdir(join(top, '.hg')):
564 if isdir(join(top, '.hg')):
568 continue
565 continue
569 for f, kind, st in entries:
566 for f, kind, st in entries:
570 np = pconvert(join(nd, f))
567 np = pconvert(join(nd, f))
571 if np in known:
568 if np in known:
572 continue
569 continue
573 known[np] = 1
570 known[np] = 1
574 p = join(top, f)
571 p = join(top, f)
575 # don't trip over symlinks
572 # don't trip over symlinks
576 if kind == stat.S_IFDIR:
573 if kind == stat.S_IFDIR:
577 if not ignore(np):
574 if not ignore(np):
578 wadd(p)
575 wadd(p)
579 if hasattr(match, 'dir'):
576 if hasattr(match, 'dir'):
580 match.dir(np)
577 match.dir(np)
581 if np in dc and match(np):
578 if np in dc and match(np):
582 add((np, 'm', st))
579 add((np, 'm', st))
583 elif imatch(np):
580 elif imatch(np):
584 if supported(np, st.st_mode):
581 if supported(np, st.st_mode):
585 add((np, 'f', st))
582 add((np, 'f', st))
586 elif np in dc:
583 elif np in dc:
587 add((np, 'm', st))
584 add((np, 'm', st))
588 found.sort()
585 found.sort()
589 return found
586 return found
590
587
591 # step one, find all files that match our criteria
588 # step one, find all files that match our criteria
592 files.sort()
589 files.sort()
593 for ff in files:
590 for ff in files:
594 nf = normpath(ff)
591 nf = normpath(ff)
595 f = _join(ff)
592 f = _join(ff)
596 try:
593 try:
597 st = lstat(f)
594 st = lstat(f)
598 except OSError, inst:
595 except OSError, inst:
599 found = False
596 found = False
600 for fn in dc:
597 for fn in dc:
601 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
598 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
602 found = True
599 found = True
603 break
600 break
604 if not found:
601 if not found:
605 if inst.errno != errno.ENOENT:
602 if inst.errno != errno.ENOENT:
606 fwarn(ff, inst.strerror)
603 fwarn(ff, inst.strerror)
607 elif badfn(ff, inst.strerror) and imatch(nf):
604 elif badfn(ff, inst.strerror) and imatch(nf):
608 yield 'f', ff, None
605 yield 'f', ff, None
609 continue
606 continue
610 if s_isdir(st.st_mode):
607 if s_isdir(st.st_mode):
611 if not dirignore(nf):
608 if not dirignore(nf):
612 for f, src, st in findfiles(f):
609 for f, src, st in findfiles(f):
613 yield src, f, st
610 yield src, f, st
614 else:
611 else:
615 if nf in known:
612 if nf in known:
616 continue
613 continue
617 known[nf] = 1
614 known[nf] = 1
618 if match(nf):
615 if match(nf):
619 if supported(ff, st.st_mode, verbose=True):
616 if supported(ff, st.st_mode, verbose=True):
620 yield 'f', self.normalize(nf), st
617 yield 'f', self.normalize(nf), st
621 elif ff in dc:
618 elif ff in dc:
622 yield 'm', nf, st
619 yield 'm', nf, st
623
620
624 # step two run through anything left in the dc hash and yield
621 # step two run through anything left in the dc hash and yield
625 # if we haven't already seen it
622 # if we haven't already seen it
626 ks = dc.keys()
623 ks = dc.keys()
627 ks.sort()
624 ks.sort()
628 for k in ks:
625 for k in ks:
629 if k in known:
626 if k in known:
630 continue
627 continue
631 known[k] = 1
628 known[k] = 1
632 if imatch(k):
629 if imatch(k):
633 yield 'm', k, None
630 yield 'm', k, None
634
631
635 def status(self, match, list_ignored, list_clean, list_unknown):
632 def status(self, match, list_ignored, list_clean, list_unknown):
636 lookup, modified, added, unknown, ignored = [], [], [], [], []
633 lookup, modified, added, unknown, ignored = [], [], [], [], []
637 removed, deleted, clean = [], [], []
634 removed, deleted, clean = [], [], []
638
635
639 _join = self._join
636 _join = self._join
640 lstat = os.lstat
637 lstat = os.lstat
641 cmap = self._copymap
638 cmap = self._copymap
642 dmap = self._map
639 dmap = self._map
643 ladd = lookup.append
640 ladd = lookup.append
644 madd = modified.append
641 madd = modified.append
645 aadd = added.append
642 aadd = added.append
646 uadd = unknown.append
643 uadd = unknown.append
647 iadd = ignored.append
644 iadd = ignored.append
648 radd = removed.append
645 radd = removed.append
649 dadd = deleted.append
646 dadd = deleted.append
650 cadd = clean.append
647 cadd = clean.append
651
648
652 for src, fn, st in self.statwalk(match, unknown=list_unknown,
649 for src, fn, st in self.statwalk(match, unknown=list_unknown,
653 ignored=list_ignored):
650 ignored=list_ignored):
654 if fn not in dmap:
651 if fn not in dmap:
655 if (list_ignored or match.exact(fn)) and self._dirignore(fn):
652 if (list_ignored or match.exact(fn)) and self._dirignore(fn):
656 if list_ignored:
653 if list_ignored:
657 iadd(fn)
654 iadd(fn)
658 elif list_unknown:
655 elif list_unknown:
659 uadd(fn)
656 uadd(fn)
660 continue
657 continue
661
658
662 state, mode, size, time, foo = dmap[fn]
659 state, mode, size, time, foo = dmap[fn]
663
660
664 if src == 'm':
661 if src == 'm':
665 nonexistent = True
662 nonexistent = True
666 if not st:
663 if not st:
667 try:
664 try:
668 st = lstat(_join(fn))
665 st = lstat(_join(fn))
669 except OSError, inst:
666 except OSError, inst:
670 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
667 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
671 raise
668 raise
672 st = None
669 st = None
673 # We need to re-check that it is a valid file
670 # We need to re-check that it is a valid file
674 if st and self._supported(fn, st.st_mode):
671 if st and self._supported(fn, st.st_mode):
675 nonexistent = False
672 nonexistent = False
676 if nonexistent and state in "nma":
673 if nonexistent and state in "nma":
677 dadd(fn)
674 dadd(fn)
678 continue
675 continue
679 # check the common case first
676 # check the common case first
680 if state == 'n':
677 if state == 'n':
681 if not st:
678 if not st:
682 st = lstat(_join(fn))
679 st = lstat(_join(fn))
683 if (size >= 0 and
680 if (size >= 0 and
684 (size != st.st_size
681 (size != st.st_size
685 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
682 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
686 or size == -2
683 or size == -2
687 or fn in self._copymap):
684 or fn in self._copymap):
688 madd(fn)
685 madd(fn)
689 elif time != int(st.st_mtime):
686 elif time != int(st.st_mtime):
690 ladd(fn)
687 ladd(fn)
691 elif list_clean:
688 elif list_clean:
692 cadd(fn)
689 cadd(fn)
693 elif state == 'm':
690 elif state == 'm':
694 madd(fn)
691 madd(fn)
695 elif state == 'a':
692 elif state == 'a':
696 aadd(fn)
693 aadd(fn)
697 elif state == 'r':
694 elif state == 'r':
698 radd(fn)
695 radd(fn)
699
696
700 return (lookup, modified, added, removed, deleted, unknown, ignored,
697 return (lookup, modified, added, removed, deleted, unknown, ignored,
701 clean)
698 clean)
General Comments 0
You need to be logged in to leave comments. Login now