##// END OF EJS Templates
self.root == '/': prefix length computation out of the loop...
Benoit Boissinot -
r2671:82864a2e default
parent child Browse files
Show More
@@ -1,491 +1,491 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
4 Copyright 2005 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 util re errno")
13 demandload(globals(), "struct os time bisect stat 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.copies = {}
25 self.copies = {}
26 self.ignorefunc = None
26 self.ignorefunc = None
27 self.blockignore = False
27 self.blockignore = False
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 self.blockignore:
100 if self.blockignore:
101 return False
101 return False
102 if not self.ignorefunc:
102 if not self.ignorefunc:
103 ignore = self.hgignore()
103 ignore = self.hgignore()
104 allpats = []
104 allpats = []
105 [allpats.extend(patlist) for patlist in ignore.values()]
105 [allpats.extend(patlist) for patlist in ignore.values()]
106 if allpats:
106 if allpats:
107 try:
107 try:
108 files, self.ignorefunc, anypats = (
108 files, self.ignorefunc, anypats = (
109 util.matcher(self.root, inc=allpats, src='.hgignore'))
109 util.matcher(self.root, inc=allpats, src='.hgignore'))
110 except util.Abort:
110 except util.Abort:
111 # Re-raise an exception where the src is the right file
111 # Re-raise an exception where the src is the right file
112 for f, patlist in ignore.items():
112 for f, patlist in ignore.items():
113 files, self.ignorefunc, anypats = (
113 files, self.ignorefunc, anypats = (
114 util.matcher(self.root, inc=patlist, src=f))
114 util.matcher(self.root, inc=patlist, src=f))
115 else:
115 else:
116 self.ignorefunc = util.never
116 self.ignorefunc = util.never
117 return self.ignorefunc(fn)
117 return self.ignorefunc(fn)
118
118
119 def __del__(self):
119 def __del__(self):
120 if self.dirty:
120 if self.dirty:
121 self.write()
121 self.write()
122
122
123 def __getitem__(self, key):
123 def __getitem__(self, key):
124 try:
124 try:
125 return self.map[key]
125 return self.map[key]
126 except TypeError:
126 except TypeError:
127 self.lazyread()
127 self.lazyread()
128 return self[key]
128 return self[key]
129
129
130 def __contains__(self, key):
130 def __contains__(self, key):
131 self.lazyread()
131 self.lazyread()
132 return key in self.map
132 return key in self.map
133
133
134 def parents(self):
134 def parents(self):
135 self.lazyread()
135 self.lazyread()
136 return self.pl
136 return self.pl
137
137
138 def markdirty(self):
138 def markdirty(self):
139 if not self.dirty:
139 if not self.dirty:
140 self.dirty = 1
140 self.dirty = 1
141
141
142 def setparents(self, p1, p2=nullid):
142 def setparents(self, p1, p2=nullid):
143 self.lazyread()
143 self.lazyread()
144 self.markdirty()
144 self.markdirty()
145 self.pl = p1, p2
145 self.pl = p1, p2
146
146
147 def state(self, key):
147 def state(self, key):
148 try:
148 try:
149 return self[key][0]
149 return self[key][0]
150 except KeyError:
150 except KeyError:
151 return "?"
151 return "?"
152
152
153 def lazyread(self):
153 def lazyread(self):
154 if self.map is None:
154 if self.map is None:
155 self.read()
155 self.read()
156
156
157 def parse(self, st):
157 def parse(self, st):
158 self.pl = [st[:20], st[20: 40]]
158 self.pl = [st[:20], st[20: 40]]
159
159
160 # deref fields so they will be local in loop
160 # deref fields so they will be local in loop
161 map = self.map
161 map = self.map
162 copies = self.copies
162 copies = self.copies
163 format = self.format
163 format = self.format
164 unpack = struct.unpack
164 unpack = struct.unpack
165
165
166 pos = 40
166 pos = 40
167 e_size = struct.calcsize(format)
167 e_size = struct.calcsize(format)
168
168
169 while pos < len(st):
169 while pos < len(st):
170 newpos = pos + e_size
170 newpos = pos + e_size
171 e = unpack(format, st[pos:newpos])
171 e = unpack(format, st[pos:newpos])
172 l = e[4]
172 l = e[4]
173 pos = newpos
173 pos = newpos
174 newpos = pos + l
174 newpos = pos + l
175 f = st[pos:newpos]
175 f = st[pos:newpos]
176 if '\0' in f:
176 if '\0' in f:
177 f, c = f.split('\0')
177 f, c = f.split('\0')
178 copies[f] = c
178 copies[f] = c
179 map[f] = e[:4]
179 map[f] = e[:4]
180 pos = newpos
180 pos = newpos
181
181
182 def read(self):
182 def read(self):
183 self.map = {}
183 self.map = {}
184 self.pl = [nullid, nullid]
184 self.pl = [nullid, nullid]
185 try:
185 try:
186 st = self.opener("dirstate").read()
186 st = self.opener("dirstate").read()
187 if st:
187 if st:
188 self.parse(st)
188 self.parse(st)
189 except IOError, err:
189 except IOError, err:
190 if err.errno != errno.ENOENT: raise
190 if err.errno != errno.ENOENT: raise
191
191
192 def copy(self, source, dest):
192 def copy(self, source, dest):
193 self.lazyread()
193 self.lazyread()
194 self.markdirty()
194 self.markdirty()
195 self.copies[dest] = source
195 self.copies[dest] = source
196
196
197 def copied(self, file):
197 def copied(self, file):
198 return self.copies.get(file, None)
198 return self.copies.get(file, None)
199
199
200 def update(self, files, state, **kw):
200 def update(self, files, state, **kw):
201 ''' current states:
201 ''' current states:
202 n normal
202 n normal
203 m needs merging
203 m needs merging
204 r marked for removal
204 r marked for removal
205 a marked for addition'''
205 a marked for addition'''
206
206
207 if not files: return
207 if not files: return
208 self.lazyread()
208 self.lazyread()
209 self.markdirty()
209 self.markdirty()
210 for f in files:
210 for f in files:
211 if state == "r":
211 if state == "r":
212 self.map[f] = ('r', 0, 0, 0)
212 self.map[f] = ('r', 0, 0, 0)
213 else:
213 else:
214 s = os.lstat(self.wjoin(f))
214 s = os.lstat(self.wjoin(f))
215 st_size = kw.get('st_size', s.st_size)
215 st_size = kw.get('st_size', s.st_size)
216 st_mtime = kw.get('st_mtime', s.st_mtime)
216 st_mtime = kw.get('st_mtime', s.st_mtime)
217 self.map[f] = (state, s.st_mode, st_size, st_mtime)
217 self.map[f] = (state, s.st_mode, st_size, st_mtime)
218 if self.copies.has_key(f):
218 if self.copies.has_key(f):
219 del self.copies[f]
219 del self.copies[f]
220
220
221 def forget(self, files):
221 def forget(self, files):
222 if not files: return
222 if not files: return
223 self.lazyread()
223 self.lazyread()
224 self.markdirty()
224 self.markdirty()
225 for f in files:
225 for f in files:
226 try:
226 try:
227 del self.map[f]
227 del self.map[f]
228 except KeyError:
228 except KeyError:
229 self.ui.warn(_("not in dirstate: %s!\n") % f)
229 self.ui.warn(_("not in dirstate: %s!\n") % f)
230 pass
230 pass
231
231
232 def clear(self):
232 def clear(self):
233 self.map = {}
233 self.map = {}
234 self.copies = {}
234 self.copies = {}
235 self.markdirty()
235 self.markdirty()
236
236
237 def rebuild(self, parent, files):
237 def rebuild(self, parent, files):
238 self.clear()
238 self.clear()
239 umask = os.umask(0)
239 umask = os.umask(0)
240 os.umask(umask)
240 os.umask(umask)
241 for f, mode in files:
241 for f, mode in files:
242 if mode:
242 if mode:
243 self.map[f] = ('n', ~umask, -1, 0)
243 self.map[f] = ('n', ~umask, -1, 0)
244 else:
244 else:
245 self.map[f] = ('n', ~umask & 0666, -1, 0)
245 self.map[f] = ('n', ~umask & 0666, -1, 0)
246 self.pl = (parent, nullid)
246 self.pl = (parent, nullid)
247 self.markdirty()
247 self.markdirty()
248
248
249 def write(self):
249 def write(self):
250 if not self.dirty:
250 if not self.dirty:
251 return
251 return
252 st = self.opener("dirstate", "w", atomic=True)
252 st = self.opener("dirstate", "w", atomic=True)
253 st.write("".join(self.pl))
253 st.write("".join(self.pl))
254 for f, e in self.map.items():
254 for f, e in self.map.items():
255 c = self.copied(f)
255 c = self.copied(f)
256 if c:
256 if c:
257 f = f + "\0" + c
257 f = f + "\0" + c
258 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
258 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
259 st.write(e + f)
259 st.write(e + f)
260 self.dirty = 0
260 self.dirty = 0
261
261
262 def filterfiles(self, files):
262 def filterfiles(self, files):
263 ret = {}
263 ret = {}
264 unknown = []
264 unknown = []
265
265
266 for x in files:
266 for x in files:
267 if x == '.':
267 if x == '.':
268 return self.map.copy()
268 return self.map.copy()
269 if x not in self.map:
269 if x not in self.map:
270 unknown.append(x)
270 unknown.append(x)
271 else:
271 else:
272 ret[x] = self.map[x]
272 ret[x] = self.map[x]
273
273
274 if not unknown:
274 if not unknown:
275 return ret
275 return ret
276
276
277 b = self.map.keys()
277 b = self.map.keys()
278 b.sort()
278 b.sort()
279 blen = len(b)
279 blen = len(b)
280
280
281 for x in unknown:
281 for x in unknown:
282 bs = bisect.bisect(b, "%s%s" % (x, '/'))
282 bs = bisect.bisect(b, "%s%s" % (x, '/'))
283 while bs < blen:
283 while bs < blen:
284 s = b[bs]
284 s = b[bs]
285 if len(s) > len(x) and s.startswith(x):
285 if len(s) > len(x) and s.startswith(x):
286 ret[s] = self.map[s]
286 ret[s] = self.map[s]
287 else:
287 else:
288 break
288 break
289 bs += 1
289 bs += 1
290 return ret
290 return ret
291
291
292 def supported_type(self, f, st, verbose=False):
292 def supported_type(self, f, st, verbose=False):
293 if stat.S_ISREG(st.st_mode):
293 if stat.S_ISREG(st.st_mode):
294 return True
294 return True
295 if verbose:
295 if verbose:
296 kind = 'unknown'
296 kind = 'unknown'
297 if stat.S_ISCHR(st.st_mode): kind = _('character device')
297 if stat.S_ISCHR(st.st_mode): kind = _('character device')
298 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
298 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
299 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
299 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
300 elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
300 elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
301 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
301 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
302 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
302 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
303 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
303 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
304 util.pathto(self.getcwd(), f),
304 util.pathto(self.getcwd(), f),
305 kind))
305 kind))
306 return False
306 return False
307
307
308 def statwalk(self, files=None, match=util.always, dc=None, ignored=False,
308 def statwalk(self, files=None, match=util.always, dc=None, ignored=False,
309 badmatch=None):
309 badmatch=None):
310 self.lazyread()
310 self.lazyread()
311
311
312 # walk all files by default
312 # walk all files by default
313 if not files:
313 if not files:
314 files = [self.root]
314 files = [self.root]
315 if not dc:
315 if not dc:
316 dc = self.map.copy()
316 dc = self.map.copy()
317 elif not dc:
317 elif not dc:
318 dc = self.filterfiles(files)
318 dc = self.filterfiles(files)
319
319
320 def statmatch(file_, stat):
320 def statmatch(file_, stat):
321 file_ = util.pconvert(file_)
321 file_ = util.pconvert(file_)
322 if not ignored and file_ not in dc and self.ignore(file_):
322 if not ignored and file_ not in dc and self.ignore(file_):
323 return False
323 return False
324 return match(file_)
324 return match(file_)
325
325
326 return self.walkhelper(files=files, statmatch=statmatch, dc=dc,
326 return self.walkhelper(files=files, statmatch=statmatch, dc=dc,
327 badmatch=badmatch)
327 badmatch=badmatch)
328
328
329 def walk(self, files=None, match=util.always, dc=None, badmatch=None):
329 def walk(self, files=None, match=util.always, dc=None, badmatch=None):
330 # filter out the stat
330 # filter out the stat
331 for src, f, st in self.statwalk(files, match, dc, badmatch=badmatch):
331 for src, f, st in self.statwalk(files, match, dc, badmatch=badmatch):
332 yield src, f
332 yield src, f
333
333
334 # walk recursively through the directory tree, finding all files
334 # walk recursively through the directory tree, finding all files
335 # matched by the statmatch function
335 # matched by the statmatch function
336 #
336 #
337 # results are yielded in a tuple (src, filename, st), where src
337 # results are yielded in a tuple (src, filename, st), where src
338 # is one of:
338 # is one of:
339 # 'f' the file was found in the directory tree
339 # 'f' the file was found in the directory tree
340 # 'm' the file was only in the dirstate and not in the tree
340 # 'm' the file was only in the dirstate and not in the tree
341 # and st is the stat result if the file was found in the directory.
341 # and st is the stat result if the file was found in the directory.
342 #
342 #
343 # dc is an optional arg for the current dirstate. dc is not modified
343 # dc is an optional arg for the current dirstate. dc is not modified
344 # directly by this function, but might be modified by your statmatch call.
344 # directly by this function, but might be modified by your statmatch call.
345 #
345 #
346 def walkhelper(self, files, statmatch, dc, badmatch=None):
346 def walkhelper(self, files, statmatch, dc, badmatch=None):
347 # self.root may end with a path separator when self.root == '/'
348 common_prefix_len = len(self.root)
349 if not self.root.endswith('/'):
350 common_prefix_len += 1
347 # recursion free walker, faster than os.walk.
351 # recursion free walker, faster than os.walk.
348 def findfiles(s):
352 def findfiles(s):
349 work = [s]
353 work = [s]
350 # self.root may end with a path separator when self.root == '/'
351 root_subtract_len = len(self.root)
352 if not self.root.endswith('/'):
353 root_subtract_len += 1
354 while work:
354 while work:
355 top = work.pop()
355 top = work.pop()
356 names = os.listdir(top)
356 names = os.listdir(top)
357 names.sort()
357 names.sort()
358 # nd is the top of the repository dir tree
358 # nd is the top of the repository dir tree
359 nd = util.normpath(top[root_subtract_len:])
359 nd = util.normpath(top[common_prefix_len:])
360 if nd == '.':
360 if nd == '.':
361 nd = ''
361 nd = ''
362 else:
362 else:
363 # do not recurse into a repo contained in this
363 # do not recurse into a repo contained in this
364 # one. use bisect to find .hg directory so speed
364 # one. use bisect to find .hg directory so speed
365 # is good on big directory.
365 # is good on big directory.
366 hg = bisect.bisect_left(names, '.hg')
366 hg = bisect.bisect_left(names, '.hg')
367 if hg < len(names) and names[hg] == '.hg':
367 if hg < len(names) and names[hg] == '.hg':
368 if os.path.isdir(os.path.join(top, '.hg')):
368 if os.path.isdir(os.path.join(top, '.hg')):
369 continue
369 continue
370 for f in names:
370 for f in names:
371 np = util.pconvert(os.path.join(nd, f))
371 np = util.pconvert(os.path.join(nd, f))
372 if seen(np):
372 if seen(np):
373 continue
373 continue
374 p = os.path.join(top, f)
374 p = os.path.join(top, f)
375 # don't trip over symlinks
375 # don't trip over symlinks
376 st = os.lstat(p)
376 st = os.lstat(p)
377 if stat.S_ISDIR(st.st_mode):
377 if stat.S_ISDIR(st.st_mode):
378 ds = os.path.join(nd, f +'/')
378 ds = os.path.join(nd, f +'/')
379 if statmatch(ds, st):
379 if statmatch(ds, st):
380 work.append(p)
380 work.append(p)
381 if statmatch(np, st) and np in dc:
381 if statmatch(np, st) and np in dc:
382 yield 'm', np, st
382 yield 'm', np, st
383 elif statmatch(np, st):
383 elif statmatch(np, st):
384 if self.supported_type(np, st):
384 if self.supported_type(np, st):
385 yield 'f', np, st
385 yield 'f', np, st
386 elif np in dc:
386 elif np in dc:
387 yield 'm', np, st
387 yield 'm', np, st
388
388
389 known = {'.hg': 1}
389 known = {'.hg': 1}
390 def seen(fn):
390 def seen(fn):
391 if fn in known: return True
391 if fn in known: return True
392 known[fn] = 1
392 known[fn] = 1
393
393
394 # step one, find all files that match our criteria
394 # step one, find all files that match our criteria
395 files.sort()
395 files.sort()
396 for ff in util.unique(files):
396 for ff in util.unique(files):
397 f = self.wjoin(ff)
397 f = self.wjoin(ff)
398 try:
398 try:
399 st = os.lstat(f)
399 st = os.lstat(f)
400 except OSError, inst:
400 except OSError, inst:
401 nf = util.normpath(ff)
401 nf = util.normpath(ff)
402 found = False
402 found = False
403 for fn in dc:
403 for fn in dc:
404 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
404 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
405 found = True
405 found = True
406 break
406 break
407 if not found:
407 if not found:
408 if inst.errno != errno.ENOENT or not badmatch:
408 if inst.errno != errno.ENOENT or not badmatch:
409 self.ui.warn('%s: %s\n' % (
409 self.ui.warn('%s: %s\n' % (
410 util.pathto(self.getcwd(), ff),
410 util.pathto(self.getcwd(), ff),
411 inst.strerror))
411 inst.strerror))
412 elif badmatch and badmatch(ff) and statmatch(ff, None):
412 elif badmatch and badmatch(ff) and statmatch(ff, None):
413 yield 'b', ff, None
413 yield 'b', ff, None
414 continue
414 continue
415 if stat.S_ISDIR(st.st_mode):
415 if stat.S_ISDIR(st.st_mode):
416 cmp1 = (lambda x, y: cmp(x[1], y[1]))
416 cmp1 = (lambda x, y: cmp(x[1], y[1]))
417 sorted_ = [ x for x in findfiles(f) ]
417 sorted_ = [ x for x in findfiles(f) ]
418 sorted_.sort(cmp1)
418 sorted_.sort(cmp1)
419 for e in sorted_:
419 for e in sorted_:
420 yield e
420 yield e
421 else:
421 else:
422 ff = util.normpath(ff)
422 ff = util.normpath(ff)
423 if seen(ff):
423 if seen(ff):
424 continue
424 continue
425 self.blockignore = True
425 self.blockignore = True
426 if statmatch(ff, st):
426 if statmatch(ff, st):
427 if self.supported_type(ff, st, verbose=True):
427 if self.supported_type(ff, st, verbose=True):
428 yield 'f', ff, st
428 yield 'f', ff, st
429 elif ff in dc:
429 elif ff in dc:
430 yield 'm', ff, st
430 yield 'm', ff, st
431 self.blockignore = False
431 self.blockignore = False
432
432
433 # step two run through anything left in the dc hash and yield
433 # step two run through anything left in the dc hash and yield
434 # if we haven't already seen it
434 # if we haven't already seen it
435 ks = dc.keys()
435 ks = dc.keys()
436 ks.sort()
436 ks.sort()
437 for k in ks:
437 for k in ks:
438 if not seen(k) and (statmatch(k, None)):
438 if not seen(k) and (statmatch(k, None)):
439 yield 'm', k, None
439 yield 'm', k, None
440
440
441 def status(self, files=None, match=util.always, list_ignored=False,
441 def status(self, files=None, match=util.always, list_ignored=False,
442 list_clean=False):
442 list_clean=False):
443 lookup, modified, added, unknown, ignored = [], [], [], [], []
443 lookup, modified, added, unknown, ignored = [], [], [], [], []
444 removed, deleted, clean = [], [], []
444 removed, deleted, clean = [], [], []
445
445
446 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
446 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
447 try:
447 try:
448 type_, mode, size, time = self[fn]
448 type_, mode, size, time = self[fn]
449 except KeyError:
449 except KeyError:
450 if list_ignored and self.ignore(fn):
450 if list_ignored and self.ignore(fn):
451 ignored.append(fn)
451 ignored.append(fn)
452 else:
452 else:
453 unknown.append(fn)
453 unknown.append(fn)
454 continue
454 continue
455 if src == 'm':
455 if src == 'm':
456 nonexistent = True
456 nonexistent = True
457 if not st:
457 if not st:
458 try:
458 try:
459 st = os.lstat(self.wjoin(fn))
459 st = os.lstat(self.wjoin(fn))
460 except OSError, inst:
460 except OSError, inst:
461 if inst.errno != errno.ENOENT:
461 if inst.errno != errno.ENOENT:
462 raise
462 raise
463 st = None
463 st = None
464 # We need to re-check that it is a valid file
464 # We need to re-check that it is a valid file
465 if st and self.supported_type(fn, st):
465 if st and self.supported_type(fn, st):
466 nonexistent = False
466 nonexistent = False
467 # XXX: what to do with file no longer present in the fs
467 # XXX: what to do with file no longer present in the fs
468 # who are not removed in the dirstate ?
468 # who are not removed in the dirstate ?
469 if nonexistent and type_ in "nm":
469 if nonexistent and type_ in "nm":
470 deleted.append(fn)
470 deleted.append(fn)
471 continue
471 continue
472 # check the common case first
472 # check the common case first
473 if type_ == 'n':
473 if type_ == 'n':
474 if not st:
474 if not st:
475 st = os.lstat(self.wjoin(fn))
475 st = os.lstat(self.wjoin(fn))
476 if size >= 0 and (size != st.st_size
476 if size >= 0 and (size != st.st_size
477 or (mode ^ st.st_mode) & 0100):
477 or (mode ^ st.st_mode) & 0100):
478 modified.append(fn)
478 modified.append(fn)
479 elif time != st.st_mtime:
479 elif time != st.st_mtime:
480 lookup.append(fn)
480 lookup.append(fn)
481 elif list_clean:
481 elif list_clean:
482 clean.append(fn)
482 clean.append(fn)
483 elif type_ == 'm':
483 elif type_ == 'm':
484 modified.append(fn)
484 modified.append(fn)
485 elif type_ == 'a':
485 elif type_ == 'a':
486 added.append(fn)
486 added.append(fn)
487 elif type_ == 'r':
487 elif type_ == 'r':
488 removed.append(fn)
488 removed.append(fn)
489
489
490 return (lookup, modified, added, removed, deleted, unknown, ignored,
490 return (lookup, modified, added, removed, deleted, unknown, ignored,
491 clean)
491 clean)
General Comments 0
You need to be logged in to leave comments. Login now