##// END OF EJS Templates
Use atomictemp files to write the dirstate....
Alexis S. L. Carvalho -
r4507:289ec1f3 default
parent child Browse files
Show More
@@ -1,592 +1,593 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 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 *
10 from node import *
11 from i18n import _
11 from i18n import _
12 import struct, os, time, bisect, stat, strutil, util, re, errno
12 import struct, os, time, bisect, stat, strutil, util, re, errno
13 import cStringIO
13 import cStringIO
14
14
15 class dirstate(object):
15 class dirstate(object):
16 format = ">cllll"
16 format = ">cllll"
17
17
18 def __init__(self, opener, ui, root):
18 def __init__(self, opener, ui, root):
19 self.opener = opener
19 self.opener = opener
20 self.root = root
20 self.root = root
21 self.dirty = 0
21 self.dirty = 0
22 self.ui = ui
22 self.ui = ui
23 self.map = None
23 self.map = None
24 self.fp = None
24 self.fp = None
25 self.pl = None
25 self.pl = None
26 self.dirs = None
26 self.dirs = None
27 self.copymap = {}
27 self.copymap = {}
28 self.ignorefunc = None
28 self.ignorefunc = None
29 self._branch = None
29 self._branch = None
30
30
31 def wjoin(self, f):
31 def wjoin(self, f):
32 return os.path.join(self.root, f)
32 return os.path.join(self.root, f)
33
33
34 def getcwd(self):
34 def getcwd(self):
35 cwd = os.getcwd()
35 cwd = os.getcwd()
36 if cwd == self.root: return ''
36 if cwd == self.root: return ''
37 # self.root ends with a path separator if self.root is '/' or 'C:\'
37 # self.root ends with a path separator if self.root is '/' or 'C:\'
38 rootsep = self.root
38 rootsep = self.root
39 if not rootsep.endswith(os.sep):
39 if not rootsep.endswith(os.sep):
40 rootsep += os.sep
40 rootsep += os.sep
41 if cwd.startswith(rootsep):
41 if cwd.startswith(rootsep):
42 return cwd[len(rootsep):]
42 return cwd[len(rootsep):]
43 else:
43 else:
44 # we're outside the repo. return an absolute path.
44 # we're outside the repo. return an absolute path.
45 return cwd
45 return cwd
46
46
47 def hgignore(self):
47 def hgignore(self):
48 '''return the contents of .hgignore files as a list of patterns.
48 '''return the contents of .hgignore files as a list of patterns.
49
49
50 the files parsed for patterns include:
50 the files parsed for patterns include:
51 .hgignore in the repository root
51 .hgignore in the repository root
52 any additional files specified in the [ui] section of ~/.hgrc
52 any additional files specified in the [ui] section of ~/.hgrc
53
53
54 trailing white space is dropped.
54 trailing white space is dropped.
55 the escape character is backslash.
55 the escape character is backslash.
56 comments start with #.
56 comments start with #.
57 empty lines are skipped.
57 empty lines are skipped.
58
58
59 lines can be of the following formats:
59 lines can be of the following formats:
60
60
61 syntax: regexp # defaults following lines to non-rooted regexps
61 syntax: regexp # defaults following lines to non-rooted regexps
62 syntax: glob # defaults following lines to non-rooted globs
62 syntax: glob # defaults following lines to non-rooted globs
63 re:pattern # non-rooted regular expression
63 re:pattern # non-rooted regular expression
64 glob:pattern # non-rooted glob
64 glob:pattern # non-rooted glob
65 pattern # pattern of the current default type'''
65 pattern # pattern of the current default type'''
66 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
66 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
67 def parselines(fp):
67 def parselines(fp):
68 for line in fp:
68 for line in fp:
69 if not line.endswith('\n'):
69 if not line.endswith('\n'):
70 line += '\n'
70 line += '\n'
71 escape = False
71 escape = False
72 for i in xrange(len(line)):
72 for i in xrange(len(line)):
73 if escape: escape = False
73 if escape: escape = False
74 elif line[i] == '\\': escape = True
74 elif line[i] == '\\': escape = True
75 elif line[i] == '#': break
75 elif line[i] == '#': break
76 line = line[:i].rstrip()
76 line = line[:i].rstrip()
77 if line: yield line
77 if line: yield line
78 repoignore = self.wjoin('.hgignore')
78 repoignore = self.wjoin('.hgignore')
79 files = [repoignore]
79 files = [repoignore]
80 files.extend(self.ui.hgignorefiles())
80 files.extend(self.ui.hgignorefiles())
81 pats = {}
81 pats = {}
82 for f in files:
82 for f in files:
83 try:
83 try:
84 pats[f] = []
84 pats[f] = []
85 fp = open(f)
85 fp = open(f)
86 syntax = 'relre:'
86 syntax = 'relre:'
87 for line in parselines(fp):
87 for line in parselines(fp):
88 if line.startswith('syntax:'):
88 if line.startswith('syntax:'):
89 s = line[7:].strip()
89 s = line[7:].strip()
90 try:
90 try:
91 syntax = syntaxes[s]
91 syntax = syntaxes[s]
92 except KeyError:
92 except KeyError:
93 self.ui.warn(_("%s: ignoring invalid "
93 self.ui.warn(_("%s: ignoring invalid "
94 "syntax '%s'\n") % (f, s))
94 "syntax '%s'\n") % (f, s))
95 continue
95 continue
96 pat = syntax + line
96 pat = syntax + line
97 for s in syntaxes.values():
97 for s in syntaxes.values():
98 if line.startswith(s):
98 if line.startswith(s):
99 pat = line
99 pat = line
100 break
100 break
101 pats[f].append(pat)
101 pats[f].append(pat)
102 except IOError, inst:
102 except IOError, inst:
103 if f != repoignore:
103 if f != repoignore:
104 self.ui.warn(_("skipping unreadable ignore file"
104 self.ui.warn(_("skipping unreadable ignore file"
105 " '%s': %s\n") % (f, inst.strerror))
105 " '%s': %s\n") % (f, inst.strerror))
106 return pats
106 return pats
107
107
108 def ignore(self, fn):
108 def ignore(self, fn):
109 '''default match function used by dirstate and
109 '''default match function used by dirstate and
110 localrepository. this honours the repository .hgignore file
110 localrepository. this honours the repository .hgignore file
111 and any other files specified in the [ui] section of .hgrc.'''
111 and any other files specified in the [ui] section of .hgrc.'''
112 if not self.ignorefunc:
112 if not self.ignorefunc:
113 ignore = self.hgignore()
113 ignore = self.hgignore()
114 allpats = []
114 allpats = []
115 [allpats.extend(patlist) for patlist in ignore.values()]
115 [allpats.extend(patlist) for patlist in ignore.values()]
116 if allpats:
116 if allpats:
117 try:
117 try:
118 files, self.ignorefunc, anypats = (
118 files, self.ignorefunc, anypats = (
119 util.matcher(self.root, inc=allpats, src='.hgignore'))
119 util.matcher(self.root, inc=allpats, src='.hgignore'))
120 except util.Abort:
120 except util.Abort:
121 # Re-raise an exception where the src is the right file
121 # Re-raise an exception where the src is the right file
122 for f, patlist in ignore.items():
122 for f, patlist in ignore.items():
123 files, self.ignorefunc, anypats = (
123 files, self.ignorefunc, anypats = (
124 util.matcher(self.root, inc=patlist, src=f))
124 util.matcher(self.root, inc=patlist, src=f))
125 else:
125 else:
126 self.ignorefunc = util.never
126 self.ignorefunc = util.never
127 return self.ignorefunc(fn)
127 return self.ignorefunc(fn)
128
128
129 def __del__(self):
129 def __del__(self):
130 if self.dirty:
130 if self.dirty:
131 self.write()
131 self.write()
132
132
133 def __getitem__(self, key):
133 def __getitem__(self, key):
134 try:
134 try:
135 return self.map[key]
135 return self.map[key]
136 except TypeError:
136 except TypeError:
137 self.lazyread()
137 self.lazyread()
138 return self[key]
138 return self[key]
139
139
140 _unknown = ('?', 0, 0, 0)
140 _unknown = ('?', 0, 0, 0)
141
141
142 def get(self, key):
142 def get(self, key):
143 try:
143 try:
144 return self[key]
144 return self[key]
145 except KeyError:
145 except KeyError:
146 return self._unknown
146 return self._unknown
147
147
148 def __contains__(self, key):
148 def __contains__(self, key):
149 self.lazyread()
149 self.lazyread()
150 return key in self.map
150 return key in self.map
151
151
152 def parents(self):
152 def parents(self):
153 if self.pl is None:
153 if self.pl is None:
154 self.pl = [nullid, nullid]
154 self.pl = [nullid, nullid]
155 try:
155 try:
156 self.fp = self.opener('dirstate')
156 self.fp = self.opener('dirstate')
157 st = self.fp.read(40)
157 st = self.fp.read(40)
158 if len(st) == 40:
158 if len(st) == 40:
159 self.pl = st[:20], st[20:40]
159 self.pl = st[:20], st[20:40]
160 except IOError, err:
160 except IOError, err:
161 if err.errno != errno.ENOENT: raise
161 if err.errno != errno.ENOENT: raise
162 return self.pl
162 return self.pl
163
163
164 def branch(self):
164 def branch(self):
165 if not self._branch:
165 if not self._branch:
166 try:
166 try:
167 self._branch = self.opener("branch").read().strip()\
167 self._branch = self.opener("branch").read().strip()\
168 or "default"
168 or "default"
169 except IOError:
169 except IOError:
170 self._branch = "default"
170 self._branch = "default"
171 return self._branch
171 return self._branch
172
172
173 def markdirty(self):
173 def markdirty(self):
174 if not self.dirty:
174 if not self.dirty:
175 self.dirty = 1
175 self.dirty = 1
176
176
177 def setparents(self, p1, p2=nullid):
177 def setparents(self, p1, p2=nullid):
178 self.lazyread()
178 self.lazyread()
179 self.markdirty()
179 self.markdirty()
180 self.pl = p1, p2
180 self.pl = p1, p2
181
181
182 def setbranch(self, branch):
182 def setbranch(self, branch):
183 self._branch = branch
183 self._branch = branch
184 self.opener("branch", "w").write(branch + '\n')
184 self.opener("branch", "w").write(branch + '\n')
185
185
186 def state(self, key):
186 def state(self, key):
187 try:
187 try:
188 return self[key][0]
188 return self[key][0]
189 except KeyError:
189 except KeyError:
190 return "?"
190 return "?"
191
191
192 def lazyread(self):
192 def lazyread(self):
193 if self.map is None:
193 if self.map is None:
194 self.read()
194 self.read()
195
195
196 def parse(self, st):
196 def parse(self, st):
197 self.pl = [st[:20], st[20: 40]]
197 self.pl = [st[:20], st[20: 40]]
198
198
199 # deref fields so they will be local in loop
199 # deref fields so they will be local in loop
200 map = self.map
200 map = self.map
201 copymap = self.copymap
201 copymap = self.copymap
202 format = self.format
202 format = self.format
203 unpack = struct.unpack
203 unpack = struct.unpack
204
204
205 pos = 40
205 pos = 40
206 e_size = struct.calcsize(format)
206 e_size = struct.calcsize(format)
207
207
208 while pos < len(st):
208 while pos < len(st):
209 newpos = pos + e_size
209 newpos = pos + e_size
210 e = unpack(format, st[pos:newpos])
210 e = unpack(format, st[pos:newpos])
211 l = e[4]
211 l = e[4]
212 pos = newpos
212 pos = newpos
213 newpos = pos + l
213 newpos = pos + l
214 f = st[pos:newpos]
214 f = st[pos:newpos]
215 if '\0' in f:
215 if '\0' in f:
216 f, c = f.split('\0')
216 f, c = f.split('\0')
217 copymap[f] = c
217 copymap[f] = c
218 map[f] = e[:4]
218 map[f] = e[:4]
219 pos = newpos
219 pos = newpos
220
220
221 def read(self):
221 def read(self):
222 self.map = {}
222 self.map = {}
223 self.pl = [nullid, nullid]
223 self.pl = [nullid, nullid]
224 try:
224 try:
225 if self.fp:
225 if self.fp:
226 self.fp.seek(0)
226 self.fp.seek(0)
227 st = self.fp.read()
227 st = self.fp.read()
228 self.fp = None
228 self.fp = None
229 else:
229 else:
230 st = self.opener("dirstate").read()
230 st = self.opener("dirstate").read()
231 if st:
231 if st:
232 self.parse(st)
232 self.parse(st)
233 except IOError, err:
233 except IOError, err:
234 if err.errno != errno.ENOENT: raise
234 if err.errno != errno.ENOENT: raise
235
235
236 def reload(self):
236 def reload(self):
237 def mtime():
237 def mtime():
238 m = self.map and self.map.get('.hgignore')
238 m = self.map and self.map.get('.hgignore')
239 return m and m[-1]
239 return m and m[-1]
240
240
241 old_mtime = self.ignorefunc and mtime()
241 old_mtime = self.ignorefunc and mtime()
242 self.read()
242 self.read()
243 if old_mtime != mtime():
243 if old_mtime != mtime():
244 self.ignorefunc = None
244 self.ignorefunc = None
245
245
246 def copy(self, source, dest):
246 def copy(self, source, dest):
247 self.lazyread()
247 self.lazyread()
248 self.markdirty()
248 self.markdirty()
249 self.copymap[dest] = source
249 self.copymap[dest] = source
250
250
251 def copied(self, file):
251 def copied(self, file):
252 return self.copymap.get(file, None)
252 return self.copymap.get(file, None)
253
253
254 def copies(self):
254 def copies(self):
255 return self.copymap
255 return self.copymap
256
256
257 def initdirs(self):
257 def initdirs(self):
258 if self.dirs is None:
258 if self.dirs is None:
259 self.dirs = {}
259 self.dirs = {}
260 for f in self.map:
260 for f in self.map:
261 self.updatedirs(f, 1)
261 self.updatedirs(f, 1)
262
262
263 def updatedirs(self, path, delta):
263 def updatedirs(self, path, delta):
264 if self.dirs is not None:
264 if self.dirs is not None:
265 for c in strutil.findall(path, '/'):
265 for c in strutil.findall(path, '/'):
266 pc = path[:c]
266 pc = path[:c]
267 self.dirs.setdefault(pc, 0)
267 self.dirs.setdefault(pc, 0)
268 self.dirs[pc] += delta
268 self.dirs[pc] += delta
269
269
270 def checkinterfering(self, files):
270 def checkinterfering(self, files):
271 def prefixes(f):
271 def prefixes(f):
272 for c in strutil.rfindall(f, '/'):
272 for c in strutil.rfindall(f, '/'):
273 yield f[:c]
273 yield f[:c]
274 self.lazyread()
274 self.lazyread()
275 self.initdirs()
275 self.initdirs()
276 seendirs = {}
276 seendirs = {}
277 for f in files:
277 for f in files:
278 # shadows
278 # shadows
279 if self.dirs.get(f):
279 if self.dirs.get(f):
280 raise util.Abort(_('directory named %r already in dirstate') %
280 raise util.Abort(_('directory named %r already in dirstate') %
281 f)
281 f)
282 for d in prefixes(f):
282 for d in prefixes(f):
283 if d in seendirs:
283 if d in seendirs:
284 break
284 break
285 if d in self.map:
285 if d in self.map:
286 raise util.Abort(_('file named %r already in dirstate') %
286 raise util.Abort(_('file named %r already in dirstate') %
287 d)
287 d)
288 seendirs[d] = True
288 seendirs[d] = True
289 # disallowed
289 # disallowed
290 if '\r' in f or '\n' in f:
290 if '\r' in f or '\n' in f:
291 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
291 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
292
292
293 def update(self, files, state, **kw):
293 def update(self, files, state, **kw):
294 ''' current states:
294 ''' current states:
295 n normal
295 n normal
296 m needs merging
296 m needs merging
297 r marked for removal
297 r marked for removal
298 a marked for addition'''
298 a marked for addition'''
299
299
300 if not files: return
300 if not files: return
301 self.lazyread()
301 self.lazyread()
302 self.markdirty()
302 self.markdirty()
303 if state == "a":
303 if state == "a":
304 self.initdirs()
304 self.initdirs()
305 self.checkinterfering(files)
305 self.checkinterfering(files)
306 for f in files:
306 for f in files:
307 if state == "r":
307 if state == "r":
308 self.map[f] = ('r', 0, 0, 0)
308 self.map[f] = ('r', 0, 0, 0)
309 self.updatedirs(f, -1)
309 self.updatedirs(f, -1)
310 else:
310 else:
311 if state == "a":
311 if state == "a":
312 self.updatedirs(f, 1)
312 self.updatedirs(f, 1)
313 s = os.lstat(self.wjoin(f))
313 s = os.lstat(self.wjoin(f))
314 st_size = kw.get('st_size', s.st_size)
314 st_size = kw.get('st_size', s.st_size)
315 st_mtime = kw.get('st_mtime', s.st_mtime)
315 st_mtime = kw.get('st_mtime', s.st_mtime)
316 self.map[f] = (state, s.st_mode, st_size, st_mtime)
316 self.map[f] = (state, s.st_mode, st_size, st_mtime)
317 if self.copymap.has_key(f):
317 if self.copymap.has_key(f):
318 del self.copymap[f]
318 del self.copymap[f]
319
319
320 def forget(self, files):
320 def forget(self, files):
321 if not files: return
321 if not files: return
322 self.lazyread()
322 self.lazyread()
323 self.markdirty()
323 self.markdirty()
324 self.initdirs()
324 self.initdirs()
325 for f in files:
325 for f in files:
326 try:
326 try:
327 del self.map[f]
327 del self.map[f]
328 self.updatedirs(f, -1)
328 self.updatedirs(f, -1)
329 except KeyError:
329 except KeyError:
330 self.ui.warn(_("not in dirstate: %s!\n") % f)
330 self.ui.warn(_("not in dirstate: %s!\n") % f)
331 pass
331 pass
332
332
333 def clear(self):
333 def clear(self):
334 self.map = {}
334 self.map = {}
335 self.copymap = {}
335 self.copymap = {}
336 self.dirs = None
336 self.dirs = None
337 self.markdirty()
337 self.markdirty()
338
338
339 def rebuild(self, parent, files):
339 def rebuild(self, parent, files):
340 self.clear()
340 self.clear()
341 for f in files:
341 for f in files:
342 if files.execf(f):
342 if files.execf(f):
343 self.map[f] = ('n', 0777, -1, 0)
343 self.map[f] = ('n', 0777, -1, 0)
344 else:
344 else:
345 self.map[f] = ('n', 0666, -1, 0)
345 self.map[f] = ('n', 0666, -1, 0)
346 self.pl = (parent, nullid)
346 self.pl = (parent, nullid)
347 self.markdirty()
347 self.markdirty()
348
348
349 def write(self):
349 def write(self):
350 if not self.dirty:
350 if not self.dirty:
351 return
351 return
352 cs = cStringIO.StringIO()
352 cs = cStringIO.StringIO()
353 cs.write("".join(self.pl))
353 cs.write("".join(self.pl))
354 for f, e in self.map.iteritems():
354 for f, e in self.map.iteritems():
355 c = self.copied(f)
355 c = self.copied(f)
356 if c:
356 if c:
357 f = f + "\0" + c
357 f = f + "\0" + c
358 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
358 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
359 cs.write(e)
359 cs.write(e)
360 cs.write(f)
360 cs.write(f)
361 st = self.opener("dirstate", "w", atomic=True)
361 st = self.opener("dirstate", "w", atomictemp=True)
362 st.write(cs.getvalue())
362 st.write(cs.getvalue())
363 st.rename()
363 self.dirty = 0
364 self.dirty = 0
364
365
365 def filterfiles(self, files):
366 def filterfiles(self, files):
366 ret = {}
367 ret = {}
367 unknown = []
368 unknown = []
368
369
369 for x in files:
370 for x in files:
370 if x == '.':
371 if x == '.':
371 return self.map.copy()
372 return self.map.copy()
372 if x not in self.map:
373 if x not in self.map:
373 unknown.append(x)
374 unknown.append(x)
374 else:
375 else:
375 ret[x] = self.map[x]
376 ret[x] = self.map[x]
376
377
377 if not unknown:
378 if not unknown:
378 return ret
379 return ret
379
380
380 b = self.map.keys()
381 b = self.map.keys()
381 b.sort()
382 b.sort()
382 blen = len(b)
383 blen = len(b)
383
384
384 for x in unknown:
385 for x in unknown:
385 bs = bisect.bisect(b, "%s%s" % (x, '/'))
386 bs = bisect.bisect(b, "%s%s" % (x, '/'))
386 while bs < blen:
387 while bs < blen:
387 s = b[bs]
388 s = b[bs]
388 if len(s) > len(x) and s.startswith(x):
389 if len(s) > len(x) and s.startswith(x):
389 ret[s] = self.map[s]
390 ret[s] = self.map[s]
390 else:
391 else:
391 break
392 break
392 bs += 1
393 bs += 1
393 return ret
394 return ret
394
395
395 def supported_type(self, f, st, verbose=False):
396 def supported_type(self, f, st, verbose=False):
396 if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
397 if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
397 return True
398 return True
398 if verbose:
399 if verbose:
399 kind = 'unknown'
400 kind = 'unknown'
400 if stat.S_ISCHR(st.st_mode): kind = _('character device')
401 if stat.S_ISCHR(st.st_mode): kind = _('character device')
401 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
402 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
402 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
403 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
403 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
404 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
404 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
405 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
405 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
406 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
406 util.pathto(self.root, self.getcwd(), f),
407 util.pathto(self.root, self.getcwd(), f),
407 kind))
408 kind))
408 return False
409 return False
409
410
410 def walk(self, files=None, match=util.always, badmatch=None):
411 def walk(self, files=None, match=util.always, badmatch=None):
411 # filter out the stat
412 # filter out the stat
412 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
413 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
413 yield src, f
414 yield src, f
414
415
415 def statwalk(self, files=None, match=util.always, ignored=False,
416 def statwalk(self, files=None, match=util.always, ignored=False,
416 badmatch=None, directories=False):
417 badmatch=None, directories=False):
417 '''
418 '''
418 walk recursively through the directory tree, finding all files
419 walk recursively through the directory tree, finding all files
419 matched by the match function
420 matched by the match function
420
421
421 results are yielded in a tuple (src, filename, st), where src
422 results are yielded in a tuple (src, filename, st), where src
422 is one of:
423 is one of:
423 'f' the file was found in the directory tree
424 'f' the file was found in the directory tree
424 'd' the file is a directory of the tree
425 'd' the file is a directory of the tree
425 'm' the file was only in the dirstate and not in the tree
426 'm' the file was only in the dirstate and not in the tree
426 'b' file was not found and matched badmatch
427 'b' file was not found and matched badmatch
427
428
428 and st is the stat result if the file was found in the directory.
429 and st is the stat result if the file was found in the directory.
429 '''
430 '''
430 self.lazyread()
431 self.lazyread()
431
432
432 # walk all files by default
433 # walk all files by default
433 if not files:
434 if not files:
434 files = ['.']
435 files = ['.']
435 dc = self.map.copy()
436 dc = self.map.copy()
436 else:
437 else:
437 files = util.unique(files)
438 files = util.unique(files)
438 dc = self.filterfiles(files)
439 dc = self.filterfiles(files)
439
440
440 def imatch(file_):
441 def imatch(file_):
441 if file_ not in dc and self.ignore(file_):
442 if file_ not in dc and self.ignore(file_):
442 return False
443 return False
443 return match(file_)
444 return match(file_)
444
445
445 ignore = self.ignore
446 ignore = self.ignore
446 if ignored:
447 if ignored:
447 imatch = match
448 imatch = match
448 ignore = util.never
449 ignore = util.never
449
450
450 # self.root may end with a path separator when self.root == '/'
451 # self.root may end with a path separator when self.root == '/'
451 common_prefix_len = len(self.root)
452 common_prefix_len = len(self.root)
452 if not self.root.endswith(os.sep):
453 if not self.root.endswith(os.sep):
453 common_prefix_len += 1
454 common_prefix_len += 1
454 # recursion free walker, faster than os.walk.
455 # recursion free walker, faster than os.walk.
455 def findfiles(s):
456 def findfiles(s):
456 work = [s]
457 work = [s]
457 if directories:
458 if directories:
458 yield 'd', util.normpath(s[common_prefix_len:]), os.lstat(s)
459 yield 'd', util.normpath(s[common_prefix_len:]), os.lstat(s)
459 while work:
460 while work:
460 top = work.pop()
461 top = work.pop()
461 names = os.listdir(top)
462 names = os.listdir(top)
462 names.sort()
463 names.sort()
463 # nd is the top of the repository dir tree
464 # nd is the top of the repository dir tree
464 nd = util.normpath(top[common_prefix_len:])
465 nd = util.normpath(top[common_prefix_len:])
465 if nd == '.':
466 if nd == '.':
466 nd = ''
467 nd = ''
467 else:
468 else:
468 # do not recurse into a repo contained in this
469 # do not recurse into a repo contained in this
469 # one. use bisect to find .hg directory so speed
470 # one. use bisect to find .hg directory so speed
470 # is good on big directory.
471 # is good on big directory.
471 hg = bisect.bisect_left(names, '.hg')
472 hg = bisect.bisect_left(names, '.hg')
472 if hg < len(names) and names[hg] == '.hg':
473 if hg < len(names) and names[hg] == '.hg':
473 if os.path.isdir(os.path.join(top, '.hg')):
474 if os.path.isdir(os.path.join(top, '.hg')):
474 continue
475 continue
475 for f in names:
476 for f in names:
476 np = util.pconvert(os.path.join(nd, f))
477 np = util.pconvert(os.path.join(nd, f))
477 if seen(np):
478 if seen(np):
478 continue
479 continue
479 p = os.path.join(top, f)
480 p = os.path.join(top, f)
480 # don't trip over symlinks
481 # don't trip over symlinks
481 st = os.lstat(p)
482 st = os.lstat(p)
482 if stat.S_ISDIR(st.st_mode):
483 if stat.S_ISDIR(st.st_mode):
483 if not ignore(np):
484 if not ignore(np):
484 work.append(p)
485 work.append(p)
485 if directories:
486 if directories:
486 yield 'd', np, st
487 yield 'd', np, st
487 if imatch(np) and np in dc:
488 if imatch(np) and np in dc:
488 yield 'm', np, st
489 yield 'm', np, st
489 elif imatch(np):
490 elif imatch(np):
490 if self.supported_type(np, st):
491 if self.supported_type(np, st):
491 yield 'f', np, st
492 yield 'f', np, st
492 elif np in dc:
493 elif np in dc:
493 yield 'm', np, st
494 yield 'm', np, st
494
495
495 known = {'.hg': 1}
496 known = {'.hg': 1}
496 def seen(fn):
497 def seen(fn):
497 if fn in known: return True
498 if fn in known: return True
498 known[fn] = 1
499 known[fn] = 1
499
500
500 # step one, find all files that match our criteria
501 # step one, find all files that match our criteria
501 files.sort()
502 files.sort()
502 for ff in files:
503 for ff in files:
503 nf = util.normpath(ff)
504 nf = util.normpath(ff)
504 f = self.wjoin(ff)
505 f = self.wjoin(ff)
505 try:
506 try:
506 st = os.lstat(f)
507 st = os.lstat(f)
507 except OSError, inst:
508 except OSError, inst:
508 found = False
509 found = False
509 for fn in dc:
510 for fn in dc:
510 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
511 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
511 found = True
512 found = True
512 break
513 break
513 if not found:
514 if not found:
514 if inst.errno != errno.ENOENT or not badmatch:
515 if inst.errno != errno.ENOENT or not badmatch:
515 self.ui.warn('%s: %s\n' % (
516 self.ui.warn('%s: %s\n' % (
516 util.pathto(self.root, self.getcwd(), ff),
517 util.pathto(self.root, self.getcwd(), ff),
517 inst.strerror))
518 inst.strerror))
518 elif badmatch and badmatch(ff) and imatch(nf):
519 elif badmatch and badmatch(ff) and imatch(nf):
519 yield 'b', ff, None
520 yield 'b', ff, None
520 continue
521 continue
521 if stat.S_ISDIR(st.st_mode):
522 if stat.S_ISDIR(st.st_mode):
522 cmp1 = (lambda x, y: cmp(x[1], y[1]))
523 cmp1 = (lambda x, y: cmp(x[1], y[1]))
523 sorted_ = [ x for x in findfiles(f) ]
524 sorted_ = [ x for x in findfiles(f) ]
524 sorted_.sort(cmp1)
525 sorted_.sort(cmp1)
525 for e in sorted_:
526 for e in sorted_:
526 yield e
527 yield e
527 else:
528 else:
528 if not seen(nf) and match(nf):
529 if not seen(nf) and match(nf):
529 if self.supported_type(ff, st, verbose=True):
530 if self.supported_type(ff, st, verbose=True):
530 yield 'f', nf, st
531 yield 'f', nf, st
531 elif ff in dc:
532 elif ff in dc:
532 yield 'm', nf, st
533 yield 'm', nf, st
533
534
534 # step two run through anything left in the dc hash and yield
535 # step two run through anything left in the dc hash and yield
535 # if we haven't already seen it
536 # if we haven't already seen it
536 ks = dc.keys()
537 ks = dc.keys()
537 ks.sort()
538 ks.sort()
538 for k in ks:
539 for k in ks:
539 if not seen(k) and imatch(k):
540 if not seen(k) and imatch(k):
540 yield 'm', k, None
541 yield 'm', k, None
541
542
542 def status(self, files=None, match=util.always, list_ignored=False,
543 def status(self, files=None, match=util.always, list_ignored=False,
543 list_clean=False):
544 list_clean=False):
544 lookup, modified, added, unknown, ignored = [], [], [], [], []
545 lookup, modified, added, unknown, ignored = [], [], [], [], []
545 removed, deleted, clean = [], [], []
546 removed, deleted, clean = [], [], []
546
547
547 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
548 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
548 try:
549 try:
549 type_, mode, size, time = self[fn]
550 type_, mode, size, time = self[fn]
550 except KeyError:
551 except KeyError:
551 if list_ignored and self.ignore(fn):
552 if list_ignored and self.ignore(fn):
552 ignored.append(fn)
553 ignored.append(fn)
553 else:
554 else:
554 unknown.append(fn)
555 unknown.append(fn)
555 continue
556 continue
556 if src == 'm':
557 if src == 'm':
557 nonexistent = True
558 nonexistent = True
558 if not st:
559 if not st:
559 try:
560 try:
560 st = os.lstat(self.wjoin(fn))
561 st = os.lstat(self.wjoin(fn))
561 except OSError, inst:
562 except OSError, inst:
562 if inst.errno != errno.ENOENT:
563 if inst.errno != errno.ENOENT:
563 raise
564 raise
564 st = None
565 st = None
565 # We need to re-check that it is a valid file
566 # We need to re-check that it is a valid file
566 if st and self.supported_type(fn, st):
567 if st and self.supported_type(fn, st):
567 nonexistent = False
568 nonexistent = False
568 # XXX: what to do with file no longer present in the fs
569 # XXX: what to do with file no longer present in the fs
569 # who are not removed in the dirstate ?
570 # who are not removed in the dirstate ?
570 if nonexistent and type_ in "nm":
571 if nonexistent and type_ in "nm":
571 deleted.append(fn)
572 deleted.append(fn)
572 continue
573 continue
573 # check the common case first
574 # check the common case first
574 if type_ == 'n':
575 if type_ == 'n':
575 if not st:
576 if not st:
576 st = os.lstat(self.wjoin(fn))
577 st = os.lstat(self.wjoin(fn))
577 if size >= 0 and (size != st.st_size
578 if size >= 0 and (size != st.st_size
578 or (mode ^ st.st_mode) & 0100):
579 or (mode ^ st.st_mode) & 0100):
579 modified.append(fn)
580 modified.append(fn)
580 elif time != int(st.st_mtime):
581 elif time != int(st.st_mtime):
581 lookup.append(fn)
582 lookup.append(fn)
582 elif list_clean:
583 elif list_clean:
583 clean.append(fn)
584 clean.append(fn)
584 elif type_ == 'm':
585 elif type_ == 'm':
585 modified.append(fn)
586 modified.append(fn)
586 elif type_ == 'a':
587 elif type_ == 'a':
587 added.append(fn)
588 added.append(fn)
588 elif type_ == 'r':
589 elif type_ == 'r':
589 removed.append(fn)
590 removed.append(fn)
590
591
591 return (lookup, modified, added, removed, deleted, unknown, ignored,
592 return (lookup, modified, added, removed, deleted, unknown, ignored,
592 clean)
593 clean)
General Comments 0
You need to be logged in to leave comments. Login now