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