##// END OF EJS Templates
dirstate: translate forgotten string
Simon Heimberg -
r8310:8417d82d default
parent child Browse files
Show More
@@ -1,591 +1,591 b''
1 1 # dirstate.py - working directory tracking for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 from node import nullid
9 9 from i18n import _
10 10 import struct, os, stat, util, errno, ignore
11 11 import cStringIO, osutil, sys, parsers
12 12
13 13 _unknown = ('?', 0, 0, 0)
14 14 _format = ">cllll"
15 15 propertycache = util.propertycache
16 16
17 17 def _finddirs(path):
18 18 pos = path.rfind('/')
19 19 while pos != -1:
20 20 yield path[:pos]
21 21 pos = path.rfind('/', 0, pos)
22 22
23 23 def _incdirs(dirs, path):
24 24 for base in _finddirs(path):
25 25 if base in dirs:
26 26 dirs[base] += 1
27 27 return
28 28 dirs[base] = 1
29 29
30 30 def _decdirs(dirs, path):
31 31 for base in _finddirs(path):
32 32 if dirs[base] > 1:
33 33 dirs[base] -= 1
34 34 return
35 35 del dirs[base]
36 36
37 37 class dirstate(object):
38 38
39 39 def __init__(self, opener, ui, root):
40 40 self._opener = opener
41 41 self._root = root
42 42 self._rootdir = os.path.join(root, '')
43 43 self._dirty = False
44 44 self._dirtypl = False
45 45 self._ui = ui
46 46
47 47 @propertycache
48 48 def _map(self):
49 49 self._read()
50 50 return self._map
51 51
52 52 @propertycache
53 53 def _copymap(self):
54 54 self._read()
55 55 return self._copymap
56 56
57 57 @propertycache
58 58 def _foldmap(self):
59 59 f = {}
60 60 for name in self._map:
61 61 f[os.path.normcase(name)] = name
62 62 return f
63 63
64 64 @propertycache
65 65 def _branch(self):
66 66 try:
67 67 return self._opener("branch").read().strip() or "default"
68 68 except IOError:
69 69 return "default"
70 70
71 71 @propertycache
72 72 def _pl(self):
73 73 try:
74 74 st = self._opener("dirstate").read(40)
75 75 if len(st) == 40:
76 76 return st[:20], st[20:40]
77 77 except IOError, err:
78 78 if err.errno != errno.ENOENT: raise
79 79 return [nullid, nullid]
80 80
81 81 @propertycache
82 82 def _dirs(self):
83 83 dirs = {}
84 84 for f,s in self._map.iteritems():
85 85 if s[0] != 'r':
86 86 _incdirs(dirs, f)
87 87 return dirs
88 88
89 89 @propertycache
90 90 def _ignore(self):
91 91 files = [self._join('.hgignore')]
92 92 for name, path in self._ui.configitems("ui"):
93 93 if name == 'ignore' or name.startswith('ignore.'):
94 94 files.append(os.path.expanduser(path))
95 95 return ignore.ignore(self._root, files, self._ui.warn)
96 96
97 97 @propertycache
98 98 def _slash(self):
99 99 return self._ui.configbool('ui', 'slash') and os.sep != '/'
100 100
101 101 @propertycache
102 102 def _checklink(self):
103 103 return util.checklink(self._root)
104 104
105 105 @propertycache
106 106 def _checkexec(self):
107 107 return util.checkexec(self._root)
108 108
109 109 @propertycache
110 110 def _checkcase(self):
111 111 return not util.checkcase(self._join('.hg'))
112 112
113 113 @propertycache
114 114 def normalize(self):
115 115 if self._checkcase:
116 116 return self._normalize
117 117 return lambda x, y=False: x
118 118
119 119 def _join(self, f):
120 120 # much faster than os.path.join()
121 121 # it's safe because f is always a relative path
122 122 return self._rootdir + f
123 123
124 124 def flagfunc(self, fallback):
125 125 if self._checklink:
126 126 if self._checkexec:
127 127 def f(x):
128 128 p = self._join(x)
129 129 if os.path.islink(p):
130 130 return 'l'
131 131 if util.is_exec(p):
132 132 return 'x'
133 133 return ''
134 134 return f
135 135 def f(x):
136 136 if os.path.islink(self._join(x)):
137 137 return 'l'
138 138 if 'x' in fallback(x):
139 139 return 'x'
140 140 return ''
141 141 return f
142 142 if self._checkexec:
143 143 def f(x):
144 144 if 'l' in fallback(x):
145 145 return 'l'
146 146 if util.is_exec(self._join(x)):
147 147 return 'x'
148 148 return ''
149 149 return f
150 150 return fallback
151 151
152 152 def getcwd(self):
153 153 cwd = os.getcwd()
154 154 if cwd == self._root: return ''
155 155 # self._root ends with a path separator if self._root is '/' or 'C:\'
156 156 rootsep = self._root
157 157 if not util.endswithsep(rootsep):
158 158 rootsep += os.sep
159 159 if cwd.startswith(rootsep):
160 160 return cwd[len(rootsep):]
161 161 else:
162 162 # we're outside the repo. return an absolute path.
163 163 return cwd
164 164
165 165 def pathto(self, f, cwd=None):
166 166 if cwd is None:
167 167 cwd = self.getcwd()
168 168 path = util.pathto(self._root, cwd, f)
169 169 if self._slash:
170 170 return util.normpath(path)
171 171 return path
172 172
173 173 def __getitem__(self, key):
174 174 ''' current states:
175 175 n normal
176 176 m needs merging
177 177 r marked for removal
178 178 a marked for addition
179 179 ? not tracked'''
180 180 return self._map.get(key, ("?",))[0]
181 181
182 182 def __contains__(self, key):
183 183 return key in self._map
184 184
185 185 def __iter__(self):
186 186 for x in sorted(self._map):
187 187 yield x
188 188
189 189 def parents(self):
190 190 return self._pl
191 191
192 192 def branch(self):
193 193 return self._branch
194 194
195 195 def setparents(self, p1, p2=nullid):
196 196 self._dirty = self._dirtypl = True
197 197 self._pl = p1, p2
198 198
199 199 def setbranch(self, branch):
200 200 self._branch = branch
201 201 self._opener("branch", "w").write(branch + '\n')
202 202
203 203 def _read(self):
204 204 self._map = {}
205 205 self._copymap = {}
206 206 try:
207 207 st = self._opener("dirstate").read()
208 208 except IOError, err:
209 209 if err.errno != errno.ENOENT: raise
210 210 return
211 211 if not st:
212 212 return
213 213
214 214 p = parsers.parse_dirstate(self._map, self._copymap, st)
215 215 if not self._dirtypl:
216 216 self._pl = p
217 217
218 218 def invalidate(self):
219 219 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
220 220 if a in self.__dict__:
221 221 delattr(self, a)
222 222 self._dirty = False
223 223
224 224 def copy(self, source, dest):
225 225 """Mark dest as a copy of source. Unmark dest if source is None.
226 226 """
227 227 if source == dest:
228 228 return
229 229 self._dirty = True
230 230 if source is not None:
231 231 self._copymap[dest] = source
232 232 elif dest in self._copymap:
233 233 del self._copymap[dest]
234 234
235 235 def copied(self, file):
236 236 return self._copymap.get(file, None)
237 237
238 238 def copies(self):
239 239 return self._copymap
240 240
241 241 def _droppath(self, f):
242 242 if self[f] not in "?r" and "_dirs" in self.__dict__:
243 243 _decdirs(self._dirs, f)
244 244
245 245 def _addpath(self, f, check=False):
246 246 oldstate = self[f]
247 247 if check or oldstate == "r":
248 248 if '\r' in f or '\n' in f:
249 249 raise util.Abort(
250 250 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
251 251 if f in self._dirs:
252 252 raise util.Abort(_('directory %r already in dirstate') % f)
253 253 # shadows
254 254 for d in _finddirs(f):
255 255 if d in self._dirs:
256 256 break
257 257 if d in self._map and self[d] != 'r':
258 258 raise util.Abort(
259 259 _('file %r in dirstate clashes with %r') % (d, f))
260 260 if oldstate in "?r" and "_dirs" in self.__dict__:
261 261 _incdirs(self._dirs, f)
262 262
263 263 def normal(self, f):
264 264 'mark a file normal and clean'
265 265 self._dirty = True
266 266 self._addpath(f)
267 267 s = os.lstat(self._join(f))
268 268 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
269 269 if f in self._copymap:
270 270 del self._copymap[f]
271 271
272 272 def normallookup(self, f):
273 273 'mark a file normal, but possibly dirty'
274 274 if self._pl[1] != nullid and f in self._map:
275 275 # if there is a merge going on and the file was either
276 276 # in state 'm' or dirty before being removed, restore that state.
277 277 entry = self._map[f]
278 278 if entry[0] == 'r' and entry[2] in (-1, -2):
279 279 source = self._copymap.get(f)
280 280 if entry[2] == -1:
281 281 self.merge(f)
282 282 elif entry[2] == -2:
283 283 self.normaldirty(f)
284 284 if source:
285 285 self.copy(source, f)
286 286 return
287 287 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
288 288 return
289 289 self._dirty = True
290 290 self._addpath(f)
291 291 self._map[f] = ('n', 0, -1, -1)
292 292 if f in self._copymap:
293 293 del self._copymap[f]
294 294
295 295 def normaldirty(self, f):
296 296 'mark a file normal, but dirty'
297 297 self._dirty = True
298 298 self._addpath(f)
299 299 self._map[f] = ('n', 0, -2, -1)
300 300 if f in self._copymap:
301 301 del self._copymap[f]
302 302
303 303 def add(self, f):
304 304 'mark a file added'
305 305 self._dirty = True
306 306 self._addpath(f, True)
307 307 self._map[f] = ('a', 0, -1, -1)
308 308 if f in self._copymap:
309 309 del self._copymap[f]
310 310
311 311 def remove(self, f):
312 312 'mark a file removed'
313 313 self._dirty = True
314 314 self._droppath(f)
315 315 size = 0
316 316 if self._pl[1] != nullid and f in self._map:
317 317 entry = self._map[f]
318 318 if entry[0] == 'm':
319 319 size = -1
320 320 elif entry[0] == 'n' and entry[2] == -2:
321 321 size = -2
322 322 self._map[f] = ('r', 0, size, 0)
323 323 if size == 0 and f in self._copymap:
324 324 del self._copymap[f]
325 325
326 326 def merge(self, f):
327 327 'mark a file merged'
328 328 self._dirty = True
329 329 s = os.lstat(self._join(f))
330 330 self._addpath(f)
331 331 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
332 332 if f in self._copymap:
333 333 del self._copymap[f]
334 334
335 335 def forget(self, f):
336 336 'forget a file'
337 337 self._dirty = True
338 338 try:
339 339 self._droppath(f)
340 340 del self._map[f]
341 341 except KeyError:
342 342 self._ui.warn(_("not in dirstate: %s\n") % f)
343 343
344 344 def _normalize(self, path, knownpath=False):
345 345 norm_path = os.path.normcase(path)
346 346 fold_path = self._foldmap.get(norm_path, None)
347 347 if fold_path is None:
348 348 if knownpath or not os.path.exists(os.path.join(self._root, path)):
349 349 fold_path = path
350 350 else:
351 351 fold_path = self._foldmap.setdefault(norm_path,
352 352 util.fspath(path, self._root))
353 353 return fold_path
354 354
355 355 def clear(self):
356 356 self._map = {}
357 357 if "_dirs" in self.__dict__:
358 358 delattr(self, "_dirs");
359 359 self._copymap = {}
360 360 self._pl = [nullid, nullid]
361 361 self._dirty = True
362 362
363 363 def rebuild(self, parent, files):
364 364 self.clear()
365 365 for f in files:
366 366 if 'x' in files.flags(f):
367 367 self._map[f] = ('n', 0777, -1, 0)
368 368 else:
369 369 self._map[f] = ('n', 0666, -1, 0)
370 370 self._pl = (parent, nullid)
371 371 self._dirty = True
372 372
373 373 def write(self):
374 374 if not self._dirty:
375 375 return
376 376 st = self._opener("dirstate", "w", atomictemp=True)
377 377
378 378 try:
379 379 gran = int(self._ui.config('dirstate', 'granularity', 1))
380 380 except ValueError:
381 381 gran = 1
382 382 limit = sys.maxint
383 383 if gran > 0:
384 384 limit = util.fstat(st).st_mtime - gran
385 385
386 386 cs = cStringIO.StringIO()
387 387 copymap = self._copymap
388 388 pack = struct.pack
389 389 write = cs.write
390 390 write("".join(self._pl))
391 391 for f, e in self._map.iteritems():
392 392 if f in copymap:
393 393 f = "%s\0%s" % (f, copymap[f])
394 394 if e[3] > limit and e[0] == 'n':
395 395 e = (e[0], 0, -1, -1)
396 396 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
397 397 write(e)
398 398 write(f)
399 399 st.write(cs.getvalue())
400 400 st.rename()
401 401 self._dirty = self._dirtypl = False
402 402
403 403 def _dirignore(self, f):
404 404 if f == '.':
405 405 return False
406 406 if self._ignore(f):
407 407 return True
408 408 for p in _finddirs(f):
409 409 if self._ignore(p):
410 410 return True
411 411 return False
412 412
413 413 def walk(self, match, unknown, ignored):
414 414 '''
415 415 walk recursively through the directory tree, finding all files
416 416 matched by the match function
417 417
418 418 results are yielded in a tuple (filename, stat), where stat
419 419 and st is the stat result if the file was found in the directory.
420 420 '''
421 421
422 422 def fwarn(f, msg):
423 423 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
424 424 return False
425 425 badfn = fwarn
426 426 if hasattr(match, 'bad'):
427 427 badfn = match.bad
428 428
429 429 def badtype(f, mode):
430 kind = 'unknown'
430 kind = _('unknown')
431 431 if stat.S_ISCHR(mode): kind = _('character device')
432 432 elif stat.S_ISBLK(mode): kind = _('block device')
433 433 elif stat.S_ISFIFO(mode): kind = _('fifo')
434 434 elif stat.S_ISSOCK(mode): kind = _('socket')
435 435 elif stat.S_ISDIR(mode): kind = _('directory')
436 436 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
437 437 % (self.pathto(f), kind))
438 438
439 439 ignore = self._ignore
440 440 dirignore = self._dirignore
441 441 if ignored:
442 442 ignore = util.never
443 443 dirignore = util.never
444 444 elif not unknown:
445 445 # if unknown and ignored are False, skip step 2
446 446 ignore = util.always
447 447 dirignore = util.always
448 448
449 449 matchfn = match.matchfn
450 450 dmap = self._map
451 451 normpath = util.normpath
452 452 normalize = self.normalize
453 453 listdir = osutil.listdir
454 454 lstat = os.lstat
455 455 getkind = stat.S_IFMT
456 456 dirkind = stat.S_IFDIR
457 457 regkind = stat.S_IFREG
458 458 lnkkind = stat.S_IFLNK
459 459 join = self._join
460 460 work = []
461 461 wadd = work.append
462 462
463 463 files = set(match.files())
464 464 if not files or '.' in files:
465 465 files = ['']
466 466 results = {'.hg': None}
467 467
468 468 # step 1: find all explicit files
469 469 for ff in sorted(files):
470 470 nf = normalize(normpath(ff))
471 471 if nf in results:
472 472 continue
473 473
474 474 try:
475 475 st = lstat(join(nf))
476 476 kind = getkind(st.st_mode)
477 477 if kind == dirkind:
478 478 if not dirignore(nf):
479 479 wadd(nf)
480 480 elif kind == regkind or kind == lnkkind:
481 481 results[nf] = st
482 482 else:
483 483 badtype(ff, kind)
484 484 if nf in dmap:
485 485 results[nf] = None
486 486 except OSError, inst:
487 487 keep = False
488 488 prefix = nf + "/"
489 489 for fn in dmap:
490 490 if nf == fn or fn.startswith(prefix):
491 491 keep = True
492 492 break
493 493 if not keep:
494 494 if inst.errno != errno.ENOENT:
495 495 fwarn(ff, inst.strerror)
496 496 elif badfn(ff, inst.strerror):
497 497 if (nf in dmap or not ignore(nf)) and matchfn(nf):
498 498 results[nf] = None
499 499
500 500 # step 2: visit subdirectories
501 501 while work:
502 502 nd = work.pop()
503 503 if hasattr(match, 'dir'):
504 504 match.dir(nd)
505 505 skip = None
506 506 if nd == '.':
507 507 nd = ''
508 508 else:
509 509 skip = '.hg'
510 510 try:
511 511 entries = listdir(join(nd), stat=True, skip=skip)
512 512 except OSError, inst:
513 513 if inst.errno == errno.EACCES:
514 514 fwarn(nd, inst.strerror)
515 515 continue
516 516 raise
517 517 for f, kind, st in entries:
518 518 nf = normalize(nd and (nd + "/" + f) or f, True)
519 519 if nf not in results:
520 520 if kind == dirkind:
521 521 if not ignore(nf):
522 522 wadd(nf)
523 523 if nf in dmap and matchfn(nf):
524 524 results[nf] = None
525 525 elif kind == regkind or kind == lnkkind:
526 526 if nf in dmap:
527 527 if matchfn(nf):
528 528 results[nf] = st
529 529 elif matchfn(nf) and not ignore(nf):
530 530 results[nf] = st
531 531 elif nf in dmap and matchfn(nf):
532 532 results[nf] = None
533 533
534 534 # step 3: report unseen items in the dmap hash
535 535 visit = sorted([f for f in dmap if f not in results and match(f)])
536 536 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
537 537 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
538 538 st = None
539 539 results[nf] = st
540 540
541 541 del results['.hg']
542 542 return results
543 543
544 544 def status(self, match, ignored, clean, unknown):
545 545 listignored, listclean, listunknown = ignored, clean, unknown
546 546 lookup, modified, added, unknown, ignored = [], [], [], [], []
547 547 removed, deleted, clean = [], [], []
548 548
549 549 dmap = self._map
550 550 ladd = lookup.append
551 551 madd = modified.append
552 552 aadd = added.append
553 553 uadd = unknown.append
554 554 iadd = ignored.append
555 555 radd = removed.append
556 556 dadd = deleted.append
557 557 cadd = clean.append
558 558
559 559 for fn, st in self.walk(match, listunknown, listignored).iteritems():
560 560 if fn not in dmap:
561 561 if (listignored or match.exact(fn)) and self._dirignore(fn):
562 562 if listignored:
563 563 iadd(fn)
564 564 elif listunknown:
565 565 uadd(fn)
566 566 continue
567 567
568 568 state, mode, size, time = dmap[fn]
569 569
570 570 if not st and state in "nma":
571 571 dadd(fn)
572 572 elif state == 'n':
573 573 if (size >= 0 and
574 574 (size != st.st_size
575 575 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
576 576 or size == -2
577 577 or fn in self._copymap):
578 578 madd(fn)
579 579 elif time != int(st.st_mtime):
580 580 ladd(fn)
581 581 elif listclean:
582 582 cadd(fn)
583 583 elif state == 'm':
584 584 madd(fn)
585 585 elif state == 'a':
586 586 aadd(fn)
587 587 elif state == 'r':
588 588 radd(fn)
589 589
590 590 return (lookup, modified, added, removed, deleted, unknown, ignored,
591 591 clean)
General Comments 0
You need to be logged in to leave comments. Login now