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