##// END OF EJS Templates
dirstate: add copies function...
Matt Mackall -
r3154:b1f10d32 default
parent child Browse files
Show More
@@ -1,533 +1,536 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.copymap = {}
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 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 copies[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.copies[dest] = source
196 self.copymap[dest] = source
197
197
198 def copied(self, file):
198 def copied(self, file):
199 return self.copies.get(file, None)
199 return self.copymap.get(file, None)
200
201 def copies(self):
202 return self.copymap
200
203
201 def initdirs(self):
204 def initdirs(self):
202 if self.dirs is None:
205 if self.dirs is None:
203 self.dirs = {}
206 self.dirs = {}
204 for f in self.map:
207 for f in self.map:
205 self.updatedirs(f, 1)
208 self.updatedirs(f, 1)
206
209
207 def updatedirs(self, path, delta):
210 def updatedirs(self, path, delta):
208 if self.dirs is not None:
211 if self.dirs is not None:
209 for c in strutil.findall(path, '/'):
212 for c in strutil.findall(path, '/'):
210 pc = path[:c]
213 pc = path[:c]
211 self.dirs.setdefault(pc, 0)
214 self.dirs.setdefault(pc, 0)
212 self.dirs[pc] += delta
215 self.dirs[pc] += delta
213
216
214 def checkshadows(self, files):
217 def checkshadows(self, files):
215 def prefixes(f):
218 def prefixes(f):
216 for c in strutil.rfindall(f, '/'):
219 for c in strutil.rfindall(f, '/'):
217 yield f[:c]
220 yield f[:c]
218 self.lazyread()
221 self.lazyread()
219 self.initdirs()
222 self.initdirs()
220 seendirs = {}
223 seendirs = {}
221 for f in files:
224 for f in files:
222 if self.dirs.get(f):
225 if self.dirs.get(f):
223 raise util.Abort(_('directory named %r already in dirstate') %
226 raise util.Abort(_('directory named %r already in dirstate') %
224 f)
227 f)
225 for d in prefixes(f):
228 for d in prefixes(f):
226 if d in seendirs:
229 if d in seendirs:
227 break
230 break
228 if d in self.map:
231 if d in self.map:
229 raise util.Abort(_('file named %r already in dirstate') %
232 raise util.Abort(_('file named %r already in dirstate') %
230 d)
233 d)
231 seendirs[d] = True
234 seendirs[d] = True
232
235
233 def update(self, files, state, **kw):
236 def update(self, files, state, **kw):
234 ''' current states:
237 ''' current states:
235 n normal
238 n normal
236 m needs merging
239 m needs merging
237 r marked for removal
240 r marked for removal
238 a marked for addition'''
241 a marked for addition'''
239
242
240 if not files: return
243 if not files: return
241 self.lazyread()
244 self.lazyread()
242 self.markdirty()
245 self.markdirty()
243 if state == "a":
246 if state == "a":
244 self.initdirs()
247 self.initdirs()
245 self.checkshadows(files)
248 self.checkshadows(files)
246 for f in files:
249 for f in files:
247 if state == "r":
250 if state == "r":
248 self.map[f] = ('r', 0, 0, 0)
251 self.map[f] = ('r', 0, 0, 0)
249 self.updatedirs(f, -1)
252 self.updatedirs(f, -1)
250 else:
253 else:
251 if state == "a":
254 if state == "a":
252 self.updatedirs(f, 1)
255 self.updatedirs(f, 1)
253 s = os.lstat(self.wjoin(f))
256 s = os.lstat(self.wjoin(f))
254 st_size = kw.get('st_size', s.st_size)
257 st_size = kw.get('st_size', s.st_size)
255 st_mtime = kw.get('st_mtime', s.st_mtime)
258 st_mtime = kw.get('st_mtime', s.st_mtime)
256 self.map[f] = (state, s.st_mode, st_size, st_mtime)
259 self.map[f] = (state, s.st_mode, st_size, st_mtime)
257 if self.copies.has_key(f):
260 if self.copymap.has_key(f):
258 del self.copies[f]
261 del self.copymap[f]
259
262
260 def forget(self, files):
263 def forget(self, files):
261 if not files: return
264 if not files: return
262 self.lazyread()
265 self.lazyread()
263 self.markdirty()
266 self.markdirty()
264 self.initdirs()
267 self.initdirs()
265 for f in files:
268 for f in files:
266 try:
269 try:
267 del self.map[f]
270 del self.map[f]
268 self.updatedirs(f, -1)
271 self.updatedirs(f, -1)
269 except KeyError:
272 except KeyError:
270 self.ui.warn(_("not in dirstate: %s!\n") % f)
273 self.ui.warn(_("not in dirstate: %s!\n") % f)
271 pass
274 pass
272
275
273 def clear(self):
276 def clear(self):
274 self.map = {}
277 self.map = {}
275 self.copies = {}
278 self.copymap = {}
276 self.dirs = None
279 self.dirs = None
277 self.markdirty()
280 self.markdirty()
278
281
279 def rebuild(self, parent, files):
282 def rebuild(self, parent, files):
280 self.clear()
283 self.clear()
281 umask = os.umask(0)
284 umask = os.umask(0)
282 os.umask(umask)
285 os.umask(umask)
283 for f in files:
286 for f in files:
284 if files.execf(f):
287 if files.execf(f):
285 self.map[f] = ('n', ~umask, -1, 0)
288 self.map[f] = ('n', ~umask, -1, 0)
286 else:
289 else:
287 self.map[f] = ('n', ~umask & 0666, -1, 0)
290 self.map[f] = ('n', ~umask & 0666, -1, 0)
288 self.pl = (parent, nullid)
291 self.pl = (parent, nullid)
289 self.markdirty()
292 self.markdirty()
290
293
291 def write(self):
294 def write(self):
292 if not self.dirty:
295 if not self.dirty:
293 return
296 return
294 st = self.opener("dirstate", "w", atomic=True)
297 st = self.opener("dirstate", "w", atomic=True)
295 st.write("".join(self.pl))
298 st.write("".join(self.pl))
296 for f, e in self.map.items():
299 for f, e in self.map.items():
297 c = self.copied(f)
300 c = self.copied(f)
298 if c:
301 if c:
299 f = f + "\0" + c
302 f = f + "\0" + c
300 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
303 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
301 st.write(e + f)
304 st.write(e + f)
302 self.dirty = 0
305 self.dirty = 0
303
306
304 def filterfiles(self, files):
307 def filterfiles(self, files):
305 ret = {}
308 ret = {}
306 unknown = []
309 unknown = []
307
310
308 for x in files:
311 for x in files:
309 if x == '.':
312 if x == '.':
310 return self.map.copy()
313 return self.map.copy()
311 if x not in self.map:
314 if x not in self.map:
312 unknown.append(x)
315 unknown.append(x)
313 else:
316 else:
314 ret[x] = self.map[x]
317 ret[x] = self.map[x]
315
318
316 if not unknown:
319 if not unknown:
317 return ret
320 return ret
318
321
319 b = self.map.keys()
322 b = self.map.keys()
320 b.sort()
323 b.sort()
321 blen = len(b)
324 blen = len(b)
322
325
323 for x in unknown:
326 for x in unknown:
324 bs = bisect.bisect(b, "%s%s" % (x, '/'))
327 bs = bisect.bisect(b, "%s%s" % (x, '/'))
325 while bs < blen:
328 while bs < blen:
326 s = b[bs]
329 s = b[bs]
327 if len(s) > len(x) and s.startswith(x):
330 if len(s) > len(x) and s.startswith(x):
328 ret[s] = self.map[s]
331 ret[s] = self.map[s]
329 else:
332 else:
330 break
333 break
331 bs += 1
334 bs += 1
332 return ret
335 return ret
333
336
334 def supported_type(self, f, st, verbose=False):
337 def supported_type(self, f, st, verbose=False):
335 if stat.S_ISREG(st.st_mode):
338 if stat.S_ISREG(st.st_mode):
336 return True
339 return True
337 if verbose:
340 if verbose:
338 kind = 'unknown'
341 kind = 'unknown'
339 if stat.S_ISCHR(st.st_mode): kind = _('character device')
342 if stat.S_ISCHR(st.st_mode): kind = _('character device')
340 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
343 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
341 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
344 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
342 elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
345 elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
343 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
346 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
344 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
347 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
345 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
348 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
346 util.pathto(self.getcwd(), f),
349 util.pathto(self.getcwd(), f),
347 kind))
350 kind))
348 return False
351 return False
349
352
350 def statwalk(self, files=None, match=util.always, dc=None, ignored=False,
353 def statwalk(self, files=None, match=util.always, dc=None, ignored=False,
351 badmatch=None):
354 badmatch=None):
352 self.lazyread()
355 self.lazyread()
353
356
354 # walk all files by default
357 # walk all files by default
355 if not files:
358 if not files:
356 files = [self.root]
359 files = [self.root]
357 if not dc:
360 if not dc:
358 dc = self.map.copy()
361 dc = self.map.copy()
359 elif not dc:
362 elif not dc:
360 dc = self.filterfiles(files)
363 dc = self.filterfiles(files)
361
364
362 def statmatch(file_, stat):
365 def statmatch(file_, stat):
363 file_ = util.pconvert(file_)
366 file_ = util.pconvert(file_)
364 if not ignored and file_ not in dc and self.ignore(file_):
367 if not ignored and file_ not in dc and self.ignore(file_):
365 return False
368 return False
366 return match(file_)
369 return match(file_)
367
370
368 return self.walkhelper(files=files, statmatch=statmatch, dc=dc,
371 return self.walkhelper(files=files, statmatch=statmatch, dc=dc,
369 badmatch=badmatch)
372 badmatch=badmatch)
370
373
371 def walk(self, files=None, match=util.always, dc=None, badmatch=None):
374 def walk(self, files=None, match=util.always, dc=None, badmatch=None):
372 # filter out the stat
375 # filter out the stat
373 for src, f, st in self.statwalk(files, match, dc, badmatch=badmatch):
376 for src, f, st in self.statwalk(files, match, dc, badmatch=badmatch):
374 yield src, f
377 yield src, f
375
378
376 # walk recursively through the directory tree, finding all files
379 # walk recursively through the directory tree, finding all files
377 # matched by the statmatch function
380 # matched by the statmatch function
378 #
381 #
379 # results are yielded in a tuple (src, filename, st), where src
382 # results are yielded in a tuple (src, filename, st), where src
380 # is one of:
383 # is one of:
381 # 'f' the file was found in the directory tree
384 # 'f' the file was found in the directory tree
382 # 'm' the file was only in the dirstate and not in the tree
385 # '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.
386 # and st is the stat result if the file was found in the directory.
384 #
387 #
385 # dc is an optional arg for the current dirstate. dc is not modified
388 # 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.
389 # directly by this function, but might be modified by your statmatch call.
387 #
390 #
388 def walkhelper(self, files, statmatch, dc, badmatch=None):
391 def walkhelper(self, files, statmatch, dc, badmatch=None):
389 # self.root may end with a path separator when self.root == '/'
392 # self.root may end with a path separator when self.root == '/'
390 common_prefix_len = len(self.root)
393 common_prefix_len = len(self.root)
391 if not self.root.endswith('/'):
394 if not self.root.endswith('/'):
392 common_prefix_len += 1
395 common_prefix_len += 1
393 # recursion free walker, faster than os.walk.
396 # recursion free walker, faster than os.walk.
394 def findfiles(s):
397 def findfiles(s):
395 work = [s]
398 work = [s]
396 while work:
399 while work:
397 top = work.pop()
400 top = work.pop()
398 names = os.listdir(top)
401 names = os.listdir(top)
399 names.sort()
402 names.sort()
400 # nd is the top of the repository dir tree
403 # nd is the top of the repository dir tree
401 nd = util.normpath(top[common_prefix_len:])
404 nd = util.normpath(top[common_prefix_len:])
402 if nd == '.':
405 if nd == '.':
403 nd = ''
406 nd = ''
404 else:
407 else:
405 # do not recurse into a repo contained in this
408 # do not recurse into a repo contained in this
406 # one. use bisect to find .hg directory so speed
409 # one. use bisect to find .hg directory so speed
407 # is good on big directory.
410 # is good on big directory.
408 hg = bisect.bisect_left(names, '.hg')
411 hg = bisect.bisect_left(names, '.hg')
409 if hg < len(names) and names[hg] == '.hg':
412 if hg < len(names) and names[hg] == '.hg':
410 if os.path.isdir(os.path.join(top, '.hg')):
413 if os.path.isdir(os.path.join(top, '.hg')):
411 continue
414 continue
412 for f in names:
415 for f in names:
413 np = util.pconvert(os.path.join(nd, f))
416 np = util.pconvert(os.path.join(nd, f))
414 if seen(np):
417 if seen(np):
415 continue
418 continue
416 p = os.path.join(top, f)
419 p = os.path.join(top, f)
417 # don't trip over symlinks
420 # don't trip over symlinks
418 st = os.lstat(p)
421 st = os.lstat(p)
419 if stat.S_ISDIR(st.st_mode):
422 if stat.S_ISDIR(st.st_mode):
420 ds = os.path.join(nd, f +'/')
423 ds = os.path.join(nd, f +'/')
421 if statmatch(ds, st):
424 if statmatch(ds, st):
422 work.append(p)
425 work.append(p)
423 if statmatch(np, st) and np in dc:
426 if statmatch(np, st) and np in dc:
424 yield 'm', np, st
427 yield 'm', np, st
425 elif statmatch(np, st):
428 elif statmatch(np, st):
426 if self.supported_type(np, st):
429 if self.supported_type(np, st):
427 yield 'f', np, st
430 yield 'f', np, st
428 elif np in dc:
431 elif np in dc:
429 yield 'm', np, st
432 yield 'm', np, st
430
433
431 known = {'.hg': 1}
434 known = {'.hg': 1}
432 def seen(fn):
435 def seen(fn):
433 if fn in known: return True
436 if fn in known: return True
434 known[fn] = 1
437 known[fn] = 1
435
438
436 # step one, find all files that match our criteria
439 # step one, find all files that match our criteria
437 files.sort()
440 files.sort()
438 for ff in util.unique(files):
441 for ff in util.unique(files):
439 f = self.wjoin(ff)
442 f = self.wjoin(ff)
440 try:
443 try:
441 st = os.lstat(f)
444 st = os.lstat(f)
442 except OSError, inst:
445 except OSError, inst:
443 nf = util.normpath(ff)
446 nf = util.normpath(ff)
444 found = False
447 found = False
445 for fn in dc:
448 for fn in dc:
446 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
449 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
447 found = True
450 found = True
448 break
451 break
449 if not found:
452 if not found:
450 if inst.errno != errno.ENOENT or not badmatch:
453 if inst.errno != errno.ENOENT or not badmatch:
451 self.ui.warn('%s: %s\n' % (
454 self.ui.warn('%s: %s\n' % (
452 util.pathto(self.getcwd(), ff),
455 util.pathto(self.getcwd(), ff),
453 inst.strerror))
456 inst.strerror))
454 elif badmatch and badmatch(ff) and statmatch(ff, None):
457 elif badmatch and badmatch(ff) and statmatch(ff, None):
455 yield 'b', ff, None
458 yield 'b', ff, None
456 continue
459 continue
457 if stat.S_ISDIR(st.st_mode):
460 if stat.S_ISDIR(st.st_mode):
458 cmp1 = (lambda x, y: cmp(x[1], y[1]))
461 cmp1 = (lambda x, y: cmp(x[1], y[1]))
459 sorted_ = [ x for x in findfiles(f) ]
462 sorted_ = [ x for x in findfiles(f) ]
460 sorted_.sort(cmp1)
463 sorted_.sort(cmp1)
461 for e in sorted_:
464 for e in sorted_:
462 yield e
465 yield e
463 else:
466 else:
464 ff = util.normpath(ff)
467 ff = util.normpath(ff)
465 if seen(ff):
468 if seen(ff):
466 continue
469 continue
467 self.blockignore = True
470 self.blockignore = True
468 if statmatch(ff, st):
471 if statmatch(ff, st):
469 if self.supported_type(ff, st, verbose=True):
472 if self.supported_type(ff, st, verbose=True):
470 yield 'f', ff, st
473 yield 'f', ff, st
471 elif ff in dc:
474 elif ff in dc:
472 yield 'm', ff, st
475 yield 'm', ff, st
473 self.blockignore = False
476 self.blockignore = False
474
477
475 # step two run through anything left in the dc hash and yield
478 # step two run through anything left in the dc hash and yield
476 # if we haven't already seen it
479 # if we haven't already seen it
477 ks = dc.keys()
480 ks = dc.keys()
478 ks.sort()
481 ks.sort()
479 for k in ks:
482 for k in ks:
480 if not seen(k) and (statmatch(k, None)):
483 if not seen(k) and (statmatch(k, None)):
481 yield 'm', k, None
484 yield 'm', k, None
482
485
483 def status(self, files=None, match=util.always, list_ignored=False,
486 def status(self, files=None, match=util.always, list_ignored=False,
484 list_clean=False):
487 list_clean=False):
485 lookup, modified, added, unknown, ignored = [], [], [], [], []
488 lookup, modified, added, unknown, ignored = [], [], [], [], []
486 removed, deleted, clean = [], [], []
489 removed, deleted, clean = [], [], []
487
490
488 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
491 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
489 try:
492 try:
490 type_, mode, size, time = self[fn]
493 type_, mode, size, time = self[fn]
491 except KeyError:
494 except KeyError:
492 if list_ignored and self.ignore(fn):
495 if list_ignored and self.ignore(fn):
493 ignored.append(fn)
496 ignored.append(fn)
494 else:
497 else:
495 unknown.append(fn)
498 unknown.append(fn)
496 continue
499 continue
497 if src == 'm':
500 if src == 'm':
498 nonexistent = True
501 nonexistent = True
499 if not st:
502 if not st:
500 try:
503 try:
501 st = os.lstat(self.wjoin(fn))
504 st = os.lstat(self.wjoin(fn))
502 except OSError, inst:
505 except OSError, inst:
503 if inst.errno != errno.ENOENT:
506 if inst.errno != errno.ENOENT:
504 raise
507 raise
505 st = None
508 st = None
506 # We need to re-check that it is a valid file
509 # We need to re-check that it is a valid file
507 if st and self.supported_type(fn, st):
510 if st and self.supported_type(fn, st):
508 nonexistent = False
511 nonexistent = False
509 # XXX: what to do with file no longer present in the fs
512 # XXX: what to do with file no longer present in the fs
510 # who are not removed in the dirstate ?
513 # who are not removed in the dirstate ?
511 if nonexistent and type_ in "nm":
514 if nonexistent and type_ in "nm":
512 deleted.append(fn)
515 deleted.append(fn)
513 continue
516 continue
514 # check the common case first
517 # check the common case first
515 if type_ == 'n':
518 if type_ == 'n':
516 if not st:
519 if not st:
517 st = os.lstat(self.wjoin(fn))
520 st = os.lstat(self.wjoin(fn))
518 if size >= 0 and (size != st.st_size
521 if size >= 0 and (size != st.st_size
519 or (mode ^ st.st_mode) & 0100):
522 or (mode ^ st.st_mode) & 0100):
520 modified.append(fn)
523 modified.append(fn)
521 elif time != int(st.st_mtime):
524 elif time != int(st.st_mtime):
522 lookup.append(fn)
525 lookup.append(fn)
523 elif list_clean:
526 elif list_clean:
524 clean.append(fn)
527 clean.append(fn)
525 elif type_ == 'm':
528 elif type_ == 'm':
526 modified.append(fn)
529 modified.append(fn)
527 elif type_ == 'a':
530 elif type_ == 'a':
528 added.append(fn)
531 added.append(fn)
529 elif type_ == 'r':
532 elif type_ == 'r':
530 removed.append(fn)
533 removed.append(fn)
531
534
532 return (lookup, modified, added, removed, deleted, unknown, ignored,
535 return (lookup, modified, added, removed, deleted, unknown, ignored,
533 clean)
536 clean)
General Comments 0
You need to be logged in to leave comments. Login now