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-----
--- 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")
--- 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?
--- 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:
--- 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
--- /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/"
--- /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