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