##// END OF EJS Templates
dirstate.py: when comparing mtimes, use only the integer part....
Alexis S. L. Carvalho -
r2962:882e703e default
parent child Browse files
Show More
@@ -1,533 +1,533 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.copies = {}
26 self.copies = {}
27 self.ignorefunc = None
27 self.ignorefunc = None
28 self.blockignore = False
28 self.blockignore = False
29
29
30 def wjoin(self, f):
30 def wjoin(self, f):
31 return os.path.join(self.root, f)
31 return os.path.join(self.root, f)
32
32
33 def getcwd(self):
33 def getcwd(self):
34 cwd = os.getcwd()
34 cwd = os.getcwd()
35 if cwd == self.root: return ''
35 if cwd == self.root: return ''
36 return cwd[len(self.root) + 1:]
36 return cwd[len(self.root) + 1:]
37
37
38 def hgignore(self):
38 def hgignore(self):
39 '''return the contents of .hgignore files as a list of patterns.
39 '''return the contents of .hgignore files as a list of patterns.
40
40
41 the files parsed for patterns include:
41 the files parsed for patterns include:
42 .hgignore in the repository root
42 .hgignore in the repository root
43 any additional files specified in the [ui] section of ~/.hgrc
43 any additional files specified in the [ui] section of ~/.hgrc
44
44
45 trailing white space is dropped.
45 trailing white space is dropped.
46 the escape character is backslash.
46 the escape character is backslash.
47 comments start with #.
47 comments start with #.
48 empty lines are skipped.
48 empty lines are skipped.
49
49
50 lines can be of the following formats:
50 lines can be of the following formats:
51
51
52 syntax: regexp # defaults following lines to non-rooted regexps
52 syntax: regexp # defaults following lines to non-rooted regexps
53 syntax: glob # defaults following lines to non-rooted globs
53 syntax: glob # defaults following lines to non-rooted globs
54 re:pattern # non-rooted regular expression
54 re:pattern # non-rooted regular expression
55 glob:pattern # non-rooted glob
55 glob:pattern # non-rooted glob
56 pattern # pattern of the current default type'''
56 pattern # pattern of the current default type'''
57 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
57 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
58 def parselines(fp):
58 def parselines(fp):
59 for line in fp:
59 for line in fp:
60 escape = False
60 escape = False
61 for i in xrange(len(line)):
61 for i in xrange(len(line)):
62 if escape: escape = False
62 if escape: escape = False
63 elif line[i] == '\\': escape = True
63 elif line[i] == '\\': escape = True
64 elif line[i] == '#': break
64 elif line[i] == '#': break
65 line = line[:i].rstrip()
65 line = line[:i].rstrip()
66 if line: yield line
66 if line: yield line
67 repoignore = self.wjoin('.hgignore')
67 repoignore = self.wjoin('.hgignore')
68 files = [repoignore]
68 files = [repoignore]
69 files.extend(self.ui.hgignorefiles())
69 files.extend(self.ui.hgignorefiles())
70 pats = {}
70 pats = {}
71 for f in files:
71 for f in files:
72 try:
72 try:
73 pats[f] = []
73 pats[f] = []
74 fp = open(f)
74 fp = open(f)
75 syntax = 'relre:'
75 syntax = 'relre:'
76 for line in parselines(fp):
76 for line in parselines(fp):
77 if line.startswith('syntax:'):
77 if line.startswith('syntax:'):
78 s = line[7:].strip()
78 s = line[7:].strip()
79 try:
79 try:
80 syntax = syntaxes[s]
80 syntax = syntaxes[s]
81 except KeyError:
81 except KeyError:
82 self.ui.warn(_("%s: ignoring invalid "
82 self.ui.warn(_("%s: ignoring invalid "
83 "syntax '%s'\n") % (f, s))
83 "syntax '%s'\n") % (f, s))
84 continue
84 continue
85 pat = syntax + line
85 pat = syntax + line
86 for s in syntaxes.values():
86 for s in syntaxes.values():
87 if line.startswith(s):
87 if line.startswith(s):
88 pat = line
88 pat = line
89 break
89 break
90 pats[f].append(pat)
90 pats[f].append(pat)
91 except IOError, inst:
91 except IOError, inst:
92 if f != repoignore:
92 if f != repoignore:
93 self.ui.warn(_("skipping unreadable ignore file"
93 self.ui.warn(_("skipping unreadable ignore file"
94 " '%s': %s\n") % (f, inst.strerror))
94 " '%s': %s\n") % (f, inst.strerror))
95 return pats
95 return pats
96
96
97 def ignore(self, fn):
97 def ignore(self, fn):
98 '''default match function used by dirstate and
98 '''default match function used by dirstate and
99 localrepository. this honours the repository .hgignore file
99 localrepository. this honours the repository .hgignore file
100 and any other files specified in the [ui] section of .hgrc.'''
100 and any other files specified in the [ui] section of .hgrc.'''
101 if self.blockignore:
101 if self.blockignore:
102 return False
102 return False
103 if not self.ignorefunc:
103 if not self.ignorefunc:
104 ignore = self.hgignore()
104 ignore = self.hgignore()
105 allpats = []
105 allpats = []
106 [allpats.extend(patlist) for patlist in ignore.values()]
106 [allpats.extend(patlist) for patlist in ignore.values()]
107 if allpats:
107 if allpats:
108 try:
108 try:
109 files, self.ignorefunc, anypats = (
109 files, self.ignorefunc, anypats = (
110 util.matcher(self.root, inc=allpats, src='.hgignore'))
110 util.matcher(self.root, inc=allpats, src='.hgignore'))
111 except util.Abort:
111 except util.Abort:
112 # Re-raise an exception where the src is the right file
112 # Re-raise an exception where the src is the right file
113 for f, patlist in ignore.items():
113 for f, patlist in ignore.items():
114 files, self.ignorefunc, anypats = (
114 files, self.ignorefunc, anypats = (
115 util.matcher(self.root, inc=patlist, src=f))
115 util.matcher(self.root, inc=patlist, src=f))
116 else:
116 else:
117 self.ignorefunc = util.never
117 self.ignorefunc = util.never
118 return self.ignorefunc(fn)
118 return self.ignorefunc(fn)
119
119
120 def __del__(self):
120 def __del__(self):
121 if self.dirty:
121 if self.dirty:
122 self.write()
122 self.write()
123
123
124 def __getitem__(self, key):
124 def __getitem__(self, key):
125 try:
125 try:
126 return self.map[key]
126 return self.map[key]
127 except TypeError:
127 except TypeError:
128 self.lazyread()
128 self.lazyread()
129 return self[key]
129 return self[key]
130
130
131 def __contains__(self, key):
131 def __contains__(self, key):
132 self.lazyread()
132 self.lazyread()
133 return key in self.map
133 return key in self.map
134
134
135 def parents(self):
135 def parents(self):
136 self.lazyread()
136 self.lazyread()
137 return self.pl
137 return self.pl
138
138
139 def markdirty(self):
139 def markdirty(self):
140 if not self.dirty:
140 if not self.dirty:
141 self.dirty = 1
141 self.dirty = 1
142
142
143 def setparents(self, p1, p2=nullid):
143 def setparents(self, p1, p2=nullid):
144 self.lazyread()
144 self.lazyread()
145 self.markdirty()
145 self.markdirty()
146 self.pl = p1, p2
146 self.pl = p1, p2
147
147
148 def state(self, key):
148 def state(self, key):
149 try:
149 try:
150 return self[key][0]
150 return self[key][0]
151 except KeyError:
151 except KeyError:
152 return "?"
152 return "?"
153
153
154 def lazyread(self):
154 def lazyread(self):
155 if self.map is None:
155 if self.map is None:
156 self.read()
156 self.read()
157
157
158 def parse(self, st):
158 def parse(self, st):
159 self.pl = [st[:20], st[20: 40]]
159 self.pl = [st[:20], st[20: 40]]
160
160
161 # deref fields so they will be local in loop
161 # deref fields so they will be local in loop
162 map = self.map
162 map = self.map
163 copies = self.copies
163 copies = self.copies
164 format = self.format
164 format = self.format
165 unpack = struct.unpack
165 unpack = struct.unpack
166
166
167 pos = 40
167 pos = 40
168 e_size = struct.calcsize(format)
168 e_size = struct.calcsize(format)
169
169
170 while pos < len(st):
170 while pos < len(st):
171 newpos = pos + e_size
171 newpos = pos + e_size
172 e = unpack(format, st[pos:newpos])
172 e = unpack(format, st[pos:newpos])
173 l = e[4]
173 l = e[4]
174 pos = newpos
174 pos = newpos
175 newpos = pos + l
175 newpos = pos + l
176 f = st[pos:newpos]
176 f = st[pos:newpos]
177 if '\0' in f:
177 if '\0' in f:
178 f, c = f.split('\0')
178 f, c = f.split('\0')
179 copies[f] = c
179 copies[f] = c
180 map[f] = e[:4]
180 map[f] = e[:4]
181 pos = newpos
181 pos = newpos
182
182
183 def read(self):
183 def read(self):
184 self.map = {}
184 self.map = {}
185 self.pl = [nullid, nullid]
185 self.pl = [nullid, nullid]
186 try:
186 try:
187 st = self.opener("dirstate").read()
187 st = self.opener("dirstate").read()
188 if st:
188 if st:
189 self.parse(st)
189 self.parse(st)
190 except IOError, err:
190 except IOError, err:
191 if err.errno != errno.ENOENT: raise
191 if err.errno != errno.ENOENT: raise
192
192
193 def copy(self, source, dest):
193 def copy(self, source, dest):
194 self.lazyread()
194 self.lazyread()
195 self.markdirty()
195 self.markdirty()
196 self.copies[dest] = source
196 self.copies[dest] = source
197
197
198 def copied(self, file):
198 def copied(self, file):
199 return self.copies.get(file, None)
199 return self.copies.get(file, None)
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.copies.has_key(f):
257 if self.copies.has_key(f):
258 del self.copies[f]
258 del self.copies[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.copies = {}
275 self.copies = {}
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 statwalk(self, files=None, match=util.always, dc=None, ignored=False,
350 def statwalk(self, files=None, match=util.always, dc=None, ignored=False,
351 badmatch=None):
351 badmatch=None):
352 self.lazyread()
352 self.lazyread()
353
353
354 # walk all files by default
354 # walk all files by default
355 if not files:
355 if not files:
356 files = [self.root]
356 files = [self.root]
357 if not dc:
357 if not dc:
358 dc = self.map.copy()
358 dc = self.map.copy()
359 elif not dc:
359 elif not dc:
360 dc = self.filterfiles(files)
360 dc = self.filterfiles(files)
361
361
362 def statmatch(file_, stat):
362 def statmatch(file_, stat):
363 file_ = util.pconvert(file_)
363 file_ = util.pconvert(file_)
364 if not ignored and file_ not in dc and self.ignore(file_):
364 if not ignored and file_ not in dc and self.ignore(file_):
365 return False
365 return False
366 return match(file_)
366 return match(file_)
367
367
368 return self.walkhelper(files=files, statmatch=statmatch, dc=dc,
368 return self.walkhelper(files=files, statmatch=statmatch, dc=dc,
369 badmatch=badmatch)
369 badmatch=badmatch)
370
370
371 def walk(self, files=None, match=util.always, dc=None, badmatch=None):
371 def walk(self, files=None, match=util.always, dc=None, badmatch=None):
372 # filter out the stat
372 # filter out the stat
373 for src, f, st in self.statwalk(files, match, dc, badmatch=badmatch):
373 for src, f, st in self.statwalk(files, match, dc, badmatch=badmatch):
374 yield src, f
374 yield src, f
375
375
376 # walk recursively through the directory tree, finding all files
376 # walk recursively through the directory tree, finding all files
377 # matched by the statmatch function
377 # matched by the statmatch function
378 #
378 #
379 # results are yielded in a tuple (src, filename, st), where src
379 # results are yielded in a tuple (src, filename, st), where src
380 # is one of:
380 # is one of:
381 # 'f' the file was found in the directory tree
381 # 'f' the file was found in the directory tree
382 # 'm' the file was only in the dirstate and not in the tree
382 # 'm' the file was only in the dirstate and not in the tree
383 # and st is the stat result if the file was found in the directory.
383 # and st is the stat result if the file was found in the directory.
384 #
384 #
385 # dc is an optional arg for the current dirstate. dc is not modified
385 # dc is an optional arg for the current dirstate. dc is not modified
386 # directly by this function, but might be modified by your statmatch call.
386 # directly by this function, but might be modified by your statmatch call.
387 #
387 #
388 def walkhelper(self, files, statmatch, dc, badmatch=None):
388 def walkhelper(self, files, statmatch, dc, badmatch=None):
389 # self.root may end with a path separator when self.root == '/'
389 # self.root may end with a path separator when self.root == '/'
390 common_prefix_len = len(self.root)
390 common_prefix_len = len(self.root)
391 if not self.root.endswith('/'):
391 if not self.root.endswith('/'):
392 common_prefix_len += 1
392 common_prefix_len += 1
393 # recursion free walker, faster than os.walk.
393 # recursion free walker, faster than os.walk.
394 def findfiles(s):
394 def findfiles(s):
395 work = [s]
395 work = [s]
396 while work:
396 while work:
397 top = work.pop()
397 top = work.pop()
398 names = os.listdir(top)
398 names = os.listdir(top)
399 names.sort()
399 names.sort()
400 # nd is the top of the repository dir tree
400 # nd is the top of the repository dir tree
401 nd = util.normpath(top[common_prefix_len:])
401 nd = util.normpath(top[common_prefix_len:])
402 if nd == '.':
402 if nd == '.':
403 nd = ''
403 nd = ''
404 else:
404 else:
405 # do not recurse into a repo contained in this
405 # do not recurse into a repo contained in this
406 # one. use bisect to find .hg directory so speed
406 # one. use bisect to find .hg directory so speed
407 # is good on big directory.
407 # is good on big directory.
408 hg = bisect.bisect_left(names, '.hg')
408 hg = bisect.bisect_left(names, '.hg')
409 if hg < len(names) and names[hg] == '.hg':
409 if hg < len(names) and names[hg] == '.hg':
410 if os.path.isdir(os.path.join(top, '.hg')):
410 if os.path.isdir(os.path.join(top, '.hg')):
411 continue
411 continue
412 for f in names:
412 for f in names:
413 np = util.pconvert(os.path.join(nd, f))
413 np = util.pconvert(os.path.join(nd, f))
414 if seen(np):
414 if seen(np):
415 continue
415 continue
416 p = os.path.join(top, f)
416 p = os.path.join(top, f)
417 # don't trip over symlinks
417 # don't trip over symlinks
418 st = os.lstat(p)
418 st = os.lstat(p)
419 if stat.S_ISDIR(st.st_mode):
419 if stat.S_ISDIR(st.st_mode):
420 ds = os.path.join(nd, f +'/')
420 ds = os.path.join(nd, f +'/')
421 if statmatch(ds, st):
421 if statmatch(ds, st):
422 work.append(p)
422 work.append(p)
423 if statmatch(np, st) and np in dc:
423 if statmatch(np, st) and np in dc:
424 yield 'm', np, st
424 yield 'm', np, st
425 elif statmatch(np, st):
425 elif statmatch(np, st):
426 if self.supported_type(np, st):
426 if self.supported_type(np, st):
427 yield 'f', np, st
427 yield 'f', np, st
428 elif np in dc:
428 elif np in dc:
429 yield 'm', np, st
429 yield 'm', np, st
430
430
431 known = {'.hg': 1}
431 known = {'.hg': 1}
432 def seen(fn):
432 def seen(fn):
433 if fn in known: return True
433 if fn in known: return True
434 known[fn] = 1
434 known[fn] = 1
435
435
436 # step one, find all files that match our criteria
436 # step one, find all files that match our criteria
437 files.sort()
437 files.sort()
438 for ff in util.unique(files):
438 for ff in util.unique(files):
439 f = self.wjoin(ff)
439 f = self.wjoin(ff)
440 try:
440 try:
441 st = os.lstat(f)
441 st = os.lstat(f)
442 except OSError, inst:
442 except OSError, inst:
443 nf = util.normpath(ff)
443 nf = util.normpath(ff)
444 found = False
444 found = False
445 for fn in dc:
445 for fn in dc:
446 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
446 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
447 found = True
447 found = True
448 break
448 break
449 if not found:
449 if not found:
450 if inst.errno != errno.ENOENT or not badmatch:
450 if inst.errno != errno.ENOENT or not badmatch:
451 self.ui.warn('%s: %s\n' % (
451 self.ui.warn('%s: %s\n' % (
452 util.pathto(self.getcwd(), ff),
452 util.pathto(self.getcwd(), ff),
453 inst.strerror))
453 inst.strerror))
454 elif badmatch and badmatch(ff) and statmatch(ff, None):
454 elif badmatch and badmatch(ff) and statmatch(ff, None):
455 yield 'b', ff, None
455 yield 'b', ff, None
456 continue
456 continue
457 if stat.S_ISDIR(st.st_mode):
457 if stat.S_ISDIR(st.st_mode):
458 cmp1 = (lambda x, y: cmp(x[1], y[1]))
458 cmp1 = (lambda x, y: cmp(x[1], y[1]))
459 sorted_ = [ x for x in findfiles(f) ]
459 sorted_ = [ x for x in findfiles(f) ]
460 sorted_.sort(cmp1)
460 sorted_.sort(cmp1)
461 for e in sorted_:
461 for e in sorted_:
462 yield e
462 yield e
463 else:
463 else:
464 ff = util.normpath(ff)
464 ff = util.normpath(ff)
465 if seen(ff):
465 if seen(ff):
466 continue
466 continue
467 self.blockignore = True
467 self.blockignore = True
468 if statmatch(ff, st):
468 if statmatch(ff, st):
469 if self.supported_type(ff, st, verbose=True):
469 if self.supported_type(ff, st, verbose=True):
470 yield 'f', ff, st
470 yield 'f', ff, st
471 elif ff in dc:
471 elif ff in dc:
472 yield 'm', ff, st
472 yield 'm', ff, st
473 self.blockignore = False
473 self.blockignore = False
474
474
475 # step two run through anything left in the dc hash and yield
475 # step two run through anything left in the dc hash and yield
476 # if we haven't already seen it
476 # if we haven't already seen it
477 ks = dc.keys()
477 ks = dc.keys()
478 ks.sort()
478 ks.sort()
479 for k in ks:
479 for k in ks:
480 if not seen(k) and (statmatch(k, None)):
480 if not seen(k) and (statmatch(k, None)):
481 yield 'm', k, None
481 yield 'm', k, None
482
482
483 def status(self, files=None, match=util.always, list_ignored=False,
483 def status(self, files=None, match=util.always, list_ignored=False,
484 list_clean=False):
484 list_clean=False):
485 lookup, modified, added, unknown, ignored = [], [], [], [], []
485 lookup, modified, added, unknown, ignored = [], [], [], [], []
486 removed, deleted, clean = [], [], []
486 removed, deleted, clean = [], [], []
487
487
488 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
488 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
489 try:
489 try:
490 type_, mode, size, time = self[fn]
490 type_, mode, size, time = self[fn]
491 except KeyError:
491 except KeyError:
492 if list_ignored and self.ignore(fn):
492 if list_ignored and self.ignore(fn):
493 ignored.append(fn)
493 ignored.append(fn)
494 else:
494 else:
495 unknown.append(fn)
495 unknown.append(fn)
496 continue
496 continue
497 if src == 'm':
497 if src == 'm':
498 nonexistent = True
498 nonexistent = True
499 if not st:
499 if not st:
500 try:
500 try:
501 st = os.lstat(self.wjoin(fn))
501 st = os.lstat(self.wjoin(fn))
502 except OSError, inst:
502 except OSError, inst:
503 if inst.errno != errno.ENOENT:
503 if inst.errno != errno.ENOENT:
504 raise
504 raise
505 st = None
505 st = None
506 # We need to re-check that it is a valid file
506 # We need to re-check that it is a valid file
507 if st and self.supported_type(fn, st):
507 if st and self.supported_type(fn, st):
508 nonexistent = False
508 nonexistent = False
509 # XXX: what to do with file no longer present in the fs
509 # XXX: what to do with file no longer present in the fs
510 # who are not removed in the dirstate ?
510 # who are not removed in the dirstate ?
511 if nonexistent and type_ in "nm":
511 if nonexistent and type_ in "nm":
512 deleted.append(fn)
512 deleted.append(fn)
513 continue
513 continue
514 # check the common case first
514 # check the common case first
515 if type_ == 'n':
515 if type_ == 'n':
516 if not st:
516 if not st:
517 st = os.lstat(self.wjoin(fn))
517 st = os.lstat(self.wjoin(fn))
518 if size >= 0 and (size != st.st_size
518 if size >= 0 and (size != st.st_size
519 or (mode ^ st.st_mode) & 0100):
519 or (mode ^ st.st_mode) & 0100):
520 modified.append(fn)
520 modified.append(fn)
521 elif time != st.st_mtime:
521 elif time != int(st.st_mtime):
522 lookup.append(fn)
522 lookup.append(fn)
523 elif list_clean:
523 elif list_clean:
524 clean.append(fn)
524 clean.append(fn)
525 elif type_ == 'm':
525 elif type_ == 'm':
526 modified.append(fn)
526 modified.append(fn)
527 elif type_ == 'a':
527 elif type_ == 'a':
528 added.append(fn)
528 added.append(fn)
529 elif type_ == 'r':
529 elif type_ == 'r':
530 removed.append(fn)
530 removed.append(fn)
531
531
532 return (lookup, modified, added, removed, deleted, unknown, ignored,
532 return (lookup, modified, added, removed, deleted, unknown, ignored,
533 clean)
533 clean)
General Comments 0
You need to be logged in to leave comments. Login now