# HG changeset patch # User mpm@selenic.com # Date 1120193641 28800 # Node ID c15b4bc0a11cd0d89a35f96b75cff47cc67f17d5 # Parent fba26990604a48dcea3506399409145aed3b7dc8 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----- diff -r fba26990604a -r c15b4bc0a11c mercurial/commands.py --- a/mercurial/commands.py Thu Jun 30 10:07:50 2005 -0800 +++ b/mercurial/commands.py Thu Jun 30 20:54:01 2005 -0800 @@ -39,12 +39,12 @@ if node2: change = repo.changelog.read(node2) mmap2 = repo.manifest.read(change[0]) - (c, a, d) = repo.diffrevs(node1, node2) + (c, a, d, u) = repo.changes(node1, node2) def read(f): return repo.file(f).read(mmap2[f]) date2 = date(change) else: date2 = time.asctime() - (c, a, d, u) = repo.diffdir(path, node1) + (c, a, d, u) = repo.changes(None, node1, path) if not node1: node1 = repo.dirstate.parents()[0] def read(f): return repo.wfile(f).read() @@ -124,7 +124,7 @@ ui.status("date: %s\n" % time.asctime( time.localtime(float(changes[2].split(' ')[0])))) if ui.debugflag: - files = repo.diffrevs(changelog.parents(changenode)[0], changenode) + files = repo.changes(changelog.parents(changenode)[0], changenode) for key, value in zip(["files:", "files+:", "files-:"], files): if value: ui.note("%-12s %s\n" % (key, " ".join(value))) @@ -214,7 +214,7 @@ elif s not in 'nmai' and isfile: u.append(f) else: - (c, a, d, u) = repo.diffdir(repo.root) + (c, a, d, u) = repo.changes(None, None) repo.add(u) repo.remove(d) @@ -447,7 +447,7 @@ return hexfunc = ui.verbose and hg.hex or hg.short - (c, a, d, u) = repo.diffdir(repo.root) + (c, a, d, u) = repo.changes(None, None) output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]), (c or a or d) and "+" or "")] @@ -645,7 +645,7 @@ R = removed ? = not tracked''' - (c, a, d, u) = repo.diffdir(os.getcwd()) + (c, a, d, u) = repo.changes(None, None, os.getcwd()) (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u)) for f in c: print "C", f @@ -660,7 +660,7 @@ ui.warn("abort: 'tip' is a reserved name!\n") return -1 - (c, a, d, u) = repo.diffdir(repo.root) + (c, a, d, u) = repo.changes(None, None) for x in (c, a, d, u): if ".hgtags" in x: ui.warn("abort: working copy of .hgtags is changed!\n") diff -r fba26990604a -r c15b4bc0a11c mercurial/hg.py --- a/mercurial/hg.py Thu Jun 30 10:07:50 2005 -0800 +++ b/mercurial/hg.py Thu Jun 30 20:54:01 2005 -0800 @@ -288,9 +288,56 @@ st.write(e + f) self.dirty = 0 - def dup(self): + def changes(self, files, ignore): self.read() - return self.map.copy() + dc = self.map.copy() + lookup, changed, added, unknown = [], [], [], [] + + # compare all files by default + if not files: files = [self.root] + + def uniq(g): + seen = {} + for f in g: + if f not in seen: + seen[f] = 1 + yield f + + # recursive generator of all files listed + def walk(files): + for f in uniq(files): + if os.path.isdir(f): + for dir, subdirs, fl in os.walk(f): + d = dir[len(self.root) + 1:] + if ".hg" in subdirs: subdirs.remove(".hg") + for fn in fl: + fn = util.pconvert(os.path.join(d, fn)) + yield fn + else: + yield f[len(self.root) + 1:] + + for fn in uniq(walk(files)): + try: s = os.stat(os.path.join(self.root, fn)) + except: continue + + if fn in dc: + c = dc[fn] + del dc[fn] + + if c[0] == 'm': + changed.append(fn) + elif c[0] == 'a': + added.append(fn) + elif c[0] == 'r': + unknown.append(fn) + elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: + changed.append(fn) + elif c[1] != s.st_mode or c[3] != s.st_mtime: + lookup.append(fn) + else: + if not ignore(fn): unknown.append(fn) + + return (lookup, changed, added, dc.keys(), unknown) # used to avoid circular references so destructors work def opener(base): @@ -568,7 +615,7 @@ else: self.ui.warn("%s not tracked!\n" % f) else: - (c, a, d, u) = self.diffdir(self.root) + (c, a, d, u) = self.changes(None, None) commit = c + a remove = d @@ -644,81 +691,60 @@ self.dirstate.update(new, "n") self.dirstate.forget(remove) - def diffdir(self, path, changeset = None): - changed = [] - added = [] - unknown = [] - mf = {} + def changes(self, node1, node2, *files): + # changed, added, deleted, unknown + c, a, d, u, mf1 = [], [], [], [], None - if changeset: - change = self.changelog.read(changeset) - mf = self.manifest.read(change[0]) - dc = dict.fromkeys(mf) - else: - changeset = self.dirstate.parents()[0] - change = self.changelog.read(changeset) - mf = self.manifest.read(change[0]) - dc = self.dirstate.dup() - - def fcmp(fn): + def fcmp(fn, mf): t1 = self.wfile(fn).read() t2 = self.file(fn).revision(mf[fn]) return cmp(t1, t2) - for dir, subdirs, files in os.walk(path): - d = dir[len(self.root)+1:] - if ".hg" in subdirs: subdirs.remove(".hg") + # are we comparing the working directory? + if not node1: + l, c, a, d, u = self.dirstate.changes(files, self.ignore) + + # are we comparing working dir against its parent? + if not node2: + if l: + # do a full compare of any files that might have changed + change = self.changelog.read(self.dirstate.parents()[0]) + mf1 = self.manifest.read(change[0]) + for f in lookup: + if fcmp(f, mf): + c.append(f) + return (c, a, d, u) - for f in files: - fn = util.pconvert(os.path.join(d, f)) - try: s = os.stat(os.path.join(self.root, fn)) - except: continue - if fn in dc: - c = dc[fn] - del dc[fn] - if not c: - if fcmp(fn): - changed.append(fn) - elif c[0] == 'm': - changed.append(fn) - elif c[0] == 'a': - added.append(fn) - elif c[0] == 'r': - unknown.append(fn) - elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: - changed.append(fn) - elif c[1] != s.st_mode or c[3] != s.st_mtime: - if fcmp(fn): - changed.append(fn) - else: - if self.ignore(fn): continue - unknown.append(fn) + # are we comparing working dir against non-tip? + # generate a pseudo-manifest for the working dir + if not node1: + if not mf1: + change = self.changelog.read(self.dirstate.parents()[0]) + mf1 = self.manifest.read(change[0]) + for f in a + c + l: + mf1[f] = "" + for f in d: + if f in mf1: del mf1[f] + else: + change = self.changelog.read(node1) + mf1 = self.manifest.read(change[0]) - deleted = dc.keys() - deleted.sort() - - return (changed, added, deleted, unknown) - - def diffrevs(self, node1, node2): - changed, added = [], [] - - change = self.changelog.read(node1) - mf1 = self.manifest.read(change[0]) change = self.changelog.read(node2) mf2 = self.manifest.read(change[0]) for fn in mf2: if mf1.has_key(fn): if mf1[fn] != mf2[fn]: - changed.append(fn) + if mf1[fn] != "" or fcmp(fn, mf2): + c.append(fn) del mf1[fn] else: - added.append(fn) + a.append(fn) - deleted = mf1.keys() - deleted.sort() + d = mf1.keys() + d.sort() - return (changed, added, deleted) + return (c, a, d, u) def add(self, list): for f in list: @@ -1044,7 +1070,7 @@ ma = self.manifest.read(man) mfa = self.manifest.readflags(man) - (c, a, d, u) = self.diffdir(self.root) + (c, a, d, u) = self.changes(None, None) # is this a jump, or a merge? i.e. is there a linear path # from p1 to p2? diff -r fba26990604a -r c15b4bc0a11c mercurial/hgweb.py --- a/mercurial/hgweb.py Thu Jun 30 10:07:50 2005 -0800 +++ b/mercurial/hgweb.py Thu Jun 30 20:54:01 2005 -0800 @@ -194,7 +194,7 @@ date1 = self.date(change1) date2 = self.date(change2) - c, a, d = r.diffrevs(node1, node2) + c, a, d = r.changes(node1, node2) c, a, d = map(lambda x: filterfiles(x, files), (c, a, d)) for f in c: diff -r fba26990604a -r c15b4bc0a11c tests/test-bad-pull.out --- a/tests/test-bad-pull.out Thu Jun 30 10:07:50 2005 -0800 +++ b/tests/test-bad-pull.out Thu Jun 30 20:54:01 2005 -0800 @@ -7,6 +7,7 @@ + echo 255 255 + ls copy +ls: copy: No such file or directory + cat + python dumb.py + hg clone http://localhost:20059/foo copy2 diff -r fba26990604a -r c15b4bc0a11c tests/test-diffdir --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-diffdir Thu Jun 30 20:54:01 2005 -0800 @@ -0,0 +1,12 @@ +#!/bin/sh + +hg init +touch a +hg add a +hg ci -t "a" -u test -d "0 0" + +echo 123 > b +hg add b +hg diff | sed "s/\(\(---\|+++\).*\)\t.*/\1/" + +hg diff -r tip | sed "s/\(\(---\|+++\).*\)\t.*/\1/" diff -r fba26990604a -r c15b4bc0a11c tests/test-diffdir.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-diffdir.out Thu Jun 30 20:54:01 2005 -0800 @@ -0,0 +1,10 @@ +diff -r 3903775176ed b +--- /dev/null ++++ b/b +@@ -0,0 +1,1 @@ ++123 +diff -r 3903775176ed b +--- /dev/null ++++ b/b +@@ -0,0 +1,1 @@ ++123