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