--- a/TODO Fri Jul 01 07:40:32 2005 +0100
+++ b/TODO Thu Jun 30 23:28:16 2005 -0800
@@ -20,7 +20,6 @@
- make showing removed files (in history etc.) faster.
Commands:
-- hg diff Makefile should avoid walking the tree
- hg add <directory> should work
- hg status <filename>: file rev, changeset rev, changed, added,
deleted, sha-1
@@ -43,4 +42,3 @@
- some web servers think hgweb.cgi.[di] is a CGI script with old-http://
(use quoting (see foo.d in Core) or document server configurations?)
- link children in hgweb
-- search field searching in descriptions, file names, what else?
--- a/mercurial/commands.py Fri Jul 01 07:40:32 2005 +0100
+++ b/mercurial/commands.py Thu Jun 30 23:28:16 2005 -0800
@@ -32,19 +32,24 @@
return [ util.pconvert(os.path.normpath(os.path.join(p, x))) for x in args ]
return args
-def dodiff(ui, repo, path, files = None, node1 = None, node2 = None):
+def dodiff(ui, repo, files = None, node1 = None, node2 = None):
def date(c):
return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
+ (c, a, d, u) = repo.changes(None, node1, files)
+ if files:
+ c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
+
+ if not c and not a and not d:
+ return
+
if node2:
change = repo.changelog.read(node2)
mmap2 = repo.manifest.read(change[0])
- (c, a, d) = repo.diffrevs(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)
if not node1:
node1 = repo.dirstate.parents()[0]
def read(f): return repo.wfile(f).read()
@@ -59,9 +64,6 @@
mmap = repo.manifest.read(change[0])
date1 = date(change)
- if files:
- c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
-
for f in c:
to = None
if f in mmap:
@@ -124,7 +126,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 +216,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)
@@ -413,7 +415,7 @@
else:
files = relpath(repo, [""])
- dodiff(ui, repo, os.getcwd(), files, *revs)
+ dodiff(ui, repo, files, *revs)
def export(ui, repo, changeset):
"""dump the changeset header and diffs for a revision"""
@@ -430,7 +432,7 @@
print change[4].rstrip()
print
- dodiff(ui, repo, "", None, prev, node)
+ dodiff(ui, repo, None, prev, node)
def forget(ui, repo, file, *files):
"""don't add the specified files on the next commit"""
@@ -449,7 +451,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 "")]
@@ -647,7 +649,7 @@
R = removed
? = not tracked'''
- (c, a, d, u) = repo.diffdir(os.getcwd())
+ (c, a, d, u) = repo.changes(None, None)
(c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
for f in c: print "C", f
@@ -662,7 +664,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 Fri Jul 01 07:40:32 2005 +0100
+++ b/mercurial/hg.py Thu Jun 30 23:28:16 2005 -0800
@@ -288,9 +288,57 @@
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):
+ f = os.path.join(self.root, f)
+ 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 +616,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 +692,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=None):
+ # 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 +1071,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 Fri Jul 01 07:40:32 2005 +0100
+++ b/mercurial/hgweb.py Thu Jun 30 23:28:16 2005 -0800
@@ -194,7 +194,7 @@
date1 = self.date(change1)
date2 = self.date(change2)
- c, a, d = r.diffrevs(node1, node2)
+ c, a, d, u = r.changes(node1, node2)
c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
for f in c:
@@ -286,6 +286,68 @@
manifest = hex(mf),
rev = pos, changesets = count, entries = changelist)
+ def search(self, query):
+
+ def changelist():
+ cl = self.repo.changelog
+ count = 0
+ qw = query.lower().split()
+
+ def revgen():
+ for i in range(cl.count() - 1, 0, -100):
+ l = []
+ for j in range(max(0, i - 100), i):
+ n = cl.node(j)
+ changes = cl.read(n)
+ l.insert(0, (n, j, changes))
+ for e in l:
+ yield e
+
+ for n, i, changes in revgen():
+ miss = 0
+ for q in qw:
+ if not (q in changes[1].lower() or
+ q in changes[4].lower() or
+ q in " ".join(changes[3][:20]).lower()):
+ miss = 1
+ break
+ if miss: continue
+
+ count += 1
+ hn = hex(n)
+ p1, p2 = cl.parents(n)
+ t = float(changes[2].split(' ')[0])
+
+ yield self.t(
+ 'searchentry',
+ parity = count & 1,
+ author = changes[1],
+ parent1 = self.parent("changelogparent",
+ hex(p1), cl.rev(p1)),
+ parent2 = self.parent("changelogparent",
+ hex(p2), cl.rev(p2)),
+ p1 = hex(p1), p2 = hex(p2),
+ p1rev = cl.rev(p1), p2rev = cl.rev(p2),
+ manifest = hex(changes[0]),
+ desc = changes[4],
+ date = t,
+ files = self.listfilediffs(changes[3], n),
+ rev = i,
+ node = hn)
+
+ if count >= self.maxchanges: break
+
+ cl = self.repo.changelog
+ mf = cl.read(cl.tip())[0]
+
+ yield self.t('search',
+ header = self.header(),
+ footer = self.footer(),
+ query = query,
+ repo = self.reponame,
+ manifest = hex(mf),
+ entries = changelist)
+
def changeset(self, nodeid):
n = bin(nodeid)
cl = self.repo.changelog
@@ -586,13 +648,16 @@
self.t = templater(m, self.filters)
if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
- hi = self.repo.changelog.count() - 1
+ c = self.repo.changelog.count() - 1
+ hi = c
if args.has_key('rev'):
hi = args['rev'][0]
try:
hi = self.repo.changelog.rev(self.repo.lookup(hi))
- except KeyError: pass
-
+ except KeyError:
+ write(self.search(hi))
+ return
+
write(self.changelog(hi))
elif args['cmd'][0] == 'changeset':
--- a/templates/map Fri Jul 01 07:40:32 2005 +0100
+++ b/templates/map Thu Jun 30 23:28:16 2005 -0800
@@ -1,11 +1,13 @@
header = header.tmpl
footer = footer.tmpl
+search = search.tmpl
changelog = changelog.tmpl
naventry = "<a href="?cmd=changelog;rev=#rev#">#label#</a> "
filedifflink = "<a href="?cmd=filediff;node=#node#;file=#file#">#file#</a> "
filenodelink = "<a href="?cmd=file;filenode=#filenode#;file=#file#">#file#</a> "
fileellipses = "..."
changelogentry = changelogentry.tmpl
+searchentry = searchentry.tmpl
changeset = changeset.tmpl
manifest = manifest.tmpl
manifestdirentry = "<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt> <td><a href="?cmd=manifest;manifest=#manifest#;path=#path#">#basename#/</a>"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/search.tmpl Thu Jun 30 23:28:16 2005 -0800
@@ -0,0 +1,28 @@
+#header#
+<title>#repo|escape#: searching for #query|escape#</title>
+</head>
+<body>
+
+<div class="buttons">
+<a href="?cmd=changelog;rev=#rev#">changelog</a>
+<a href="?cmd=tags">tags</a>
+<a href="?cmd=manifest;manifest=#manifest#;path=/">manifest</a>
+</div>
+
+<h2>searching for #query|escape#</h2>
+
+<form>
+search:
+<input type="hidden" name="cmd" value="changelog">
+<input name="rev" type="text" width="30" value="#query|escape#">
+</form>
+
+#entries#
+
+<form>
+search:
+<input type="hidden" name="cmd" value="changelog">
+<input name="rev" type="text" width="30">
+</form>
+
+#footer#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/searchentry.tmpl Thu Jun 30 23:28:16 2005 -0800
@@ -0,0 +1,22 @@
+<div class="parity#parity#">
+<table width="100%" cellpadding="0" cellspacing="0">
+<tr>
+ <td align="right" width="15%"><b>#date|age# ago: </b></td>
+ <td><b>#desc|firstline|escape#</b></td></tr>
+<tr>
+ <td align="right">changeset #rev#: </td>
+ <td><a href="?cmd=changeset;node=#node#">#node|short#</a></td></tr>
+#parent1#
+#parent2#
+<tr>
+ <td align="right">author: </td>
+ <td>#author|obfuscate#</td></tr>
+<tr>
+ <td align="right">date: </td>
+ <td>#date|date#</td></tr>
+<tr>
+ <td align="right" valign="top"><a href="?cmd=manifest;manifest=#manifest#;path=/">files</a>: </td>
+ <td>#files#</td></tr>
+</table>
+</div>
+
--- a/tests/test-bad-pull.out Fri Jul 01 07:40:32 2005 +0100
+++ b/tests/test-bad-pull.out Thu Jun 30 23:28:16 2005 -0800
@@ -9,8 +9,8 @@
+ ls copy
ls: copy: No such file or directory
+ cat
++ python dumb.py
+ sleep 2
-+ python dumb.py
+ hg clone http://localhost:20059/foo copy2
requesting all changes
adding changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diffdir Thu Jun 30 23:28:16 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 23:28:16 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