comparison mercurial/hg.py @ 536:c15b4bc0a11c

Refactor diffrevs/diffdir into changes -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Refactor diffrevs/diffdir into changes Add dirstate.changes to replace most of diffdir Add localrepository.changes to replace diffrevs/diffdir This code can now efficiently check for changes in single files, and often without consulting the manifest. This should eventually make 'hg diff Makefile' in a large project much faster. This also fixes a bug where 'hg diff -r tip' failed to account for files that had been added but not committed yet. manifest hash: 20fde5d4b4cee49a76bcfe50f2dacf58b1f2258b -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (GNU/Linux) iD8DBQFCxMxpywK+sNU5EO8RAhzOAJ9VLQJoC+hiRYQtTSPbDhXBEJfQZwCgpDx9 GAwQ9jZHNsgXckBfXNCkJV8= =hMuc -----END PGP SIGNATURE-----
author mpm@selenic.com
date Thu, 30 Jun 2005 20:54:01 -0800
parents aace5b681fe9
children 411e05b04ffa
comparison
equal deleted inserted replaced
535:fba26990604a 536:c15b4bc0a11c
286 f = f + "\0" + c 286 f = f + "\0" + c
287 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f)) 287 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
288 st.write(e + f) 288 st.write(e + f)
289 self.dirty = 0 289 self.dirty = 0
290 290
291 def dup(self): 291 def changes(self, files, ignore):
292 self.read() 292 self.read()
293 return self.map.copy() 293 dc = self.map.copy()
294 lookup, changed, added, unknown = [], [], [], []
295
296 # compare all files by default
297 if not files: files = [self.root]
298
299 def uniq(g):
300 seen = {}
301 for f in g:
302 if f not in seen:
303 seen[f] = 1
304 yield f
305
306 # recursive generator of all files listed
307 def walk(files):
308 for f in uniq(files):
309 if os.path.isdir(f):
310 for dir, subdirs, fl in os.walk(f):
311 d = dir[len(self.root) + 1:]
312 if ".hg" in subdirs: subdirs.remove(".hg")
313 for fn in fl:
314 fn = util.pconvert(os.path.join(d, fn))
315 yield fn
316 else:
317 yield f[len(self.root) + 1:]
318
319 for fn in uniq(walk(files)):
320 try: s = os.stat(os.path.join(self.root, fn))
321 except: continue
322
323 if fn in dc:
324 c = dc[fn]
325 del dc[fn]
326
327 if c[0] == 'm':
328 changed.append(fn)
329 elif c[0] == 'a':
330 added.append(fn)
331 elif c[0] == 'r':
332 unknown.append(fn)
333 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
334 changed.append(fn)
335 elif c[1] != s.st_mode or c[3] != s.st_mtime:
336 lookup.append(fn)
337 else:
338 if not ignore(fn): unknown.append(fn)
339
340 return (lookup, changed, added, dc.keys(), unknown)
294 341
295 # used to avoid circular references so destructors work 342 # used to avoid circular references so destructors work
296 def opener(base): 343 def opener(base):
297 p = base 344 p = base
298 def o(path, mode="r"): 345 def o(path, mode="r"):
566 elif s == 'r': 613 elif s == 'r':
567 remove.append(f) 614 remove.append(f)
568 else: 615 else:
569 self.ui.warn("%s not tracked!\n" % f) 616 self.ui.warn("%s not tracked!\n" % f)
570 else: 617 else:
571 (c, a, d, u) = self.diffdir(self.root) 618 (c, a, d, u) = self.changes(None, None)
572 commit = c + a 619 commit = c + a
573 remove = d 620 remove = d
574 621
575 if not commit and not remove: 622 if not commit and not remove:
576 self.ui.status("nothing changed\n") 623 self.ui.status("nothing changed\n")
642 689
643 self.dirstate.setparents(n) 690 self.dirstate.setparents(n)
644 self.dirstate.update(new, "n") 691 self.dirstate.update(new, "n")
645 self.dirstate.forget(remove) 692 self.dirstate.forget(remove)
646 693
647 def diffdir(self, path, changeset = None): 694 def changes(self, node1, node2, *files):
648 changed = [] 695 # changed, added, deleted, unknown
649 added = [] 696 c, a, d, u, mf1 = [], [], [], [], None
650 unknown = [] 697
651 mf = {} 698 def fcmp(fn, mf):
652
653 if changeset:
654 change = self.changelog.read(changeset)
655 mf = self.manifest.read(change[0])
656 dc = dict.fromkeys(mf)
657 else:
658 changeset = self.dirstate.parents()[0]
659 change = self.changelog.read(changeset)
660 mf = self.manifest.read(change[0])
661 dc = self.dirstate.dup()
662
663 def fcmp(fn):
664 t1 = self.wfile(fn).read() 699 t1 = self.wfile(fn).read()
665 t2 = self.file(fn).revision(mf[fn]) 700 t2 = self.file(fn).revision(mf[fn])
666 return cmp(t1, t2) 701 return cmp(t1, t2)
667 702
668 for dir, subdirs, files in os.walk(path): 703 # are we comparing the working directory?
669 d = dir[len(self.root)+1:] 704 if not node1:
670 if ".hg" in subdirs: subdirs.remove(".hg") 705 l, c, a, d, u = self.dirstate.changes(files, self.ignore)
671 706
672 for f in files: 707 # are we comparing working dir against its parent?
673 fn = util.pconvert(os.path.join(d, f)) 708 if not node2:
674 try: s = os.stat(os.path.join(self.root, fn)) 709 if l:
675 except: continue 710 # do a full compare of any files that might have changed
676 if fn in dc: 711 change = self.changelog.read(self.dirstate.parents()[0])
677 c = dc[fn] 712 mf1 = self.manifest.read(change[0])
678 del dc[fn] 713 for f in lookup:
679 if not c: 714 if fcmp(f, mf):
680 if fcmp(fn): 715 c.append(f)
681 changed.append(fn) 716 return (c, a, d, u)
682 elif c[0] == 'm': 717
683 changed.append(fn) 718 # are we comparing working dir against non-tip?
684 elif c[0] == 'a': 719 # generate a pseudo-manifest for the working dir
685 added.append(fn) 720 if not node1:
686 elif c[0] == 'r': 721 if not mf1:
687 unknown.append(fn) 722 change = self.changelog.read(self.dirstate.parents()[0])
688 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: 723 mf1 = self.manifest.read(change[0])
689 changed.append(fn) 724 for f in a + c + l:
690 elif c[1] != s.st_mode or c[3] != s.st_mtime: 725 mf1[f] = ""
691 if fcmp(fn): 726 for f in d:
692 changed.append(fn) 727 if f in mf1: del mf1[f]
693 else: 728 else:
694 if self.ignore(fn): continue 729 change = self.changelog.read(node1)
695 unknown.append(fn) 730 mf1 = self.manifest.read(change[0])
696 731
697 deleted = dc.keys()
698 deleted.sort()
699
700 return (changed, added, deleted, unknown)
701
702 def diffrevs(self, node1, node2):
703 changed, added = [], []
704
705 change = self.changelog.read(node1)
706 mf1 = self.manifest.read(change[0])
707 change = self.changelog.read(node2) 732 change = self.changelog.read(node2)
708 mf2 = self.manifest.read(change[0]) 733 mf2 = self.manifest.read(change[0])
709 734
710 for fn in mf2: 735 for fn in mf2:
711 if mf1.has_key(fn): 736 if mf1.has_key(fn):
712 if mf1[fn] != mf2[fn]: 737 if mf1[fn] != mf2[fn]:
713 changed.append(fn) 738 if mf1[fn] != "" or fcmp(fn, mf2):
739 c.append(fn)
714 del mf1[fn] 740 del mf1[fn]
715 else: 741 else:
716 added.append(fn) 742 a.append(fn)
717 743
718 deleted = mf1.keys() 744 d = mf1.keys()
719 deleted.sort() 745 d.sort()
720 746
721 return (changed, added, deleted) 747 return (c, a, d, u)
722 748
723 def add(self, list): 749 def add(self, list):
724 for f in list: 750 for f in list:
725 p = self.wjoin(f) 751 p = self.wjoin(f)
726 if not os.path.isfile(p): 752 if not os.path.isfile(p):
1042 m2 = self.manifest.read(m2n) 1068 m2 = self.manifest.read(m2n)
1043 mf2 = self.manifest.readflags(m2n) 1069 mf2 = self.manifest.readflags(m2n)
1044 ma = self.manifest.read(man) 1070 ma = self.manifest.read(man)
1045 mfa = self.manifest.readflags(man) 1071 mfa = self.manifest.readflags(man)
1046 1072
1047 (c, a, d, u) = self.diffdir(self.root) 1073 (c, a, d, u) = self.changes(None, None)
1048 1074
1049 # is this a jump, or a merge? i.e. is there a linear path 1075 # is this a jump, or a merge? i.e. is there a linear path
1050 # from p1 to p2? 1076 # from p1 to p2?
1051 linear_path = (pa == p1 or pa == p2) 1077 linear_path = (pa == p1 or pa == p2)
1052 1078