comparison mercurial/hg.py @ 723:9e0f3ba4a9c2

Work on walk code.
author Bryan O'Sullivan <bos@serpentine.com>
date Sat, 16 Jul 2005 15:13:40 -0800
parents 574869103985
children 1c0c413cccdd
comparison
equal deleted inserted replaced
705:574869103985 723:9e0f3ba4a9c2
10 from revlog import * 10 from revlog import *
11 from demandload import * 11 from demandload import *
12 demandload(globals(), "re lock urllib urllib2 transaction time socket") 12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
13 demandload(globals(), "tempfile httprangereader bdiff") 13 demandload(globals(), "tempfile httprangereader bdiff")
14 demandload(globals(), "bisect select") 14 demandload(globals(), "bisect select")
15
16 def always(fn):
17 return True
15 18
16 class filelog(revlog): 19 class filelog(revlog):
17 def __init__(self, opener, path): 20 def __init__(self, opener, path):
18 revlog.__init__(self, opener, 21 revlog.__init__(self, opener,
19 os.path.join("data", path + ".i"), 22 os.path.join("data", path + ".i"),
274 self.dirty = 0 277 self.dirty = 0
275 self.ui = ui 278 self.ui = ui
276 self.map = None 279 self.map = None
277 self.pl = None 280 self.pl = None
278 self.copies = {} 281 self.copies = {}
282 self.ignorefunc = None
283
284 def wjoin(self, f):
285 return os.path.join(self.root, f)
286
287 def ignore(self, f):
288 if not self.ignorefunc:
289 bigpat = []
290 try:
291 l = file(self.wjoin(".hgignore"))
292 for pat in l:
293 if pat != "\n":
294 p = util.pconvert(pat[:-1])
295 try:
296 r = re.compile(p)
297 except:
298 self.ui.warn("ignoring invalid ignore"
299 + " regular expression '%s'\n" % p)
300 else:
301 bigpat.append(util.pconvert(pat[:-1]))
302 except IOError: pass
303
304 s = "(?:%s)" % (")|(?:".join(bigpat))
305 r = re.compile(s)
306 self.ignorefunc = r.search
307
308 return self.ignorefunc(f)
279 309
280 def __del__(self): 310 def __del__(self):
281 if self.dirty: 311 if self.dirty:
282 self.write() 312 self.write()
283 313
295 def parents(self): 325 def parents(self):
296 if not self.pl: 326 if not self.pl:
297 self.read() 327 self.read()
298 return self.pl 328 return self.pl
299 329
330 def markdirty(self):
331 if not self.dirty:
332 self.dirty = 1
333
300 def setparents(self, p1, p2 = nullid): 334 def setparents(self, p1, p2 = nullid):
301 self.dirty = 1 335 self.markdirty()
302 self.pl = p1, p2 336 self.pl = p1, p2
303 337
304 def state(self, key): 338 def state(self, key):
305 try: 339 try:
306 return self[key][0] 340 return self[key][0]
331 self.map[f] = e[:4] 365 self.map[f] = e[:4]
332 pos += l 366 pos += l
333 367
334 def copy(self, source, dest): 368 def copy(self, source, dest):
335 self.read() 369 self.read()
336 self.dirty = 1 370 self.markdirty()
337 self.copies[dest] = source 371 self.copies[dest] = source
338 372
339 def copied(self, file): 373 def copied(self, file):
340 return self.copies.get(file, None) 374 return self.copies.get(file, None)
341 375
346 r marked for removal 380 r marked for removal
347 a marked for addition''' 381 a marked for addition'''
348 382
349 if not files: return 383 if not files: return
350 self.read() 384 self.read()
351 self.dirty = 1 385 self.markdirty()
352 for f in files: 386 for f in files:
353 if state == "r": 387 if state == "r":
354 self.map[f] = ('r', 0, 0, 0) 388 self.map[f] = ('r', 0, 0, 0)
355 else: 389 else:
356 s = os.stat(os.path.join(self.root, f)) 390 s = os.stat(os.path.join(self.root, f))
357 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime) 391 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
358 392
359 def forget(self, files): 393 def forget(self, files):
360 if not files: return 394 if not files: return
361 self.read() 395 self.read()
362 self.dirty = 1 396 self.markdirty()
363 for f in files: 397 for f in files:
364 try: 398 try:
365 del self.map[f] 399 del self.map[f]
366 except KeyError: 400 except KeyError:
367 self.ui.warn("not in dirstate: %s!\n" % f) 401 self.ui.warn("not in dirstate: %s!\n" % f)
368 pass 402 pass
369 403
370 def clear(self): 404 def clear(self):
371 self.map = {} 405 self.map = {}
372 self.dirty = 1 406 self.markdirty()
373 407
374 def write(self): 408 def write(self):
375 st = self.opener("dirstate", "w") 409 st = self.opener("dirstate", "w")
376 st.write("".join(self.pl)) 410 st.write("".join(self.pl))
377 for f, e in self.map.items(): 411 for f, e in self.map.items():
380 f = f + "\0" + c 414 f = f + "\0" + c
381 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f)) 415 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
382 st.write(e + f) 416 st.write(e + f)
383 self.dirty = 0 417 self.dirty = 0
384 418
385 def changes(self, files, ignore): 419 def walk(self, files = None, match = always):
386 self.read() 420 self.read()
387 dc = self.map.copy() 421 dc = self.map.copy()
388 lookup, changed, added, unknown = [], [], [], [] 422 # walk all files by default
389
390 # compare all files by default
391 if not files: files = [self.root] 423 if not files: files = [self.root]
392 424 def traverse():
393 # recursive generator of all files listed
394 def walk(files):
395 for f in util.unique(files): 425 for f in util.unique(files):
396 f = os.path.join(self.root, f) 426 f = os.path.join(self.root, f)
397 if os.path.isdir(f): 427 if os.path.isdir(f):
398 for dir, subdirs, fl in os.walk(f): 428 for dir, subdirs, fl in os.walk(f):
399 d = dir[len(self.root) + 1:] 429 d = dir[len(self.root) + 1:]
430 if d == '.hg':
431 subdirs[:] = []
432 continue
400 for sd in subdirs: 433 for sd in subdirs:
401 if ignore(os.path.join(d, sd +'/')): 434 ds = os.path.join(d, sd +'/')
435 if self.ignore(ds) or not match(ds):
402 subdirs.remove(sd) 436 subdirs.remove(sd)
403 for fn in fl: 437 for fn in fl:
404 fn = util.pconvert(os.path.join(d, fn)) 438 fn = util.pconvert(os.path.join(d, fn))
405 yield fn 439 yield fn
406 else: 440 else:
407 yield f[len(self.root) + 1:] 441 yield f[len(self.root) + 1:]
408 442
409 for k in dc.keys(): 443 for k in dc.keys():
410 yield k 444 yield k
411 445
412 for fn in util.unique(walk(files)): 446 # yield only files that match: all in dirstate, others only if
447 # not in .hgignore
448
449 for fn in util.unique(traverse()):
450 if fn in dc:
451 del dc[fn]
452 elif self.ignore(fn):
453 continue
454 if match(fn):
455 yield fn
456
457 def changes(self, files = None, match = always):
458 self.read()
459 dc = self.map.copy()
460 lookup, changed, added, unknown = [], [], [], []
461
462 for fn in self.walk(files, match):
413 try: s = os.stat(os.path.join(self.root, fn)) 463 try: s = os.stat(os.path.join(self.root, fn))
414 except: continue 464 except: continue
415 465
416 if fn in dc: 466 if fn in dc:
417 c = dc[fn] 467 c = dc[fn]
426 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: 476 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
427 changed.append(fn) 477 changed.append(fn)
428 elif c[1] != s.st_mode or c[3] != s.st_mtime: 478 elif c[1] != s.st_mode or c[3] != s.st_mtime:
429 lookup.append(fn) 479 lookup.append(fn)
430 else: 480 else:
431 if not ignore(fn): unknown.append(fn) 481 if match(fn): unknown.append(fn)
432 482
433 return (lookup, changed, added, dc.keys(), unknown) 483 return (lookup, changed, added, dc.keys(), unknown)
434 484
435 # used to avoid circular references so destructors work 485 # used to avoid circular references so destructors work
436 def opener(base): 486 def opener(base):
490 540
491 self.opener = opener(self.path) 541 self.opener = opener(self.path)
492 self.wopener = opener(self.root) 542 self.wopener = opener(self.root)
493 self.manifest = manifest(self.opener) 543 self.manifest = manifest(self.opener)
494 self.changelog = changelog(self.opener) 544 self.changelog = changelog(self.opener)
495 self.ignorefunc = None
496 self.tagscache = None 545 self.tagscache = None
497 self.nodetagscache = None 546 self.nodetagscache = None
498 547
499 if not self.remote: 548 if not self.remote:
500 self.dirstate = dirstate(self.opener, ui, self.root) 549 self.dirstate = dirstate(self.opener, ui, self.root)
501 try: 550 try:
502 self.ui.readconfig(self.opener("hgrc")) 551 self.ui.readconfig(self.opener("hgrc"))
503 except IOError: pass 552 except IOError: pass
504
505 def ignore(self, f):
506 if not self.ignorefunc:
507 bigpat = ["^.hg/$"]
508 try:
509 l = file(self.wjoin(".hgignore"))
510 for pat in l:
511 if pat != "\n":
512 p = util.pconvert(pat[:-1])
513 try:
514 r = re.compile(p)
515 except:
516 self.ui.warn("ignoring invalid ignore"
517 + " regular expression '%s'\n" % p)
518 else:
519 bigpat.append(util.pconvert(pat[:-1]))
520 except IOError: pass
521
522 s = "(?:%s)" % (")|(?:".join(bigpat))
523 r = re.compile(s)
524 self.ignorefunc = r.search
525
526 return self.ignorefunc(f)
527 553
528 def hook(self, name, **args): 554 def hook(self, name, **args):
529 s = self.ui.config("hooks", name) 555 s = self.ui.config("hooks", name)
530 if s: 556 if s:
531 self.ui.note("running hook %s: %s\n" % (name, s)) 557 self.ui.note("running hook %s: %s\n" % (name, s))
735 elif s == 'r': 761 elif s == 'r':
736 remove.append(f) 762 remove.append(f)
737 else: 763 else:
738 self.ui.warn("%s not tracked!\n" % f) 764 self.ui.warn("%s not tracked!\n" % f)
739 else: 765 else:
740 (c, a, d, u) = self.changes(None, None) 766 (c, a, d, u) = self.changes()
741 commit = c + a 767 commit = c + a
742 remove = d 768 remove = d
743 769
744 if not commit and not remove: 770 if not commit and not remove:
745 self.ui.status("nothing changed\n") 771 self.ui.status("nothing changed\n")
812 self.dirstate.forget(remove) 838 self.dirstate.forget(remove)
813 839
814 if not self.hook("commit", node=hex(n)): 840 if not self.hook("commit", node=hex(n)):
815 return 1 841 return 1
816 842
817 def changes(self, node1, node2, files=None): 843 def walk(self, rev = None, files = [], match = always):
844 if rev is None: fns = self.dirstate.walk(files, match)
845 else: fns = filter(match, self.manifest.read(rev))
846 for fn in fns: yield fn
847
848 def changes(self, node1 = None, node2 = None, files = [], match = always):
818 mf2, u = None, [] 849 mf2, u = None, []
819 850
820 def fcmp(fn, mf): 851 def fcmp(fn, mf):
821 t1 = self.wfile(fn).read() 852 t1 = self.wfile(fn).read()
822 t2 = self.file(fn).revision(mf[fn]) 853 t2 = self.file(fn).revision(mf[fn])
823 return cmp(t1, t2) 854 return cmp(t1, t2)
824 855
856 def mfmatches(node):
857 mf = dict(self.manifest.read(node))
858 for fn in mf.keys():
859 if not match(fn):
860 del mf[fn]
861 return mf
862
825 # are we comparing the working directory? 863 # are we comparing the working directory?
826 if not node2: 864 if not node2:
827 l, c, a, d, u = self.dirstate.changes(files, self.ignore) 865 l, c, a, d, u = self.dirstate.changes(files, match)
828 866
829 # are we comparing working dir against its parent? 867 # are we comparing working dir against its parent?
830 if not node1: 868 if not node1:
831 if l: 869 if l:
832 # do a full compare of any files that might have changed 870 # do a full compare of any files that might have changed
833 change = self.changelog.read(self.dirstate.parents()[0]) 871 change = self.changelog.read(self.dirstate.parents()[0])
834 mf2 = self.manifest.read(change[0]) 872 mf2 = mfmatches(change[0])
835 for f in l: 873 for f in l:
836 if fcmp(f, mf2): 874 if fcmp(f, mf2):
837 c.append(f) 875 c.append(f)
838 876
839 for l in c, a, d, u: 877 for l in c, a, d, u:
844 # are we comparing working dir against non-tip? 882 # are we comparing working dir against non-tip?
845 # generate a pseudo-manifest for the working dir 883 # generate a pseudo-manifest for the working dir
846 if not node2: 884 if not node2:
847 if not mf2: 885 if not mf2:
848 change = self.changelog.read(self.dirstate.parents()[0]) 886 change = self.changelog.read(self.dirstate.parents()[0])
849 mf2 = self.manifest.read(change[0]).copy() 887 mf2 = mfmatches(change[0])
850 for f in a + c + l: 888 for f in a + c + l:
851 mf2[f] = "" 889 mf2[f] = ""
852 for f in d: 890 for f in d:
853 if f in mf2: del mf2[f] 891 if f in mf2: del mf2[f]
854 else: 892 else:
855 change = self.changelog.read(node2) 893 change = self.changelog.read(node2)
856 mf2 = self.manifest.read(change[0]) 894 mf2 = mfmatches(change[0])
857 895
858 # flush lists from dirstate before comparing manifests 896 # flush lists from dirstate before comparing manifests
859 c, a = [], [] 897 c, a = [], []
860 898
861 change = self.changelog.read(node1) 899 change = self.changelog.read(node1)
862 mf1 = self.manifest.read(change[0]).copy() 900 mf1 = mfmatches(change[0])
863 901
864 for fn in mf2: 902 for fn in mf2:
865 if mf1.has_key(fn): 903 if mf1.has_key(fn):
866 if mf1[fn] != mf2[fn]: 904 if mf1[fn] != mf2[fn]:
867 if mf2[fn] != "" or fcmp(fn, mf1): 905 if mf2[fn] != "" or fcmp(fn, mf1):
1265 m2 = self.manifest.read(m2n) 1303 m2 = self.manifest.read(m2n)
1266 mf2 = self.manifest.readflags(m2n) 1304 mf2 = self.manifest.readflags(m2n)
1267 ma = self.manifest.read(man) 1305 ma = self.manifest.read(man)
1268 mfa = self.manifest.readflags(man) 1306 mfa = self.manifest.readflags(man)
1269 1307
1270 (c, a, d, u) = self.changes(None, None) 1308 (c, a, d, u) = self.changes()
1271 1309
1272 # is this a jump, or a merge? i.e. is there a linear path 1310 # is this a jump, or a merge? i.e. is there a linear path
1273 # from p1 to p2? 1311 # from p1 to p2?
1274 linear_path = (pa == p1 or pa == p2) 1312 linear_path = (pa == p1 or pa == p2)
1275 1313