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