--- a/contrib/churn.py Fri Jan 25 16:04:32 2008 -0800
+++ b/contrib/churn.py Fri Jan 25 16:04:46 2008 -0800
@@ -114,11 +114,11 @@
who, lines = __gather(ui, repo, node1, node2)
# remap the owner if possible
- if amap.has_key(who):
+ if who in amap:
ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
who = amap[who]
- if not stats.has_key(who):
+ if not who in stats:
stats[who] = 0
stats[who] += lines
--- a/contrib/zsh_completion Fri Jan 25 16:04:32 2008 -0800
+++ b/contrib/zsh_completion Fri Jan 25 16:04:46 2008 -0800
@@ -13,6 +13,9 @@
# option) any later version.
#
+emulate -LR zsh
+setopt extendedglob
+
local curcontext="$curcontext" state line
typeset -A _hg_cmd_globals
@@ -153,9 +156,9 @@
typeset -a tags
local tag rev
- _hg_cmd tags 2> /dev/null | while read tag rev
+ _hg_cmd tags 2> /dev/null | while read tag
do
- tags+=($tag)
+ tags+=(${tag/ # [0-9]#:*})
done
(( $#tags )) && _describe -t tags 'tags' tags
}
@@ -674,13 +677,13 @@
# MQ
_hg_qseries() {
typeset -a patches
- patches=($(_hg_cmd qseries 2>/dev/null))
+ patches=(${(f)"$(_hg_cmd qseries 2>/dev/null)"})
(( $#patches )) && _describe -t hg-patches 'patches' patches
}
_hg_qapplied() {
typeset -a patches
- patches=($(_hg_cmd qapplied 2>/dev/null))
+ patches=(${(f)"$(_hg_cmd qapplied 2>/dev/null)"})
if (( $#patches ))
then
patches+=(qbase qtip)
@@ -690,7 +693,7 @@
_hg_qunapplied() {
typeset -a patches
- patches=($(_hg_cmd qunapplied 2>/dev/null))
+ patches=(${(f)"$(_hg_cmd qunapplied 2>/dev/null)"})
(( $#patches )) && _describe -t hg-unapplied-patches 'unapplied patches' patches
}
@@ -730,6 +733,12 @@
'*:unapplied patch:_hg_qunapplied'
}
+_hg_cmd_qgoto() {
+ _arguments -s -w : $_hg_global_opts \
+ '(--force -f)'{-f,--force}'[overwrite any local changes]' \
+ ':patch:_hg_qseries'
+}
+
_hg_cmd_qguard() {
_arguments -s -w : $_hg_global_opts \
'(--list -l)'{-l,--list}'[list all patches and guards]' \
--- a/hgext/convert/cvs.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/convert/cvs.py Fri Jan 25 16:04:46 2008 -0800
@@ -53,11 +53,13 @@
os.chdir(self.path)
id = None
state = 0
+ filerevids = {}
for l in util.popen(cmd):
if state == 0: # header
if l.startswith("PatchSet"):
id = l[9:-2]
if maxrev and int(id) > maxrev:
+ # ignore everything
state = 3
elif l.startswith("Date"):
date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
@@ -68,7 +70,8 @@
self.lastbranch[branch] = id
elif l.startswith("Ancestor branch"):
ancestor = l[17:-1]
- self.parent[id] = self.lastbranch[ancestor]
+ # figure out the parent later
+ self.parent[id] = None
elif l.startswith("Author"):
author = self.recode(l[8:-1])
elif l.startswith("Tag:") or l.startswith("Tags:"):
@@ -77,23 +80,36 @@
if (len(t) > 1) or (t[0] and (t[0] != "(none)")):
self.tags.update(dict.fromkeys(t, id))
elif l.startswith("Log:"):
+ # switch to gathering log
state = 1
log = ""
elif state == 1: # log
if l == "Members: \n":
+ # switch to gathering members
files = {}
+ oldrevs = []
log = self.recode(log[:-1])
state = 2
else:
+ # gather log
log += l
- elif state == 2:
- if l == "\n": #
+ elif state == 2: # members
+ if l == "\n": # start of next entry
state = 0
p = [self.parent[id]]
if id == "1":
p = []
if branch == "HEAD":
branch = ""
+ if branch and p[0] == None:
+ latest = None
+ # the last changeset that contains a base
+ # file is our parent
+ for r in oldrevs:
+ latest = max(filerevids[r], latest)
+ p = [latest]
+
+ # add current commit to set
c = commit(author=author, date=date, parents=p,
desc=log, branch=branch)
self.changeset[id] = c
@@ -102,9 +118,14 @@
colon = l.rfind(':')
file = l[1:colon]
rev = l[colon+1:-2]
- rev = rev.split("->")[1]
+ oldrev, rev = rev.split("->")
files[file] = rev
+
+ # save some information for identifying branch points
+ oldrevs.append("%s:%s" % (oldrev, file))
+ filerevids["%s:%s" % (rev, file)] = id
elif state == 3:
+ # swallow all input
continue
self.heads = self.lastbranch.values()
--- a/hgext/convert/hg.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/convert/hg.py Fri Jan 25 16:04:46 2008 -0800
@@ -30,6 +30,8 @@
if os.path.isdir(path) and len(os.listdir(path)) > 0:
try:
self.repo = hg.repository(self.ui, path)
+ if not self.repo.local():
+ raise NoRepo(_('%s is not a local Mercurial repo') % path)
except hg.RepoError, err:
ui.print_exc()
raise NoRepo(err.args[0])
@@ -37,6 +39,8 @@
try:
ui.status(_('initializing destination %s repository\n') % path)
self.repo = hg.repository(self.ui, path, create=True)
+ if not self.repo.local():
+ raise NoRepo(_('%s is not a local Mercurial repo') % path)
self.created.append(path)
except hg.RepoError, err:
ui.print_exc()
--- a/hgext/convert/subversion.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/convert/subversion.py Fri Jan 25 16:04:46 2008 -0800
@@ -89,6 +89,9 @@
receiver)
except SubversionException, (inst, num):
pickle.dump(num, fp, protocol)
+ except IOError:
+ # Caller may interrupt the iteration
+ pickle.dump(None, fp, protocol)
else:
pickle.dump(None, fp, protocol)
fp.close()
@@ -102,7 +105,53 @@
args = decodeargs(sys.stdin.read())
get_log_child(sys.stdout, *args)
+class logstream:
+ """Interruptible revision log iterator."""
+ def __init__(self, stdout):
+ self._stdout = stdout
+
+ def __iter__(self):
+ while True:
+ entry = pickle.load(self._stdout)
+ try:
+ orig_paths, revnum, author, date, message = entry
+ except:
+ if entry is None:
+ break
+ raise SubversionException("child raised exception", entry)
+ yield entry
+
+ def close(self):
+ if self._stdout:
+ self._stdout.close()
+ self._stdout = None
+
+def get_log(url, paths, start, end, limit=0, discover_changed_paths=True,
+ strict_node_history=False):
+ args = [url, paths, start, end, limit, discover_changed_paths,
+ strict_node_history]
+ arg = encodeargs(args)
+ hgexe = util.hgexecutable()
+ cmd = '%s debugsvnlog' % util.shellquote(hgexe)
+ stdin, stdout = os.popen2(cmd, 'b')
+ stdin.write(arg)
+ stdin.close()
+ return logstream(stdout)
+
# SVN conversion code stolen from bzr-svn and tailor
+#
+# Subversion looks like a versioned filesystem, branches structures
+# are defined by conventions and not enforced by the tool. First,
+# we define the potential branches (modules) as "trunk" and "branches"
+# children directories. Revisions are then identified by their
+# module and revision number (and a repository identifier).
+#
+# The revision graph is really a tree (or a forest). By default, a
+# revision parent is the previous revision in the same module. If the
+# module directory is copied/moved from another module then the
+# revision is the module root and its parent the source revision in
+# the parent module. A revision has at most one parent.
+#
class svn_source(converter_source):
def __init__(self, ui, url, rev=None):
super(svn_source, self).__init__(ui, url, rev=rev)
@@ -133,7 +182,6 @@
self.ctx = self.transport.client
self.base = svn.ra.get_repos_root(self.ra)
self.module = self.url[len(self.base):]
- self.modulemap = {} # revision, module
self.commits = {}
self.paths = {}
self.uuid = svn.ra.get_uuid(self.ra).decode(self.encoding)
@@ -257,45 +305,26 @@
uuid, module, revnum = self.revsplit(rev)
self.module = module
self.reparent(module)
+ # We assume that:
+ # - requests for revisions after "stop" come from the
+ # revision graph backward traversal. Cache all of them
+ # down to stop, they will be used eventually.
+ # - requests for revisions before "stop" come to get
+ # isolated branches parents. Just fetch what is needed.
stop = self.lastrevs.get(module, 0)
- self._fetch_revisions(from_revnum=revnum, to_revnum=stop)
+ if revnum < stop:
+ stop = revnum + 1
+ self._fetch_revisions(revnum, stop)
commit = self.commits[rev]
# caller caches the result, so free it here to release memory
del self.commits[rev]
return commit
- def get_log(self, paths, start, end, limit=0, discover_changed_paths=True,
- strict_node_history=False):
-
- def parent(fp):
- while True:
- entry = pickle.load(fp)
- try:
- orig_paths, revnum, author, date, message = entry
- except:
- if entry is None:
- break
- raise SubversionException("child raised exception", entry)
- yield entry
-
- args = [self.url, paths, start, end, limit, discover_changed_paths,
- strict_node_history]
- arg = encodeargs(args)
- hgexe = util.hgexecutable()
- cmd = '%s debugsvnlog' % util.shellquote(hgexe)
- stdin, stdout = os.popen2(cmd, 'b')
-
- stdin.write(arg)
- stdin.close()
-
- for p in parent(stdout):
- yield p
-
def gettags(self):
tags = {}
start = self.revnum(self.head)
try:
- for entry in self.get_log([self.tags], 0, start):
+ for entry in get_log(self.url, [self.tags], 0, start):
orig_paths, revnum, author, date, message = entry
for path in orig_paths:
if not path.startswith(self.tags+'/'):
@@ -400,13 +429,11 @@
entries = []
copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
copies = {}
- revnum = self.revnum(rev)
- if revnum in self.modulemap:
- new_module = self.modulemap[revnum]
- if new_module != self.module:
- self.module = new_module
- self.reparent(self.module)
+ new_module, revnum = self.revsplit(rev)[1:]
+ if new_module != self.module:
+ self.module = new_module
+ self.reparent(self.module)
for path, ent in paths:
entrypath = get_entry_from_path(path, module=self.module)
@@ -432,12 +459,9 @@
# if a branch is created but entries are removed in the same
# changeset, get the right fromrev
- if parents:
- uuid, old_module, fromrev = self.revsplit(parents[0])
- else:
- fromrev = revnum - 1
- # might always need to be revnum - 1 in these 3 lines?
- old_module = self.modulemap.get(fromrev, self.module)
+ # parents cannot be empty here, you cannot remove things from
+ # a root revision.
+ uuid, old_module, fromrev = self.revsplit(parents[0])
basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
@@ -466,7 +490,12 @@
fromrev = froment.copyfrom_rev
self.ui.debug("Info: %s %s %s %s\n" % (frompath, froment, ent, entrypath))
- fromkind = svn.ra.check_path(self.ra, entrypath, fromrev)
+ # We can avoid the reparent calls if the module has not changed
+ # but it probably does not worth the pain.
+ self.reparent('')
+ fromkind = svn.ra.check_path(self.ra, entrypath.strip('/'), fromrev)
+ self.reparent(self.module)
+
if fromkind == svn.core.svn_node_file: # a deleted file
entries.append(self.recode(entry))
elif fromkind == svn.core.svn_node_dir:
@@ -508,6 +537,9 @@
# If the directory just had a prop change,
# then we shouldn't need to look for its children.
+ if ent.action == 'M':
+ continue
+
# Also this could create duplicate entries. Not sure
# whether this will matter. Maybe should make entries a set.
# print "Changed directory", revnum, path, ent.action, ent.copyfrom_path, ent.copyfrom_rev
@@ -566,25 +598,25 @@
copies[self.recode(copyto_entry)] = self.recode(entry)
# copy from quux splort/quuxfile
- return (entries, copies)
+ return (util.unique(entries), copies)
- def _fetch_revisions(self, from_revnum = 0, to_revnum = 347):
+ def _fetch_revisions(self, from_revnum, to_revnum):
+ if from_revnum < to_revnum:
+ from_revnum, to_revnum = to_revnum, from_revnum
+
self.child_cset = None
def parselogentry(orig_paths, revnum, author, date, message):
+ """Return the parsed commit object or None, and True if
+ the revision is a branch root.
+ """
self.ui.debug("parsing revision %d (%d changes)\n" %
(revnum, len(orig_paths)))
- if revnum in self.modulemap:
- new_module = self.modulemap[revnum]
- if new_module != self.module:
- self.module = new_module
- self.reparent(self.module)
-
rev = self.revid(revnum)
# branch log might return entries for a parent we already have
- if (rev in self.commits or
- (revnum < self.lastrevs.get(self.module, 0))):
- return
+
+ if (rev in self.commits or revnum < to_revnum):
+ return None, False
parents = []
# check whether this revision is the start of a branch
@@ -593,15 +625,12 @@
if ent.copyfrom_path:
# ent.copyfrom_rev may not be the actual last revision
prev = self.latest(ent.copyfrom_path, ent.copyfrom_rev)
- self.modulemap[prev] = ent.copyfrom_path
parents = [self.revid(prev, ent.copyfrom_path)]
self.ui.note('found parent of branch %s at %d: %s\n' % \
(self.module, prev, ent.copyfrom_path))
else:
self.ui.debug("No copyfrom path, don't know what to do.\n")
- self.modulemap[revnum] = self.module # track backwards in time
-
orig_paths = orig_paths.items()
orig_paths.sort()
paths = []
@@ -612,14 +641,12 @@
continue
paths.append((path, ent))
- self.paths[rev] = (paths, parents)
-
# Example SVN datetime. Includes microseconds.
# ISO-8601 conformant
# '2007-01-04T17:35:00.902377Z'
date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
- log = message and self.recode(message)
+ log = message and self.recode(message) or ''
author = author and self.recode(author) or ''
try:
branch = self.module.split("/")[-1]
@@ -636,23 +663,50 @@
rev=rev.encode('utf-8'))
self.commits[rev] = cset
+ # The parents list is *shared* among self.paths and the
+ # commit object. Both will be updated below.
+ self.paths[rev] = (paths, cset.parents)
if self.child_cset and not self.child_cset.parents:
- self.child_cset.parents = [rev]
+ self.child_cset.parents[:] = [rev]
self.child_cset = cset
+ return cset, len(parents) > 0
self.ui.note('fetching revision log for "%s" from %d to %d\n' %
(self.module, from_revnum, to_revnum))
try:
- for entry in self.get_log([self.module], from_revnum, to_revnum):
- orig_paths, revnum, author, date, message = entry
- if self.is_blacklisted(revnum):
- self.ui.note('skipping blacklisted revision %d\n' % revnum)
- continue
- if orig_paths is None:
- self.ui.debug('revision %d has no entries\n' % revnum)
- continue
- parselogentry(orig_paths, revnum, author, date, message)
+ firstcset = None
+ stream = get_log(self.url, [self.module], from_revnum, to_revnum)
+ try:
+ for entry in stream:
+ paths, revnum, author, date, message = entry
+ if self.is_blacklisted(revnum):
+ self.ui.note('skipping blacklisted revision %d\n'
+ % revnum)
+ continue
+ if paths is None:
+ self.ui.debug('revision %d has no entries\n' % revnum)
+ continue
+ cset, branched = parselogentry(paths, revnum, author,
+ date, message)
+ if cset:
+ firstcset = cset
+ if branched:
+ break
+ finally:
+ stream.close()
+
+ if firstcset and not firstcset.parents:
+ # The first revision of the sequence (the last fetched one)
+ # has invalid parents if not a branch root. Find the parent
+ # revision now, if any.
+ try:
+ firstrevnum = self.revnum(firstcset.rev)
+ if firstrevnum > 1:
+ latest = self.latest(self.module, firstrevnum - 1)
+ firstcset.parents.append(self.revid(latest))
+ except util.Abort:
+ pass
except SubversionException, (inst, num):
if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
raise NoSuchRevision(branch=self,
@@ -664,9 +718,9 @@
# TODO: ra.get_file transmits the whole file instead of diffs.
mode = ''
try:
- revnum = self.revnum(rev)
- if self.module != self.modulemap[revnum]:
- self.module = self.modulemap[revnum]
+ new_module, revnum = self.revsplit(rev)[1:]
+ if self.module != new_module:
+ self.module = new_module
self.reparent(self.module)
info = svn.ra.get_file(self.ra, file, revnum, io)
if isinstance(info, list):
--- a/hgext/graphlog.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/graphlog.py Fri Jan 25 16:04:46 2008 -0800
@@ -5,11 +5,12 @@
# This software may be used and distributed according to the terms of
# the GNU General Public License, incorporated herein by reference.
+import os
import sys
from mercurial.cmdutil import revrange, show_changeset
from mercurial.i18n import _
from mercurial.node import nullid, nullrev
-from mercurial.util import Abort
+from mercurial.util import Abort, canonpath
def revision_grapher(repo, start_rev, stop_rev):
"""incremental revision grapher
@@ -63,6 +64,62 @@
revs = next_revs
curr_rev -= 1
+def filelog_grapher(repo, path, start_rev, stop_rev):
+ """incremental file log grapher
+
+ This generator function walks through the revision history of a
+ single file from revision start_rev to revision stop_rev (which must
+ be less than or equal to start_rev) and for each revision emits
+ tuples with the following elements:
+
+ - Current revision.
+ - Current node.
+ - Column of the current node in the set of ongoing edges.
+ - Edges; a list of (col, next_col) indicating the edges between
+ the current node and its parents.
+ - Number of columns (ongoing edges) in the current revision.
+ - The difference between the number of columns (ongoing edges)
+ in the next revision and the number of columns (ongoing edges)
+ in the current revision. That is: -1 means one column removed;
+ 0 means no columns added or removed; 1 means one column added.
+ """
+
+ assert start_rev >= stop_rev
+ curr_rev = start_rev
+ revs = []
+ filerev = repo.file(path).count() - 1
+ while filerev >= 0:
+ fctx = repo.filectx(path, fileid=filerev)
+
+ # Compute revs and next_revs.
+ if filerev not in revs:
+ revs.append(filerev)
+ rev_index = revs.index(filerev)
+ next_revs = revs[:]
+
+ # Add parents to next_revs.
+ parents = [f.filerev() for f in fctx.parents()]
+ parents_to_add = []
+ for parent in parents:
+ if parent not in next_revs:
+ parents_to_add.append(parent)
+ parents_to_add.sort()
+ next_revs[rev_index:rev_index + 1] = parents_to_add
+
+ edges = []
+ for parent in parents:
+ edges.append((rev_index, next_revs.index(parent)))
+
+ changerev = fctx.linkrev()
+ if changerev <= start_rev:
+ node = repo.changelog.node(changerev)
+ n_columns_diff = len(next_revs) - len(revs)
+ yield (changerev, node, rev_index, edges, len(revs), n_columns_diff)
+ if changerev <= stop_rev:
+ break
+ revs = next_revs
+ filerev -= 1
+
def get_rev_parents(repo, rev):
return [x for x in repo.changelog.parentrevs(rev) if x != nullrev]
@@ -141,7 +198,7 @@
else:
return (repo.changelog.count() - 1, 0)
-def graphlog(ui, repo, **opts):
+def graphlog(ui, repo, path=None, **opts):
"""show revision history alongside an ASCII revision graph
Print a revision history alongside a revision graph drawn with
@@ -157,7 +214,11 @@
if start_rev == nullrev:
return
cs_printer = show_changeset(ui, repo, opts)
- grapher = revision_grapher(repo, start_rev, stop_rev)
+ if path:
+ cpath = canonpath(repo.root, os.getcwd(), path)
+ grapher = filelog_grapher(repo, cpath, start_rev, stop_rev)
+ else:
+ grapher = revision_grapher(repo, start_rev, stop_rev)
repo_parents = repo.dirstate.parents()
prev_n_columns_diff = 0
prev_node_index = 0
@@ -261,5 +322,5 @@
('r', 'rev', [], _('show the specified revision or range')),
('', 'style', '', _('display using template map file')),
('', 'template', '', _('display with template'))],
- _('hg glog [OPTION]...')),
+ _('hg glog [OPTION]... [FILE]')),
}
--- a/hgext/hgk.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/hgk.py Fri Jan 25 16:04:46 2008 -0800
@@ -45,7 +45,7 @@
# Revisions context menu will now display additional entries to fire
# vdiff on hovered and selected revisions.
-import sys, os
+import os
from mercurial import hg, fancyopts, commands, ui, util, patch, revlog
def difftree(ui, repo, node1=None, node2=None, *files, **opts):
@@ -61,17 +61,14 @@
for f in modified:
# TODO get file permissions
- print ":100664 100664 %s %s M\t%s\t%s" % (hg.short(mmap[f]),
- hg.short(mmap2[f]),
- f, f)
+ ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
+ (hg.short(mmap[f]), hg.short(mmap2[f]), f, f))
for f in added:
- print ":000000 100664 %s %s N\t%s\t%s" % (empty,
- hg.short(mmap2[f]),
- f, f)
+ ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
+ (empty, hg.short(mmap2[f]), f, f))
for f in removed:
- print ":100664 000000 %s %s D\t%s\t%s" % (hg.short(mmap[f]),
- empty,
- f, f)
+ ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
+ (hg.short(mmap[f]), empty, f, f))
##
while True:
@@ -93,7 +90,7 @@
node1 = repo.changelog.parents(node1)[0]
if opts['patch']:
if opts['pretty']:
- catcommit(repo, node2, "")
+ catcommit(ui, repo, node2, "")
patch.diff(repo, node1, node2,
files=files,
opts=patch.diffopts(ui, {'git': True}))
@@ -102,14 +99,14 @@
if not opts['stdin']:
break
-def catcommit(repo, n, prefix, ctx=None):
+def catcommit(ui, repo, n, prefix, ctx=None):
nlprefix = '\n' + prefix;
if ctx is None:
ctx = repo.changectx(n)
(p1, p2) = ctx.parents()
- print "tree %s" % (hg.short(ctx.changeset()[0])) # use ctx.node() instead ??
- if p1: print "parent %s" % (hg.short(p1.node()))
- if p2: print "parent %s" % (hg.short(p2.node()))
+ ui.write("tree %s\n" % hg.short(ctx.changeset()[0])) # use ctx.node() instead ??
+ if p1: ui.write("parent %s\n" % hg.short(p1.node()))
+ if p2: ui.write("parent %s\n" % hg.short(p2.node()))
date = ctx.date()
description = ctx.description().replace("\0", "")
lines = description.splitlines()
@@ -118,24 +115,24 @@
else:
committer = ctx.user()
- print "author %s %s %s" % (ctx.user(), int(date[0]), date[1])
- print "committer %s %s %s" % (committer, int(date[0]), date[1])
- print "revision %d" % ctx.rev()
- print "branch %s" % ctx.branch()
- print ""
+ ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
+ ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
+ ui.write("revision %d\n" % ctx.rev())
+ ui.write("branch %s\n\n" % ctx.branch())
+
if prefix != "":
- print "%s%s" % (prefix, description.replace('\n', nlprefix).strip())
+ ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
else:
- print description
+ ui.write(description + "\n")
if prefix:
- sys.stdout.write('\0')
+ ui.write('\0')
def base(ui, repo, node1, node2):
"""Output common ancestor information"""
node1 = repo.lookup(node1)
node2 = repo.lookup(node2)
n = repo.changelog.ancestor(node1, node2)
- print hg.short(n)
+ ui.write(hg.short(n) + "\n")
def catfile(ui, repo, type=None, r=None, **opts):
"""cat a specific revision"""
@@ -158,10 +155,10 @@
while r:
if type != "commit":
- sys.stderr.write("aborting hg cat-file only understands commits\n")
- sys.exit(1);
+ ui.warn("aborting hg cat-file only understands commits\n")
+ return 1;
n = repo.lookup(r)
- catcommit(repo, n, prefix)
+ catcommit(ui, repo, n, prefix)
if opts['stdin']:
try:
(type, r) = raw_input().split(' ');
@@ -175,7 +172,7 @@
# telling you which commits are reachable from the supplied ones via
# a bitmask based on arg position.
# you can specify a commit to stop at by starting the sha1 with ^
-def revtree(args, repo, full="tree", maxnr=0, parents=False):
+def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
def chlogwalk():
count = repo.changelog.count()
i = count
@@ -260,24 +257,24 @@
if pp[1] != hg.nullid:
parentstr += " " + hg.short(pp[1])
if not full:
- print hg.short(n) + parentstr
+ ui.write("%s%s\n" % (hg.short(n), parentstr))
elif full == "commit":
- print hg.short(n) + parentstr
- catcommit(repo, n, ' ', ctx)
+ ui.write("%s%s\n" % (hg.short(n), parentstr))
+ catcommit(ui, repo, n, ' ', ctx)
else:
(p1, p2) = repo.changelog.parents(n)
(h, h1, h2) = map(hg.short, (n, p1, p2))
(i1, i2) = map(repo.changelog.rev, (p1, p2))
date = ctx.date()[0]
- print "%s %s:%s" % (date, h, mask),
+ ui.write("%s %s:%s" % (date, h, mask))
mask = is_reachable(want_sha1, reachable, p1)
if i1 != hg.nullrev and mask > 0:
- print "%s:%s " % (h1, mask),
+ ui.write("%s:%s " % (h1, mask)),
mask = is_reachable(want_sha1, reachable, p2)
if i2 != hg.nullrev and mask > 0:
- print "%s:%s " % (h2, mask),
- print ""
+ ui.write("%s:%s " % (h2, mask))
+ ui.write("\n")
if maxnr and count >= maxnr:
break
count += 1
@@ -305,7 +302,7 @@
else:
full = None
copy = [x for x in revs]
- revtree(copy, repo, full, opts['max_count'], opts['parents'])
+ revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
def config(ui, repo, **opts):
"""print extension options"""
--- a/hgext/keyword.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/keyword.py Fri Jan 25 16:04:46 2008 -0800
@@ -69,12 +69,16 @@
To force expansion after enabling it, or a configuration change, run
"hg kwexpand".
+Also, when committing with the record extension or using mq's qrecord, be aware
+that keywords cannot be updated. Again, run "hg kwexpand" on the files in
+question to update keyword expansions after all changes have been checked in.
+
Expansions spanning more than one line and incremental expansions,
like CVS' $Log$, are not supported. A keyword template map
"Log = {desc}" expands to the first line of the changeset description.
'''
-from mercurial import commands, cmdutil, context, fancyopts, filelog
+from mercurial import commands, cmdutil, context, dispatch, filelog
from mercurial import patch, localrepo, revlog, templater, util
from mercurial.node import *
from mercurial.i18n import _
@@ -86,6 +90,14 @@
'''Returns hgdate in cvs-like UTC format.'''
return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
+def _kwrestrict(cmd):
+ '''Returns True if cmd should trigger restricted expansion.
+ Keywords will only expanded when writing to working dir.
+ Crucial for mq as expanded keywords should not make it into patches.'''
+ return cmd in ('diff1', 'record',
+ 'qfold', 'qimport', 'qnew', 'qpush', 'qrefresh', 'qrecord')
+
+
_kwtemplater = None
class kwtemplater(object):
@@ -103,10 +115,11 @@
'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
}
- def __init__(self, ui, repo, inc, exc):
+ def __init__(self, ui, repo, inc, exc, hgcmd):
self.ui = ui
self.repo = repo
self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
+ self.hgcmd = hgcmd
self.commitnode = None
self.path = ''
@@ -144,7 +157,7 @@
def expand(self, node, data):
'''Returns data with keywords expanded.'''
- if util.binary(data):
+ if util.binary(data) or _kwrestrict(self.hgcmd):
return data
return self.substitute(node, data, self.re_kw.sub)
@@ -230,7 +243,7 @@
mf = ctx.manifest()
if node is not None: # commit
_kwtemplater.commitnode = node
- files = [f for f in ctx.files() if mf.has_key(f)]
+ files = [f for f in ctx.files() if f in mf]
notify = ui.debug
else: # kwexpand/kwshrink
notify = ui.note
@@ -297,7 +310,7 @@
kwmaps = kwtemplater.templates
if ui.configitems('keywordmaps'):
# override maps from optional rcfile
- for k, v in kwmaps.items():
+ for k, v in kwmaps.iteritems():
ui.setconfig('keywordmaps', k, v)
elif args:
# simulate hgrc parsing
@@ -316,7 +329,7 @@
demostatus('config using %s keyword template maps' % kwstatus)
ui.write('[extensions]\n%s\n' % extension)
demoitems('keyword', ui.configitems('keyword'))
- demoitems('keywordmaps', kwmaps.items())
+ demoitems('keywordmaps', kwmaps.iteritems())
keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
repo.wopener(fn, 'w').write(keywords)
repo.add([fn])
@@ -395,22 +408,27 @@
This is done for local repos only, and only if there are
files configured at all for keyword substitution.'''
- def kwbailout():
- '''Obtains command via simplified cmdline parsing,
- returns True if keyword expansion not needed.'''
- nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy',
- 'export', 'grep', 'identify', 'incoming', 'init',
- 'log', 'outgoing', 'push', 'remove', 'rename',
- 'rollback', 'tip',
- 'convert')
- args = fancyopts.fancyopts(sys.argv[1:], commands.globalopts, {})
- if args:
- aliases, i = cmdutil.findcmd(ui, args[0], commands.table)
- return aliases[0] in nokwcommands
+ if not repo.local():
+ return
- if not repo.local() or kwbailout():
+ nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy',
+ 'export', 'grep', 'identify', 'incoming', 'init',
+ 'log', 'outgoing', 'push', 'remove', 'rename',
+ 'rollback', 'tip',
+ 'convert')
+ hgcmd, func, args, opts, cmdopts = dispatch._parse(ui, sys.argv[1:])
+ if hgcmd in nokwcommands:
return
+ if hgcmd == 'diff':
+ # only expand if comparing against working dir
+ node1, node2 = cmdutil.revpair(repo, cmdopts.get('rev'))
+ if node2 is not None:
+ return
+ # shrink if rev is not current node
+ if node1 is not None and node1 != repo.changectx().node():
+ hgcmd = 'diff1'
+
inc, exc = [], ['.hgtags']
for pat, opt in ui.configitems('keyword'):
if opt != 'ignore':
@@ -421,7 +439,7 @@
return
global _kwtemplater
- _kwtemplater = kwtemplater(ui, repo, inc, exc)
+ _kwtemplater = kwtemplater(ui, repo, inc, exc, hgcmd)
class kwrepo(repo.__class__):
def file(self, f, kwmatch=False):
@@ -431,6 +449,12 @@
return kwfilelog(self.sopener, f)
return filelog.filelog(self.sopener, f)
+ def wread(self, filename):
+ data = super(kwrepo, self).wread(filename)
+ if _kwrestrict(hgcmd) and _kwtemplater.matcher(filename):
+ return _kwtemplater.shrink(data)
+ return data
+
def commit(self, files=None, text='', user=None, date=None,
match=util.always, force=False, force_editor=False,
p1=None, p2=None, extra={}):
@@ -440,10 +464,10 @@
wlock = self.wlock()
lock = self.lock()
# store and postpone commit hooks
- commithooks = []
+ commithooks = {}
for name, cmd in ui.configitems('hooks'):
if name.split('.', 1)[0] == 'commit':
- commithooks.append((name, cmd))
+ commithooks[name] = cmd
ui.setconfig('hooks', name, None)
if commithooks:
# store parents for commit hook environment
@@ -464,7 +488,7 @@
p1=p1, p2=p2, extra=extra)
# restore commit hooks
- for name, cmd in commithooks:
+ for name, cmd in commithooks.iteritems():
ui.setconfig('hooks', name, cmd)
if node is not None:
_overwrite(ui, self, node=node)
--- a/hgext/mq.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/mq.py Fri Jan 25 16:04:46 2008 -0800
@@ -224,7 +224,7 @@
def write_list(items, path):
fp = self.opener(path, 'w')
for i in items:
- print >> fp, i
+ fp.write("%s\n" % i)
fp.close()
if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
if self.series_dirty: write_list(self.full_series, self.series_path)
@@ -1267,7 +1267,7 @@
self.ui.warn("saved queue repository parents: %s %s\n" %
(hg.short(qpp[0]), hg.short(qpp[1])))
if qupdate:
- print "queue directory updating"
+ self.ui.status(_("queue directory updating\n"))
r = self.qrepo()
if not r:
self.ui.warn("Unable to load queue repository\n")
--- a/hgext/patchbomb.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/patchbomb.py Fri Jan 25 16:04:46 2008 -0800
@@ -417,8 +417,7 @@
fp.close()
elif opts.get('mbox'):
ui.status('Writing ', m['Subject'], ' ...\n')
- fp = open(opts.get('mbox'),
- m.has_key('In-Reply-To') and 'ab+' or 'wb+')
+ fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
date = util.datestr(date=start_time,
format='%a %b %d %H:%M:%S %Y', timezone=False)
fp.write('From %s %s\n' % (sender_addr, date))
--- a/hgext/record.py Fri Jan 25 16:04:32 2008 -0800
+++ b/hgext/record.py Fri Jan 25 16:04:46 2008 -0800
@@ -364,10 +364,10 @@
dorecord(ui, repo, record_committer, *pats, **opts)
-def qrecord(ui, repo, *pats, **opts):
- '''interactively select changes for qrefresh
+def qrecord(ui, repo, patch, *pats, **opts):
+ '''interactively record a new patch
- see 'hg help record' for more information and usage
+ see 'hg help qnew' & 'hg help record' for more information and usage
'''
try:
@@ -376,8 +376,10 @@
raise util.Abort(_("'mq' extension not loaded"))
def qrecord_committer(ui, repo, pats, opts):
- mq.refresh(ui, repo, *pats, **opts)
+ mq.new(ui, repo, patch, *pats, **opts)
+ opts = opts.copy()
+ opts['force'] = True # always 'qnew -f'
dorecord(ui, repo, qrecord_committer, *pats, **opts)
@@ -513,10 +515,10 @@
"qrecord":
(qrecord,
- # add qrefresh options
- mq.cmdtable['^qrefresh'][1],
+ # add qnew options, except '--force'
+ [opt for opt in mq.cmdtable['qnew'][1] if opt[1] != 'force'],
- _('hg qrecord [OPTION]... [FILE]...')),
+ _('hg qrecord [OPTION]... PATCH [FILE]...')),
}
cmdtable.update(qcmdtable)
--- a/mercurial/byterange.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/byterange.py Fri Jan 25 16:04:46 2008 -0800
@@ -233,7 +233,7 @@
size = (lb - fb)
fo = RangeableFileObject(fo, (fb, lb))
headers = mimetools.Message(StringIO(
- 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' %
+ 'Content-Type: %s\nContent-Length: %d\nLast-Modified: %s\n' %
(mtype or 'text/plain', size, modified)))
return urllib.addinfourl(fo, headers, 'file:'+file)
--- a/mercurial/changegroup.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/changegroup.py Fri Jan 25 16:04:46 2008 -0800
@@ -80,9 +80,13 @@
# in case of sshrepo because we don't know the end of the stream
# an empty chunkiter is the end of the changegroup
+ # a changegroup has at least 2 chunkiters (changelog and manifest).
+ # after that, an empty chunkiter is the end of the changegroup
empty = False
- while not empty:
+ count = 0
+ while not empty or count <= 2:
empty = True
+ count += 1
for chunk in chunkiter(cg):
empty = False
fh.write(z.compress(chunkheader(len(chunk))))
--- a/mercurial/cmdutil.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/cmdutil.py Fri Jan 25 16:04:46 2008 -0800
@@ -50,7 +50,7 @@
"""Return (aliases, command table entry) for command string."""
choice = findpossible(ui, cmd, table)
- if choice.has_key(cmd):
+ if cmd in choice:
return choice[cmd]
if len(choice) > 1:
--- a/mercurial/commands.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/commands.py Fri Jan 25 16:04:46 2008 -0800
@@ -1528,12 +1528,13 @@
files=files)
finally:
files = patch.updatedir(ui, repo, files)
- n = repo.commit(files, message, user, date)
- if opts.get('exact'):
- if hex(n) != nodeid:
- repo.rollback()
- raise util.Abort(_('patch is damaged'
- ' or loses information'))
+ if not opts.get('no_commit'):
+ n = repo.commit(files, message, user, date)
+ if opts.get('exact'):
+ if hex(n) != nodeid:
+ repo.rollback()
+ raise util.Abort(_('patch is damaged'
+ ' or loses information'))
finally:
os.unlink(tmpname)
finally:
@@ -2896,6 +2897,7 @@
('b', 'base', '', _('base path')),
('f', 'force', None,
_('skip check for outstanding uncommitted changes')),
+ ('', 'no-commit', None, _("don't commit, just update the working directory")),
('', 'exact', None,
_('apply patch to the nodes from which it was generated')),
('', 'import-branch', None,
--- a/mercurial/demandimport.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/demandimport.py Fri Jan 25 16:04:46 2008 -0800
@@ -77,7 +77,7 @@
self._load()
setattr(self._module, attr, val)
-def _demandimport(name, globals=None, locals=None, fromlist=None):
+def _demandimport(name, globals=None, locals=None, fromlist=None, level=None):
if not locals or name in ignore or fromlist == ('*',):
# these cases we can't really delay
return _origimport(name, globals, locals, fromlist)
@@ -95,6 +95,9 @@
return locals[base]
return _demandmod(name, globals, locals)
else:
+ if level is not None:
+ # from . import b,c,d or from .a import b,c,d
+ return _origimport(name, globals, locals, fromlist, level)
# from a import b,c,d
mod = _origimport(name, globals, locals)
# recurse down the module chain
--- a/mercurial/dirstate.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/dirstate.py Fri Jan 25 16:04:46 2008 -0800
@@ -235,7 +235,7 @@
self._changepath(f, 'n', True)
s = os.lstat(self._join(f))
self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
- if self._copymap.has_key(f):
+ if f in self._copymap:
del self._copymap[f]
def normallookup(self, f):
--- a/mercurial/dispatch.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/dispatch.py Fri Jan 25 16:04:46 2008 -0800
@@ -354,12 +354,12 @@
d = lambda: func(ui, *args, **cmdoptions)
# run pre-hook, and abort if it fails
- ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
+ ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
if ret:
return ret
ret = _runcommand(ui, options, cmd, d)
# run post-hook, passing command result
- hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
+ hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
result = ret)
return ret
--- a/mercurial/fancyopts.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/fancyopts.py Fri Jan 25 16:04:46 2008 -0800
@@ -38,7 +38,6 @@
if isinstance(default, list):
state[name] = default[:]
elif callable(default):
- print "whoa", name, default
state[name] = None
else:
state[name] = default
--- a/mercurial/filelog.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/filelog.py Fri Jan 25 16:04:46 2008 -0800
@@ -58,7 +58,7 @@
if self.parents(node)[0] != nullid:
return False
m = self._readmeta(node)
- if m and m.has_key("copy"):
+ if m and "copy" in m:
return (m["copy"], bin(m["copyrev"]))
return False
--- a/mercurial/hgweb/common.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/hgweb/common.py Fri Jan 25 16:04:46 2008 -0800
@@ -36,11 +36,11 @@
return os.stat(store_path).st_mtime
def staticfile(directory, fname, req):
- """return a file inside directory with guessed content-type header
+ """return a file inside directory with guessed Content-Type header
fname always uses '/' as directory separator and isn't allowed to
contain unusual path components.
- Content-type is guessed using the mimetypes module.
+ Content-Type is guessed using the mimetypes module.
Return an empty string if fname is illegal or file not found.
"""
@@ -54,8 +54,10 @@
try:
os.stat(path)
ct = mimetypes.guess_type(path)[0] or "text/plain"
- req.header([('Content-type', ct),
- ('Content-length', str(os.path.getsize(path)))])
+ req.header([
+ ('Content-Type', ct),
+ ('Content-Length', str(os.path.getsize(path)))
+ ])
return file(path, 'rb').read()
except TypeError:
raise ErrorResponse(500, 'illegal file name')
--- a/mercurial/hgweb/hgweb_mod.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/hgweb/hgweb_mod.py Fri Jan 25 16:04:46 2008 -0800
@@ -6,7 +6,7 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os, mimetypes, re, mimetools, cStringIO
+import os, mimetypes, re
from mercurial.node import *
from mercurial import mdiff, ui, hg, util, archival, patch, hook
from mercurial import revlog, templater
@@ -153,7 +153,7 @@
req.url = req.env['SCRIPT_NAME']
if not req.url.endswith('/'):
req.url += '/'
- if req.env.has_key('REPO_NAME'):
+ if 'REPO_NAME' in req.env:
req.url += req.env['REPO_NAME'] + '/'
if req.env.get('PATH_INFO'):
@@ -206,12 +206,17 @@
method = getattr(protocol, cmd)
method(self, req)
else:
+
tmpl = self.templater(req)
if cmd == '':
req.form['cmd'] = [tmpl.cache['default']]
cmd = req.form['cmd'][0]
- method = getattr(webcommands, cmd)
- method(self, req, tmpl)
+
+ if cmd == 'file' and 'raw' in req.form.get('style', []):
+ webcommands.rawfile(self, req, tmpl)
+ else:
+ getattr(webcommands, cmd)(self, req, tmpl)
+
del tmpl
except revlog.LookupError, err:
@@ -248,17 +253,9 @@
# some functions for the templater
def header(**map):
- header_file = cStringIO.StringIO(
- ''.join(tmpl("header", encoding=self.encoding, **map)))
- msg = mimetools.Message(header_file, 0)
- req.header(msg.items())
- yield header_file.read()
-
- def rawfileheader(**map):
- req.header([('Content-type', map['mimetype']),
- ('Content-disposition', 'filename=%s' % map['file']),
- ('Content-length', str(len(map['raw'])))])
- yield ''
+ ctype = tmpl('mimetype', encoding=self.encoding)
+ req.httphdr(templater.stringify(ctype))
+ yield tmpl('header', encoding=self.encoding, **map)
def footer(**map):
yield tmpl("footer", **map)
@@ -268,7 +265,7 @@
def sessionvars(**map):
fields = []
- if req.form.has_key('style'):
+ if 'style' in req.form:
style = req.form['style'][0]
if style != self.config('web', 'style', ''):
fields.append(('style', style))
@@ -281,7 +278,7 @@
# figure out which style to use
style = self.config("web", "style", "")
- if req.form.has_key('style'):
+ if 'style' in req.form:
style = req.form['style'][0]
mapfile = style_map(self.templatepath, style)
@@ -300,7 +297,6 @@
"header": header,
"footer": footer,
"motd": motd,
- "rawfileheader": rawfileheader,
"sessionvars": sessionvars
})
return tmpl
@@ -446,13 +442,13 @@
changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
- yield tmpl(shortlog and 'shortlog' or 'changelog',
- changenav=changenav,
- node=hex(cl.tip()),
- rev=pos, changesets=count,
- entries=lambda **x: changelist(limit=0,**x),
- latestentry=lambda **x: changelist(limit=1,**x),
- archives=self.archivelist("tip"))
+ return tmpl(shortlog and 'shortlog' or 'changelog',
+ changenav=changenav,
+ node=hex(cl.tip()),
+ rev=pos, changesets=count,
+ entries=lambda **x: changelist(limit=0,**x),
+ latestentry=lambda **x: changelist(limit=1,**x),
+ archives=self.archivelist("tip"))
def search(self, tmpl, query):
@@ -505,11 +501,11 @@
cl = self.repo.changelog
parity = paritygen(self.stripecount)
- yield tmpl('search',
- query=query,
- node=hex(cl.tip()),
- entries=changelist,
- archives=self.archivelist("tip"))
+ return tmpl('search',
+ query=query,
+ node=hex(cl.tip()),
+ entries=changelist,
+ archives=self.archivelist("tip"))
def changeset(self, tmpl, ctx):
n = ctx.node()
@@ -526,20 +522,20 @@
def diff(**map):
yield self.diff(tmpl, p1, n, None)
- yield tmpl('changeset',
- diff=diff,
- rev=ctx.rev(),
- node=hex(n),
- parent=self.siblings(parents),
- child=self.siblings(ctx.children()),
- changesettag=self.showtag("changesettag",n),
- author=ctx.user(),
- desc=ctx.description(),
- date=ctx.date(),
- files=files,
- archives=self.archivelist(hex(n)),
- tags=self.nodetagsdict(n),
- branches=self.nodebranchdict(ctx))
+ return tmpl('changeset',
+ diff=diff,
+ rev=ctx.rev(),
+ node=hex(n),
+ parent=self.siblings(parents),
+ child=self.siblings(ctx.children()),
+ changesettag=self.showtag("changesettag",n),
+ author=ctx.user(),
+ desc=ctx.description(),
+ date=ctx.date(),
+ files=files,
+ archives=self.archivelist(hex(n)),
+ tags=self.nodetagsdict(n),
+ branches=self.nodebranchdict(ctx))
def filelog(self, tmpl, fctx):
f = fctx.path()
@@ -578,9 +574,9 @@
nodefunc = lambda x: fctx.filectx(fileid=x)
nav = revnavgen(pos, pagelen, count, nodefunc)
- yield tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
- entries=lambda **x: entries(limit=0, **x),
- latestentry=lambda **x: entries(limit=1, **x))
+ return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
+ entries=lambda **x: entries(limit=0, **x),
+ latestentry=lambda **x: entries(limit=1, **x))
def filerevision(self, tmpl, fctx):
f = fctx.path()
@@ -602,21 +598,21 @@
"linenumber": "% 6d" % (l + 1),
"parity": parity.next()}
- yield tmpl("filerevision",
- file=f,
- path=_up(f),
- text=lines(),
- raw=rawtext,
- mimetype=mt,
- rev=fctx.rev(),
- node=hex(fctx.node()),
- author=fctx.user(),
- date=fctx.date(),
- desc=fctx.description(),
- parent=self.siblings(fctx.parents()),
- child=self.siblings(fctx.children()),
- rename=self.renamelink(fl, n),
- permissions=fctx.manifest().flags(f))
+ return tmpl("filerevision",
+ file=f,
+ path=_up(f),
+ text=lines(),
+ raw=rawtext,
+ mimetype=mt,
+ rev=fctx.rev(),
+ node=hex(fctx.node()),
+ author=fctx.user(),
+ date=fctx.date(),
+ desc=fctx.description(),
+ parent=self.siblings(fctx.parents()),
+ child=self.siblings(fctx.children()),
+ rename=self.renamelink(fl, n),
+ permissions=fctx.manifest().flags(f))
def fileannotate(self, tmpl, fctx):
f = fctx.path()
@@ -640,19 +636,19 @@
"file": f.path(),
"line": l}
- yield tmpl("fileannotate",
- file=f,
- annotate=annotate,
- path=_up(f),
- rev=fctx.rev(),
- node=hex(fctx.node()),
- author=fctx.user(),
- date=fctx.date(),
- desc=fctx.description(),
- rename=self.renamelink(fl, n),
- parent=self.siblings(fctx.parents()),
- child=self.siblings(fctx.children()),
- permissions=fctx.manifest().flags(f))
+ return tmpl("fileannotate",
+ file=f,
+ annotate=annotate,
+ path=_up(f),
+ rev=fctx.rev(),
+ node=hex(fctx.node()),
+ author=fctx.user(),
+ date=fctx.date(),
+ desc=fctx.description(),
+ rename=self.renamelink(fl, n),
+ parent=self.siblings(fctx.parents()),
+ child=self.siblings(fctx.children()),
+ permissions=fctx.manifest().flags(f))
def manifest(self, tmpl, ctx, path):
mf = ctx.manifest()
@@ -708,17 +704,17 @@
"path": "%s%s" % (abspath, f),
"basename": f[:-1]}
- yield tmpl("manifest",
- rev=ctx.rev(),
- node=hex(node),
- path=abspath,
- up=_up(abspath),
- upparity=parity.next(),
- fentries=filelist,
- dentries=dirlist,
- archives=self.archivelist(hex(node)),
- tags=self.nodetagsdict(node),
- branches=self.nodebranchdict(ctx))
+ return tmpl("manifest",
+ rev=ctx.rev(),
+ node=hex(node),
+ path=abspath,
+ up=_up(abspath),
+ upparity=parity.next(),
+ fentries=filelist,
+ dentries=dirlist,
+ archives=self.archivelist(hex(node)),
+ tags=self.nodetagsdict(node),
+ branches=self.nodebranchdict(ctx))
def tags(self, tmpl):
i = self.repo.tagslist()
@@ -738,11 +734,11 @@
"date": self.repo.changectx(n).date(),
"node": hex(n)}
- yield tmpl("tags",
- node=hex(self.repo.changelog.tip()),
- entries=lambda **x: entries(False,0, **x),
- entriesnotip=lambda **x: entries(True,0, **x),
- latestentry=lambda **x: entries(True,1, **x))
+ return tmpl("tags",
+ node=hex(self.repo.changelog.tip()),
+ entries=lambda **x: entries(False,0, **x),
+ entriesnotip=lambda **x: entries(True,0, **x),
+ latestentry=lambda **x: entries(True,1, **x))
def summary(self, tmpl):
i = self.repo.tagslist()
@@ -807,15 +803,15 @@
start = max(0, count - self.maxchanges)
end = min(count, start + self.maxchanges)
- yield tmpl("summary",
- desc=self.config("web", "description", "unknown"),
- owner=get_contact(self.config) or "unknown",
- lastchange=cl.read(cl.tip())[2],
- tags=tagentries,
- branches=branches,
- shortlog=changelist,
- node=hex(cl.tip()),
- archives=self.archivelist("tip"))
+ return tmpl("summary",
+ desc=self.config("web", "description", "unknown"),
+ owner=get_contact(self.config) or "unknown",
+ lastchange=cl.read(cl.tip())[2],
+ tags=tagentries,
+ branches=branches,
+ shortlog=changelist,
+ node=hex(cl.tip()),
+ archives=self.archivelist("tip"))
def filediff(self, tmpl, fctx):
n = fctx.node()
@@ -826,13 +822,13 @@
def diff(**map):
yield self.diff(tmpl, p1, n, [path])
- yield tmpl("filediff",
- file=path,
- node=hex(n),
- rev=fctx.rev(),
- parent=self.siblings(parents),
- child=self.siblings(fctx.children()),
- diff=diff)
+ return tmpl("filediff",
+ file=path,
+ node=hex(n),
+ rev=fctx.rev(),
+ parent=self.siblings(parents),
+ child=self.siblings(fctx.children()),
+ diff=diff)
archive_specs = {
'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
@@ -848,13 +844,15 @@
arch_version = short(cnode)
name = "%s-%s" % (reponame, arch_version)
mimetype, artype, extension, encoding = self.archive_specs[type_]
- headers = [('Content-type', mimetype),
- ('Content-disposition', 'attachment; filename=%s%s' %
- (name, extension))]
+ headers = [
+ ('Content-Type', mimetype),
+ ('Content-Disposition', 'attachment; filename=%s%s' %
+ (name, extension))
+ ]
if encoding:
- headers.append(('Content-encoding', encoding))
+ headers.append(('Content-Encoding', encoding))
req.header(headers)
- archival.archive(self.repo, req.out, cnode, artype, prefix=name)
+ archival.archive(self.repo, req, cnode, artype, prefix=name)
# add tags to things
# tags -> list of changesets corresponding to tags
@@ -865,9 +863,9 @@
return util.canonpath(self.repo.root, '', path)
def changectx(self, req):
- if req.form.has_key('node'):
+ if 'node' in req.form:
changeid = req.form['node'][0]
- elif req.form.has_key('manifest'):
+ elif 'manifest' in req.form:
changeid = req.form['manifest'][0]
else:
changeid = self.repo.changelog.count() - 1
@@ -883,7 +881,7 @@
def filectx(self, req):
path = self.cleanpath(req.form['file'][0])
- if req.form.has_key('node'):
+ if 'node' in req.form:
changeid = req.form['node'][0]
else:
changeid = req.form['filenode'][0]
--- a/mercurial/hgweb/hgwebdir_mod.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/hgweb/hgwebdir_mod.py Fri Jan 25 16:04:46 2008 -0800
@@ -6,7 +6,7 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os, mimetools, cStringIO
+import os
from mercurial.i18n import gettext as _
from mercurial import ui, hg, util, templater
from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, \
@@ -143,7 +143,7 @@
def entries(sortcolumn="", descending=False, subdir="", **map):
def sessionvars(**map):
fields = []
- if req.form.has_key('style'):
+ if 'style' in req.form:
style = req.form['style'][0]
if style != get('web', 'style', ''):
fields.append(('style', style))
@@ -214,7 +214,7 @@
sortable = ["name", "description", "contact", "lastchange"]
sortcolumn, descending = self.repos_sorted
- if req.form.has_key('sort'):
+ if 'sort' in req.form:
sortcolumn = req.form['sort'][0]
descending = sortcolumn.startswith('-')
if descending:
@@ -226,6 +226,7 @@
"%s%s" % ((not descending and column == sortcolumn)
and "-" or "", column))
for column in sortable]
+
req.write(tmpl("index", entries=entries, subdir=subdir,
sortcolumn=sortcolumn, descending=descending,
**dict(sort)))
@@ -233,11 +234,9 @@
def templater(self, req):
def header(**map):
- header_file = cStringIO.StringIO(
- ''.join(tmpl("header", encoding=util._encoding, **map)))
- msg = mimetools.Message(header_file, 0)
- req.header(msg.items())
- yield header_file.read()
+ ctype = tmpl('mimetype', encoding=util._encoding)
+ req.httphdr(templater.stringify(ctype))
+ yield tmpl('header', encoding=util._encoding, **map)
def footer(**map):
yield tmpl("footer", **map)
@@ -262,7 +261,7 @@
style = self.style
if style is None:
style = config('web', 'style', '')
- if req.form.has_key('style'):
+ if 'style' in req.form:
style = req.form['style'][0]
if self.stripecount is None:
self.stripecount = int(config('web', 'stripes', 1))
--- a/mercurial/hgweb/protocol.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/hgweb/protocol.py Fri Jan 25 16:04:46 2008 -0800
@@ -28,7 +28,7 @@
def branches(web, req):
nodes = []
- if req.form.has_key('nodes'):
+ if 'nodes' in req.form:
nodes = map(bin, req.form['nodes'][0].split(" "))
resp = cStringIO.StringIO()
for b in web.repo.branches(nodes):
@@ -38,7 +38,7 @@
req.write(resp)
def between(web, req):
- if req.form.has_key('pairs'):
+ if 'pairs' in req.form:
pairs = [map(bin, p.split("-"))
for p in req.form['pairs'][0].split(" ")]
resp = cStringIO.StringIO()
@@ -54,7 +54,7 @@
if not web.allowpull:
return
- if req.form.has_key('roots'):
+ if 'roots' in req.form:
nodes = map(bin, req.form['roots'][0].split(" "))
z = zlib.compressobj()
@@ -74,9 +74,9 @@
if not web.allowpull:
return
- if req.form.has_key('bases'):
+ if 'bases' in req.form:
bases = [bin(x) for x in req.form['bases'][0].split(' ')]
- if req.form.has_key('heads'):
+ if 'heads' in req.form:
heads = [bin(x) for x in req.form['heads'][0].split(' ')]
z = zlib.compressobj()
--- a/mercurial/hgweb/request.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/hgweb/request.py Fri Jan 25 16:04:46 2008 -0800
@@ -24,35 +24,43 @@
self.run_once = wsgienv['wsgi.run_once']
self.env = wsgienv
self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
- self.start_response = start_response
+ self._start_response = start_response
self.headers = []
- out = property(lambda self: self)
-
def __iter__(self):
return iter([])
def read(self, count=-1):
return self.inp.read(count)
+ def start_response(self, status):
+ if self._start_response is not None:
+ if not self.headers:
+ raise RuntimeError("request.write called before headers sent")
+
+ for k, v in self.headers:
+ if not isinstance(v, str):
+ raise TypeError('header value must be string: %r' % v)
+
+ if isinstance(status, ErrorResponse):
+ status = statusmessage(status.code)
+ elif isinstance(status, int):
+ status = statusmessage(status)
+
+ self.server_write = self._start_response(status, self.headers)
+ self._start_response = None
+ self.headers = []
+
def respond(self, status, *things):
+ if not things:
+ self.start_response(status)
for thing in things:
if hasattr(thing, "__iter__"):
for part in thing:
self.respond(status, part)
else:
thing = str(thing)
- if self.server_write is None:
- if not self.headers:
- raise RuntimeError("request.write called before headers sent (%s)." % thing)
- if isinstance(status, ErrorResponse):
- status = statusmessage(status.code)
- elif isinstance(status, int):
- status = statusmessage(status)
- self.server_write = self.start_response(status,
- self.headers)
- self.start_response = None
- self.headers = []
+ self.start_response(status)
try:
self.server_write(thing)
except socket.error, inst:
@@ -72,21 +80,23 @@
def close(self):
return None
- def header(self, headers=[('Content-type','text/html')]):
+ def header(self, headers=[('Content-Type','text/html')]):
self.headers.extend(headers)
def httphdr(self, type, filename=None, length=0, headers={}):
headers = headers.items()
- headers.append(('Content-type', type))
+ headers.append(('Content-Type', type))
if filename:
- headers.append(('Content-disposition', 'attachment; filename=%s' %
+ headers.append(('Content-Disposition', 'inline; filename=%s' %
filename))
if length:
- headers.append(('Content-length', str(length)))
+ headers.append(('Content-Length', str(length)))
self.header(headers)
def wsgiapplication(app_maker):
+ '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
+ can and should now be used as a WSGI application.'''
application = app_maker()
def run_wsgi(env, respond):
- application(env, respond)
+ return application(env, respond)
return run_wsgi
--- a/mercurial/hgweb/webcommands.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/hgweb/webcommands.py Fri Jan 25 16:04:46 2008 -0800
@@ -5,16 +5,37 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os
-from mercurial import revlog
-from common import staticfile
+import os, mimetypes
+from mercurial import revlog, util
+from common import staticfile, ErrorResponse
def log(web, req, tmpl):
- if req.form.has_key('file') and req.form['file'][0]:
+ if 'file' in req.form and req.form['file'][0]:
filelog(web, req, tmpl)
else:
changelog(web, req, tmpl)
+def rawfile(web, req, tmpl):
+ path = web.cleanpath(req.form.get('file', [''])[0])
+ if not path:
+ req.write(web.manifest(tmpl, web.changectx(req), path))
+ return
+
+ try:
+ fctx = web.filectx(req)
+ except revlog.LookupError:
+ req.write(web.manifest(tmpl, web.changectx(req), path))
+ return
+
+ path = fctx.path()
+ text = fctx.data()
+ mt = mimetypes.guess_type(path)[0]
+ if mt is None or util.binary(text):
+ mt = mt or 'application/octet-stream'
+
+ req.httphdr(mt, path, len(text))
+ req.write(text)
+
def file(web, req, tmpl):
path = web.cleanpath(req.form.get('file', [''])[0])
if path:
@@ -27,10 +48,10 @@
req.write(web.manifest(tmpl, web.changectx(req), path))
def changelog(web, req, tmpl, shortlog = False):
- if req.form.has_key('node'):
+ if 'node' in req.form:
ctx = web.changectx(req)
else:
- if req.form.has_key('rev'):
+ if 'rev' in req.form:
hi = req.form['rev'][0]
else:
hi = web.repo.changelog.count() - 1
@@ -79,8 +100,7 @@
web.archive(tmpl, req, req.form['node'][0], type_)
return
- req.respond(400, tmpl('error',
- error='Unsupported archive type: %s' % type_))
+ raise ErrorResponse(400, 'Unsupported archive type: %s' % type_)
def static(web, req, tmpl):
fname = req.form['file'][0]
--- a/mercurial/hook.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/hook.py Fri Jan 25 16:04:46 2008 -0800
@@ -71,7 +71,11 @@
def _exthook(ui, repo, name, cmd, args, throw):
ui.note(_("running hook %s: %s\n") % (name, cmd))
env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
- r = util.system(cmd, environ=env, cwd=repo.root)
+ if repo:
+ cwd = repo.root
+ else:
+ cwd = os.getcwd()
+ r = util.system(cmd, environ=env, cwd=cwd)
if r:
desc, r = util.explain_exit(r)
if throw:
--- a/mercurial/httprepo.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/httprepo.py Fri Jan 25 16:04:46 2008 -0800
@@ -247,7 +247,7 @@
# will take precedence if found, so drop them
for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
try:
- if os.environ.has_key(env):
+ if env in os.environ:
del os.environ[env]
except OSError:
pass
@@ -343,7 +343,7 @@
version = proto.split('-', 1)[1]
version_info = tuple([int(n) for n in version.split('.')])
except ValueError:
- raise repo.RepoError(_("'%s' sent a broken Content-type "
+ raise repo.RepoError(_("'%s' sent a broken Content-Type "
"header (%s)") % (self._url, proto))
if version_info > (0, 1):
raise repo.RepoError(_("'%s' uses newer protocol %s") %
@@ -428,7 +428,7 @@
try:
rfp = self.do_cmd(
'unbundle', data=fp,
- headers={'content-type': 'application/octet-stream'},
+ headers={'Content-Type': 'application/octet-stream'},
heads=' '.join(map(hex, heads)))
try:
ret = int(rfp.readline())
--- a/mercurial/keepalive.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/keepalive.py Fri Jan 25 16:04:46 2008 -0800
@@ -129,7 +129,7 @@
def add(self, host, connection, ready):
self._lock.acquire()
try:
- if not self._hostmap.has_key(host): self._hostmap[host] = []
+ if not host in self._hostmap: self._hostmap[host] = []
self._hostmap[host].append(connection)
self._connmap[connection] = host
self._readymap[connection] = ready
@@ -159,7 +159,7 @@
conn = None
self._lock.acquire()
try:
- if self._hostmap.has_key(host):
+ if host in self._hostmap:
for c in self._hostmap[host]:
if self._readymap[c]:
self._readymap[c] = 0
--- a/mercurial/localrepo.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/localrepo.py Fri Jan 25 16:04:46 2008 -0800
@@ -687,6 +687,8 @@
p1=None, p2=None, extra={}, empty_ok=False):
wlock = lock = tr = None
valid = 0 # don't save the dirstate if this isn't set
+ if files:
+ files = util.unique(files)
try:
commit = []
remove = []
@@ -996,7 +998,7 @@
mf2keys.sort()
getnode = lambda fn: mf1.get(fn, nullid)
for fn in mf2keys:
- if mf1.has_key(fn):
+ if fn in mf1:
if (mf1.flags(fn) != mf2.flags(fn) or
(mf1[fn] != mf2[fn] and
(mf2[fn] != "" or fcmp(fn, getnode)))):
@@ -1510,7 +1512,7 @@
for node in nodes:
self.ui.debug("%s\n" % hex(node))
- def changegroupsubset(self, bases, heads, source):
+ def changegroupsubset(self, bases, heads, source, extranodes=None):
"""This function generates a changegroup consisting of all the nodes
that are descendents of any of the bases, and ancestors of any of
the heads.
@@ -1520,7 +1522,15 @@
is non-trivial.
Another wrinkle is doing the reverse, figuring out which changeset in
- the changegroup a particular filenode or manifestnode belongs to."""
+ the changegroup a particular filenode or manifestnode belongs to.
+
+ The caller can specify some nodes that must be included in the
+ changegroup using the extranodes argument. It should be a dict
+ where the keys are the filenames (or 1 for the manifest), and the
+ values are lists of (node, linknode) tuples, where node is a wanted
+ node and linknode is the changelog node that should be transmitted as
+ the linkrev.
+ """
self.hook('preoutgoing', throw=True, source=source)
@@ -1713,6 +1723,15 @@
return msngset[fnode]
return lookup_filenode_link
+ # Add the nodes that were explicitly requested.
+ def add_extra_nodes(name, nodes):
+ if not extranodes or name not in extranodes:
+ return
+
+ for node, linknode in extranodes[name]:
+ if node not in nodes:
+ nodes[node] = linknode
+
# Now that we have all theses utility functions to help out and
# logically divide up the task, generate the group.
def gengroup():
@@ -1728,6 +1747,7 @@
# The list of manifests has been collected by the generator
# calling our functions back.
prune_manifests()
+ add_extra_nodes(1, msng_mnfst_set)
msng_mnfst_lst = msng_mnfst_set.keys()
# Sort the manifestnodes by revision number.
msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
@@ -1743,6 +1763,13 @@
msng_mnfst_lst = None
msng_mnfst_set.clear()
+ if extranodes:
+ for fname in extranodes:
+ if isinstance(fname, int):
+ continue
+ add_extra_nodes(fname,
+ msng_filenode_set.setdefault(fname, {}))
+ changedfiles[fname] = 1
changedfiles = changedfiles.keys()
changedfiles.sort()
# Go through all our files in order sorted by name.
@@ -1752,7 +1779,7 @@
raise util.Abort(_("empty or missing revlog for %s") % fname)
# Toss out the filenodes that the recipient isn't really
# missing.
- if msng_filenode_set.has_key(fname):
+ if fname in msng_filenode_set:
prune_filenodes(fname, filerevlog)
msng_filenode_lst = msng_filenode_set[fname].keys()
else:
@@ -1771,7 +1798,7 @@
lookup_filenode_link_func(fname))
for chnk in group:
yield chnk
- if msng_filenode_set.has_key(fname):
+ if fname in msng_filenode_set:
# Don't need this anymore, toss it to free memory.
del msng_filenode_set[fname]
# Signal that no more groups are left.
@@ -1852,7 +1879,7 @@
return util.chunkbuffer(gengroup())
- def addchangegroup(self, source, srctype, url):
+ def addchangegroup(self, source, srctype, url, emptyok=False):
"""add changegroup to repo.
return values:
@@ -1888,7 +1915,7 @@
self.ui.status(_("adding changesets\n"))
cor = cl.count() - 1
chunkiter = changegroup.chunkiter(source)
- if cl.addgroup(chunkiter, csmap, trp, 1) is None:
+ if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok:
raise util.Abort(_("received changelog group is empty"))
cnr = cl.count() - 1
changesets = cnr - cor
--- a/mercurial/patch.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/patch.py Fri Jan 25 16:04:46 2008 -0800
@@ -1372,7 +1372,8 @@
try:
p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
try:
- for line in patchlines: print >> p.tochild, line
+ for line in patchlines:
+ p.tochild.write(line + "\n")
p.tochild.close()
if p.wait(): return
fp = os.fdopen(fd, 'r')
--- a/mercurial/repair.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/repair.py Fri Jan 25 16:04:46 2008 -0800
@@ -6,71 +6,86 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import changegroup, revlog, os, commands
+import changegroup, os
+from node import *
-def strip(ui, repo, rev, backup="all"):
- def limitheads(chlog, stop):
- """return the list of all nodes that have no children"""
- p = {}
- h = []
- stoprev = 0
- if stop in chlog.nodemap:
- stoprev = chlog.rev(stop)
+def _limitheads(cl, stoprev):
+ """return the list of all revs >= stoprev that have no children"""
+ seen = {}
+ heads = []
+
+ for r in xrange(cl.count() - 1, stoprev - 1, -1):
+ if r not in seen:
+ heads.append(r)
+ for p in cl.parentrevs(r):
+ seen[p] = 1
+ return heads
- for r in xrange(chlog.count() - 1, -1, -1):
- n = chlog.node(r)
- if n not in p:
- h.append(n)
- if n == stop:
- break
- if r < stoprev:
- break
- for pn in chlog.parents(n):
- p[pn] = 1
- return h
+def _bundle(repo, bases, heads, node, suffix, extranodes=None):
+ """create a bundle with the specified revisions as a backup"""
+ cg = repo.changegroupsubset(bases, heads, 'strip', extranodes)
+ backupdir = repo.join("strip-backup")
+ if not os.path.isdir(backupdir):
+ os.mkdir(backupdir)
+ name = os.path.join(backupdir, "%s-%s" % (short(node), suffix))
+ repo.ui.warn("saving bundle to %s\n" % name)
+ return changegroup.writebundle(cg, name, "HG10BZ")
- def bundle(repo, bases, heads, rev, suffix):
- cg = repo.changegroupsubset(bases, heads, 'strip')
- backupdir = repo.join("strip-backup")
- if not os.path.isdir(backupdir):
- os.mkdir(backupdir)
- name = os.path.join(backupdir, "%s-%s" % (revlog.short(rev), suffix))
- ui.warn("saving bundle to %s\n" % name)
- return changegroup.writebundle(cg, name, "HG10BZ")
+def _collectfiles(repo, striprev):
+ """find out the filelogs affected by the strip"""
+ files = {}
+
+ for x in xrange(striprev, repo.changelog.count()):
+ for name in repo.changectx(x).files():
+ if name in files:
+ continue
+ files[name] = 1
+
+ files = files.keys()
+ files.sort()
+ return files
- def stripall(revnum):
- mm = repo.changectx(rev).manifest()
- seen = {}
+def _collectextranodes(repo, files, link):
+ """return the nodes that have to be saved before the strip"""
+ def collectone(revlog):
+ extra = []
+ startrev = count = revlog.count()
+ # find the truncation point of the revlog
+ for i in xrange(0, count):
+ node = revlog.node(i)
+ lrev = revlog.linkrev(node)
+ if lrev >= link:
+ startrev = i + 1
+ break
+
+ # see if any revision after that point has a linkrev less than link
+ # (we have to manually save these guys)
+ for i in xrange(startrev, count):
+ node = revlog.node(i)
+ lrev = revlog.linkrev(node)
+ if lrev < link:
+ extra.append((node, cl.node(lrev)))
- for x in xrange(revnum, repo.changelog.count()):
- for f in repo.changectx(x).files():
- if f in seen:
- continue
- seen[f] = 1
- if f in mm:
- filerev = mm[f]
- else:
- filerev = 0
- seen[f] = filerev
- # we go in two steps here so the strip loop happens in a
- # sensible order. When stripping many files, this helps keep
- # our disk access patterns under control.
- seen_list = seen.keys()
- seen_list.sort()
- for f in seen_list:
- ff = repo.file(f)
- filerev = seen[f]
- if filerev != 0:
- if filerev in ff.nodemap:
- filerev = ff.rev(filerev)
- else:
- filerev = 0
- ff.strip(filerev, revnum)
+ return extra
- chlog = repo.changelog
+ extranodes = {}
+ cl = repo.changelog
+ extra = collectone(repo.manifest)
+ if extra:
+ extranodes[1] = extra
+ for fname in files:
+ f = repo.file(fname)
+ extra = collectone(f)
+ if extra:
+ extranodes[fname] = extra
+
+ return extranodes
+
+def strip(ui, repo, node, backup="all"):
+ cl = repo.changelog
# TODO delete the undo files, and handle undo of merge sets
- pp = chlog.parents(rev)
- revnum = chlog.rev(rev)
+ pp = cl.parents(node)
+ striprev = cl.rev(node)
# save is a list of all the branches we are truncating away
# that we actually want to keep. changegroup will be used
@@ -78,7 +93,7 @@
saveheads = []
savebases = {}
- heads = limitheads(chlog, rev)
+ heads = [cl.node(r) for r in _limitheads(cl, striprev)]
seen = {}
# search through all the heads, finding those where the revision
@@ -89,39 +104,48 @@
n = h
while True:
seen[n] = 1
- pp = chlog.parents(n)
- if pp[1] != revlog.nullid:
+ pp = cl.parents(n)
+ if pp[1] != nullid:
for p in pp:
- if chlog.rev(p) > revnum and p not in seen:
+ if cl.rev(p) > striprev and p not in seen:
heads.append(p)
- if pp[0] == revlog.nullid:
+ if pp[0] == nullid:
break
- if chlog.rev(pp[0]) < revnum:
+ if cl.rev(pp[0]) < striprev:
break
n = pp[0]
- if n == rev:
+ if n == node:
break
- r = chlog.reachable(h, rev)
- if rev not in r:
+ r = cl.reachable(h, node)
+ if node not in r:
saveheads.append(h)
for x in r:
- if chlog.rev(x) > revnum:
+ if cl.rev(x) > striprev:
savebases[x] = 1
+ files = _collectfiles(repo, striprev)
+
+ extranodes = _collectextranodes(repo, files, striprev)
+
# create a changegroup for all the branches we need to keep
if backup == "all":
- bundle(repo, [rev], chlog.heads(), rev, 'backup')
- if saveheads:
- chgrpfile = bundle(repo, savebases.keys(), saveheads, rev, 'temp')
-
- stripall(revnum)
+ _bundle(repo, [node], cl.heads(), node, 'backup')
+ if saveheads or extranodes:
+ chgrpfile = _bundle(repo, savebases.keys(), saveheads, node, 'temp',
+ extranodes)
- change = chlog.read(rev)
- chlog.strip(revnum, revnum)
- repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
- if saveheads:
+ cl.strip(striprev)
+ repo.manifest.strip(striprev)
+ for name in files:
+ f = repo.file(name)
+ f.strip(striprev)
+
+ if saveheads or extranodes:
ui.status("adding branch\n")
- commands.unbundle(ui, repo, "file:%s" % chgrpfile, update=False)
+ f = open(chgrpfile, "rb")
+ gen = changegroup.readbundle(f, chgrpfile)
+ repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
+ f.close()
if backup != "strip":
os.unlink(chgrpfile)
--- a/mercurial/revlog.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/revlog.py Fri Jan 25 16:04:46 2008 -0800
@@ -1237,21 +1237,31 @@
return node
- def strip(self, rev, minlink):
- if self.count() == 0 or rev >= self.count():
+ def strip(self, minlink):
+ """truncate the revlog on the first revision with a linkrev >= minlink
+
+ This function is called when we're stripping revision minlink and
+ its descendants from the repository.
+
+ We have to remove all revisions with linkrev >= minlink, because
+ the equivalent changelog revisions will be renumbered after the
+ strip.
+
+ So we truncate the revlog on the first of these revisions, and
+ trust that the caller has saved the revisions that shouldn't be
+ removed and that it'll readd them after this truncation.
+ """
+ if self.count() == 0:
return
if isinstance(self.index, lazyindex):
self._loadindexmap()
- # When stripping away a revision, we need to make sure it
- # does not actually belong to an older changeset.
- # The minlink parameter defines the oldest revision
- # we're allowed to strip away.
- while minlink > self.index[rev][4]:
- rev += 1
- if rev >= self.count():
- return
+ for rev in xrange(0, self.count()):
+ if self.index[rev][4] >= minlink:
+ break
+ else:
+ return
# first truncate the files on disk
end = self.start(rev)
--- a/mercurial/sshrepo.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/sshrepo.py Fri Jan 25 16:04:46 2008 -0800
@@ -195,7 +195,7 @@
r = self.pipei.read(l)
if r:
# remote may send "unsynced changes"
- self.raise_(hg.RepoError(_("push failed: %s") % r))
+ self.raise_(repo.RepoError(_("push failed: %s") % r))
self.readerr()
l = int(self.pipei.readline())
--- a/mercurial/templater.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/templater.py Fri Jan 25 16:04:46 2008 -0800
@@ -82,7 +82,7 @@
'''perform expansion.
t is name of map element to expand.
map is added elements to use during expansion.'''
- if not self.cache.has_key(t):
+ if not t in self.cache:
try:
self.cache[t] = file(self.map[t]).read()
except IOError, inst:
--- a/mercurial/ui.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/ui.py Fri Jan 25 16:04:46 2008 -0800
@@ -204,7 +204,8 @@
pathsitems = items
for n, path in pathsitems:
if path and "://" not in path and not os.path.isabs(path):
- cdata.set("paths", n, os.path.join(root, path))
+ cdata.set("paths", n,
+ os.path.normpath(os.path.join(root, path)))
# update verbosity/interactive/report_untrusted settings
if section is None or section == 'ui':
--- a/mercurial/util.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/util.py Fri Jan 25 16:04:46 2008 -0800
@@ -15,7 +15,7 @@
from i18n import _
import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
-import re, urlparse
+import urlparse
try:
set = set
--- a/mercurial/util_win32.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/util_win32.py Fri Jan 25 16:04:46 2008 -0800
@@ -147,9 +147,18 @@
self.win_strerror)
def os_link(src, dst):
- # NB will only succeed on NTFS
try:
win32file.CreateHardLink(dst, src)
+ # CreateHardLink sometimes succeeds on mapped drives but
+ # following nlinks() returns 1. Check it now and bail out.
+ if nlinks(src) < 2:
+ try:
+ win32file.DeleteFile(dst)
+ except:
+ pass
+ # Fake hardlinking error
+ raise WinOSError((18, 'CreateHardLink', 'The system cannot '
+ 'move the file to a different disk drive'))
except pywintypes.error, details:
raise WinOSError(details)
--- a/mercurial/version.py Fri Jan 25 16:04:32 2008 -0800
+++ b/mercurial/version.py Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006 by Intevation GmbH
+# Copyright (C) 2005, 2006, 2008 by Intevation GmbH
# Author(s):
# Thomas Arendsen Hein <thomas@intevation.de>
#
@@ -10,7 +10,6 @@
"""
import os
-import os.path
import re
import time
import util
--- a/templates/atom/header.tmpl Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/atom/header.tmpl Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,2 @@
-Content-type: application/atom+xml; charset={encoding}
-
<?xml version="1.0" encoding="{encoding}"?>
<feed xmlns="http://www.w3.org/2005/Atom">
\ No newline at end of file
--- a/templates/atom/map Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/atom/map Fri Jan 25 16:04:46 2008 -0800
@@ -1,5 +1,6 @@
default = 'changelog'
feedupdated = '<updated>#date|rfc3339date#</updated>'
+mimetype = 'application/atom+xml; charset={encoding}'
header = header.tmpl
changelog = changelog.tmpl
changelogentry = changelogentry.tmpl
--- a/templates/gitweb/header.tmpl Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/gitweb/header.tmpl Fri Jan 25 16:04:46 2008 -0800
@@ -1,5 +1,3 @@
-Content-type: text/html; charset={encoding}
-
<?xml version="1.0" encoding="{encoding}"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
--- a/templates/gitweb/map Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/gitweb/map Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,5 @@
default = 'summary'
+mimetype = 'text/html; charset={encoding}'
header = header.tmpl
footer = footer.tmpl
search = search.tmpl
--- a/templates/header.tmpl Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/header.tmpl Fri Jan 25 16:04:46 2008 -0800
@@ -1,5 +1,3 @@
-Content-type: text/html; charset={encoding}
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
--- a/templates/map Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/map Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,5 @@
default = 'shortlog'
+mimetype = 'text/html; charset={encoding}'
header = header.tmpl
footer = footer.tmpl
search = search.tmpl
--- a/templates/old/header.tmpl Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/old/header.tmpl Fri Jan 25 16:04:46 2008 -0800
@@ -1,5 +1,3 @@
-Content-type: text/html
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
--- a/templates/old/map Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/old/map Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,5 @@
default = 'changelog'
+mimetype = 'text/html'
header = header.tmpl
footer = footer.tmpl
search = search.tmpl
--- a/templates/raw/header.tmpl Fri Jan 25 16:04:32 2008 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-Content-type: text/plain; charset={encoding}
-
--- a/templates/raw/map Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/raw/map Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,5 @@
-header = header.tmpl
+mimetype = 'text/plain; charset={encoding}'
+header = ''
footer = ''
changeset = changeset.tmpl
difflineplus = '#line#'
@@ -8,7 +9,6 @@
changesetparent = '# Parent #node#'
changesetchild = '# Child #node#'
filenodelink = ''
-filerevision = '#rawfileheader##raw#'
fileline = '#line#'
diffblock = '#lines#'
filediff = filediff.tmpl
--- a/templates/rss/header.tmpl Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/rss/header.tmpl Fri Jan 25 16:04:46 2008 -0800
@@ -1,5 +1,3 @@
-Content-type: text/xml; charset={encoding}
-
<?xml version="1.0" encoding="{encoding}"?>
<rss version="2.0">
<channel>
--- a/templates/rss/map Fri Jan 25 16:04:32 2008 -0800
+++ b/templates/rss/map Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,5 @@
default = 'changelog'
+mimetype = 'text/xml; charset={encoding}'
header = header.tmpl
changelog = changelog.tmpl
changelogentry = changelogentry.tmpl
--- a/tests/coverage.py Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/coverage.py Fri Jan 25 16:04:46 2008 -0800
@@ -186,9 +186,9 @@
return 0
# If this line is excluded, or suite_spots maps this line to
# another line that is exlcuded, then we're excluded.
- elif self.excluded.has_key(lineno) or \
- self.suite_spots.has_key(lineno) and \
- self.excluded.has_key(self.suite_spots[lineno][1]):
+ elif lineno in self.excluded or \
+ lineno in self.suite_spots and \
+ self.suite_spots[lineno][1] in self.excluded:
return 0
# Otherwise, this is an executable line.
else:
@@ -217,8 +217,8 @@
lastprev = self.getLastLine(prevsuite)
firstelse = self.getFirstLine(suite)
for l in range(lastprev+1, firstelse):
- if self.suite_spots.has_key(l):
- self.doSuite(None, suite, exclude=self.excluded.has_key(l))
+ if l in self.suite_spots:
+ self.doSuite(None, suite, l in exclude=self.excluded)
break
else:
self.doSuite(None, suite)
@@ -353,9 +353,9 @@
long_opts = optmap.values()
options, args = getopt.getopt(argv, short_opts, long_opts)
for o, a in options:
- if optmap.has_key(o):
+ if o in optmap:
settings[optmap[o]] = 1
- elif optmap.has_key(o + ':'):
+ elif o + ':' in optmap:
settings[optmap[o + ':']] = a
elif o[2:] in long_opts:
settings[o[2:]] = 1
@@ -512,14 +512,14 @@
def merge_data(self, new_data):
for file_name, file_data in new_data.items():
- if self.cexecuted.has_key(file_name):
+ if file_name in self.cexecuted:
self.merge_file_data(self.cexecuted[file_name], file_data)
else:
self.cexecuted[file_name] = file_data
def merge_file_data(self, cache_data, new_data):
for line_number in new_data.keys():
- if not cache_data.has_key(line_number):
+ if not line_number in cache_data:
cache_data[line_number] = new_data[line_number]
# canonical_filename(filename). Return a canonical filename for the
@@ -527,7 +527,7 @@
# normalized case). See [GDR 2001-12-04b, 3.3].
def canonical_filename(self, filename):
- if not self.canonical_filename_cache.has_key(filename):
+ if not filename in self.canonical_filename_cache:
f = filename
if os.path.isabs(f) and not os.path.exists(f):
f = os.path.basename(f)
@@ -550,7 +550,7 @@
# Can't do anything useful with exec'd strings, so skip them.
continue
f = self.canonical_filename(filename)
- if not self.cexecuted.has_key(f):
+ if not f in self.cexecuted:
self.cexecuted[f] = {}
self.cexecuted[f][lineno] = 1
self.c = {}
@@ -575,7 +575,7 @@
# statements that cross lines.
def analyze_morf(self, morf):
- if self.analysis_cache.has_key(morf):
+ if morf in self.analysis_cache:
return self.analysis_cache[morf]
filename = self.morf_filename(morf)
ext = os.path.splitext(filename)[1]
@@ -752,13 +752,13 @@
def analysis2(self, morf):
filename, statements, excluded, line_map = self.analyze_morf(morf)
self.canonicalize_filenames()
- if not self.cexecuted.has_key(filename):
+ if not filename in self.cexecuted:
self.cexecuted[filename] = {}
missing = []
for line in statements:
lines = line_map.get(line, [line, line])
for l in range(lines[0], lines[1]+1):
- if self.cexecuted[filename].has_key(l):
+ if l in self.cexecuted[filename]:
break
else:
missing.append(line)
--- a/tests/test-archive Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-archive Fri Jan 25 16:04:46 2008 -0800
@@ -13,7 +13,7 @@
echo "[web]" >> .hg/hgrc
echo "name = test-archive" >> .hg/hgrc
echo "allow_archive = gz bz2, zip" >> .hg/hgrc
-hg serve -p $HGPORT -d --pid-file=hg.pid
+hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
cat hg.pid >> $DAEMON_PIDS
TIP=`hg id -v | cut -f1 -d' '`
@@ -69,8 +69,12 @@
echo 'rev-0.tar created'
fi
+echo % server errors
+cat errors.log
+
echo '% empty repo'
hg init ../empty
cd ../empty
hg archive ../test-empty
+
exit 0
--- a/tests/test-archive.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-archive.out Fri Jan 25 16:04:46 2008 -0800
@@ -39,5 +39,6 @@
test-TIP/baz/bletch
test-TIP/foo
rev-0.tar created
+% server errors
% empty repo
abort: repository has no revisions
--- a/tests/test-convert-cvs Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-convert-cvs Fri Jan 25 16:04:46 2008 -0800
@@ -71,3 +71,25 @@
cat src-hg/b/c
hg -R src-filemap log --template '#rev# #desc# files: #files#\n'
+echo % commit branch
+cd src
+cvs -q update -r1.1 b/c
+cvs -q tag -b branch
+cvs -q update -r branch
+echo d >> b/c
+cvs -q commit -mci2 . | grep '<--' |\
+ sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
+cd ..
+
+echo % convert again
+hg convert src src-hg | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
+cat src-hg/a
+cat src-hg/b/c
+
+echo % convert again with --filemap
+hg convert --filemap filemap src src-filemap | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
+cat src-hg/b/c
+hg -R src-filemap log --template '#rev# #desc# files: #files#\n'
+
+echo "graphlog = " >> $HGRCPATH
+hg -R src-hg glog --template '#rev# (#branches#) #desc# files: #files#\n'
--- a/tests/test-convert-cvs.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-convert-cvs.out Fri Jan 25 16:04:46 2008 -0800
@@ -67,3 +67,43 @@
2 update tags files: .hgtags
1 ci0 files: b/c
0 Initial revision files: b/c
+% commit branch
+U b/c
+T a
+T b/c
+checking in src/b/c,v
+% convert again
+connecting to cvsrepo
+scanning source...
+sorting...
+converting...
+0 ci2
+a
+a
+c
+d
+% convert again with --filemap
+connecting to cvsrepo
+scanning source...
+sorting...
+converting...
+0 ci2
+c
+d
+4 ci2 files: b/c
+3 ci1 files: b/c
+2 update tags files: .hgtags
+1 ci0 files: b/c
+0 Initial revision files: b/c
+o 5 (branch) ci2 files: b/c
+|
+| o 4 () ci1 files: a b/c
+| |
+| o 3 () update tags files: .hgtags
+| |
+| o 2 () ci0 files: b/c
+|/
+| o 1 (INITIAL) import files:
+|/
+o 0 () Initial revision files: a b/c
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-branches Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" svn svn-bindings || exit 80
+
+fix_path()
+{
+ tr '\\' /
+}
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert = " >> $HGRCPATH
+echo "hgext.graphlog =" >> $HGRCPATH
+
+svnadmin create svn-repo
+
+svnpath=`pwd | fix_path`
+# SVN wants all paths to start with a slash. Unfortunately,
+# Windows ones don't. Handle that.
+expr $svnpath : "\/" > /dev/null
+if [ $? -ne 0 ]; then
+ svnpath='/'$svnpath
+fi
+
+echo % initial svn import
+mkdir projA
+cd projA
+mkdir trunk
+mkdir branches
+mkdir tags
+cd ..
+
+svnurl=file://$svnpath/svn-repo/projA
+svn import -m "init projA" projA $svnurl | fix_path
+
+echo % update svn repository
+svn co $svnurl A | fix_path
+cd A
+echo hello > trunk/letter.txt
+echo hey > trunk/letter2.txt
+echo ho > trunk/letter3.txt
+svn add trunk/letter.txt trunk/letter2.txt trunk/letter3.txt
+svn ci -m hello
+
+echo % branch to old letters
+svn copy trunk branches/old
+svn rm branches/old/letter3.txt
+svn ci -m "branch trunk, remove letter3"
+svn up
+
+echo % update trunk
+echo "what can I say ?" >> trunk/letter.txt
+svn ci -m "change letter"
+
+echo % update old branch
+echo "what's up ?" >> branches/old/letter2.txt
+svn ci -m "change letter2"
+
+echo % create a cross-branch revision
+svn move -m "move letter2" trunk/letter2.txt \
+ branches/old/letter3.txt
+echo "I am fine" >> branches/old/letter3.txt
+svn ci -m "move and update letter3.txt"
+
+echo % update old branch again
+echo "bye" >> branches/old/letter2.txt
+svn ci -m "change letter2 again"
+
+echo % update trunk again
+echo "how are you ?" >> trunk/letter.txt
+svn ci -m "last change to letter"
+cd ..
+
+echo % convert trunk and branches
+hg convert --datesort $svnurl A-hg
+
+echo % branch again from a converted revision
+cd A
+svn copy -r 1 $svnurl/trunk branches/old2
+svn ci -m "branch trunk@1 into old2"
+cd ..
+
+echo % convert again
+hg convert --datesort $svnurl A-hg
+
+cd A-hg
+hg glog --template '#rev# #desc|firstline# files: #files#\n'
+hg branches | sed 's/:.*/:/'
+hg tags -q
+cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-branches.out Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,101 @@
+% initial svn import
+Adding projA/trunk
+Adding projA/branches
+Adding projA/tags
+
+Committed revision 1.
+% update svn repository
+A A/trunk
+A A/branches
+A A/tags
+Checked out revision 1.
+A trunk/letter.txt
+A trunk/letter2.txt
+A trunk/letter3.txt
+Adding trunk/letter.txt
+Adding trunk/letter2.txt
+Adding trunk/letter3.txt
+Transmitting file data ...
+Committed revision 2.
+% branch to old letters
+A branches/old
+D branches/old/letter3.txt
+Adding branches/old
+Adding branches/old/letter.txt
+Adding branches/old/letter2.txt
+Deleting branches/old/letter3.txt
+
+Committed revision 3.
+At revision 3.
+% update trunk
+Sending trunk/letter.txt
+Transmitting file data .
+Committed revision 4.
+% update old branch
+Sending branches/old/letter2.txt
+Transmitting file data .
+Committed revision 5.
+% create a cross-branch revision
+A branches/old/letter3.txt
+D trunk/letter2.txt
+Adding branches/old/letter3.txt
+Deleting trunk/letter2.txt
+Transmitting file data .
+Committed revision 6.
+% update old branch again
+Sending branches/old/letter2.txt
+Transmitting file data .
+Committed revision 7.
+% update trunk again
+Sending trunk/letter.txt
+Transmitting file data .
+Committed revision 8.
+% convert trunk and branches
+initializing destination A-hg repository
+scanning source...
+sorting...
+converting...
+8 init projA
+7 hello
+6 branch trunk, remove letter3
+5 change letter
+4 change letter2
+3 move and update letter3.txt
+2 move and update letter3.txt
+1 change letter2 again
+0 last change to letter
+% branch again from a converted revision
+Checked out revision 1.
+A branches/old2
+Adding branches/old2
+
+Committed revision 9.
+% convert again
+scanning source...
+sorting...
+converting...
+0 branch trunk@1 into old2
+o 9 branch trunk@1 into old2 files:
+|
+| o 8 last change to letter files: letter.txt
+| |
+| | o 7 change letter2 again files: letter2.txt
+| | |
+| o | 6 move and update letter3.txt files: letter2.txt
+| | |
+| | o 5 move and update letter3.txt files: letter3.txt
+| | |
+| | o 4 change letter2 files: letter2.txt
+| | |
+| o | 3 change letter files: letter.txt
+| | |
++---o 2 branch trunk, remove letter3 files: letter.txt letter2.txt
+| |
+| o 1 hello files: letter.txt letter2.txt letter3.txt
+|/
+o 0 init projA files:
+
+old2 9:
+default 8:
+old 7:
+tip
--- a/tests/test-convert-svn-source Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-convert-svn-source Fri Jan 25 16:04:46 2008 -0800
@@ -103,13 +103,16 @@
echo % update svn repository again
cd A
echo "see second letter" >> letter.txt
-echo "nice to meet you" > letter2.txt
-svn add letter2.txt
+# Put it in a subdirectory to test duplicate file records
+# from svn source (issue 714)
+mkdir todo
+echo "nice to meet you" > todo/letter2.txt
+svn add todo
svn ci -m "second letter"
svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2
-echo "blah-blah-blah" >> letter2.txt
+echo "blah-blah-blah" >> todo/letter2.txt
svn ci -m "work in progress"
cd ..
--- a/tests/test-convert-svn-source.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-convert-svn-source.out Fri Jan 25 16:04:46 2008 -0800
@@ -82,14 +82,16 @@
0 nice day
updating tags
% update svn repository again
-A letter2.txt
+A todo
+A todo/letter2.txt
Sending letter.txt
-Adding letter2.txt
+Adding todo
+Adding todo/letter2.txt
Transmitting file data ..
Committed revision 9.
Committed revision 10.
-Sending letter2.txt
+Sending todo/letter2.txt
Transmitting file data .
Committed revision 11.
% test incremental conversion
@@ -101,9 +103,9 @@
updating tags
o 7 update tags files: .hgtags
|
-o 6 work in progress files: letter2.txt
+o 6 work in progress files: todo/letter2.txt
|
-o 5 second letter files: letter.txt letter2.txt
+o 5 second letter files: letter.txt todo/letter2.txt
|
o 4 update tags files: .hgtags
|
--- a/tests/test-glog Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-glog Fri Jan 25 16:04:46 2008 -0800
@@ -139,5 +139,8 @@
echo % glog
hg glog
+echo % file glog
+hg glog 5
+
echo % unused arguments
-hg glog -q foo || echo failed
+hg glog -q foo bar || echo failed
--- a/tests/test-glog.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-glog.out Fri Jan 25 16:04:46 2008 -0800
@@ -307,9 +307,17 @@
date: Thu Jan 01 00:00:00 1970 +0000
summary: (0) root
+% file glog
+o changeset: 5:3589c3c477ab
+ parent: 3:02173ffbf857
+ parent: 4:e2cad8233c77
+ user: test
+ date: Thu Jan 01 00:00:05 1970 +0000
+ summary: (5) expand
+
% unused arguments
hg glog: invalid arguments
-hg glog [OPTION]...
+hg glog [OPTION]... [FILE]
show revision history alongside an ASCII revision graph
failed
--- a/tests/test-hgweb Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-hgweb Fri Jan 25 16:04:46 2008 -0800
@@ -7,7 +7,7 @@
echo foo > da/foo
echo foo > foo
hg ci -Ambase -d '0 0'
-hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
+hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
cat hg.pid >> $DAEMON_PIDS
echo % manifest
("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw')
@@ -37,3 +37,6 @@
echo % static file
"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/style-gitweb.css'
+
+echo % errors
+cat errors.log
--- a/tests/test-hgweb-commands Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-hgweb-commands Fri Jan 25 16:04:46 2008 -0800
@@ -27,6 +27,7 @@
"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw'
"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw'
"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo' | sed "s/[0-9]* years/many years/"
"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw'
echo % Overviews
Binary file tests/test-hgweb-commands.out has changed
--- a/tests/test-hgweb-no-request-uri.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-hgweb-no-request-uri.out Fri Jan 25 16:04:46 2008 -0800
@@ -7,7 +7,7 @@
---- HEADERS
200 Script output follows
---- DATA
-[('content-type', 'application/atom+xml; charset=ascii')]
+[('Content-Type', 'application/atom+xml; charset=ascii')]
<?xml version="1.0" encoding="ascii"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<!-- Changelog -->
@@ -41,7 +41,7 @@
---- HEADERS
200 Script output follows
---- DATA
-[('content-type', 'text/plain; charset=ascii')]
+[('Content-Type', 'text/plain; charset=ascii')]
-rw-r--r-- 4 bar
@@ -52,7 +52,7 @@
---- HEADERS
200 Script output follows
---- DATA
-[('content-type', 'text/plain; charset=ascii')]
+[('Content-Type', 'text/plain; charset=ascii')]
/repo/
@@ -62,7 +62,7 @@
---- HEADERS
200 Script output follows
---- DATA
-[('content-type', 'text/plain; charset=ascii')]
+[('Content-Type', 'text/plain; charset=ascii')]
-rw-r--r-- 4 bar
--- a/tests/test-hgweb.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-hgweb.out Fri Jan 25 16:04:46 2008 -0800
@@ -136,3 +136,4 @@
background-color: #aaffaa;
border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
}
+% errors
--- a/tests/test-import Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-import Fri Jan 25 16:04:46 2008 -0800
@@ -32,6 +32,13 @@
hg --cwd b import -mpatch ../tip.patch
rm -r b
+echo % import of plain diff should be ok with --no-commit
+hg clone -r0 a b
+hg --cwd a diff -r0:1 > tip.patch
+hg --cwd b import --no-commit ../tip.patch
+hg --cwd b diff --nodates
+rm -r b
+
echo % hg -R repo import
# put the clone in a subdir - having a directory named "a"
# used to hide a bug.
--- a/tests/test-import.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-import.out Fri Jan 25 16:04:46 2008 -0800
@@ -31,6 +31,20 @@
added 1 changesets with 2 changes to 2 files
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
applying ../tip.patch
+% import of plain diff should be ok with --no-commit
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../tip.patch
+diff -r 80971e65b431 a
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@
+ line 1
++line 2
% hg -R repo import
requesting all changes
adding changesets
--- a/tests/test-journal-exists Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-journal-exists Fri Jan 25 16:04:46 2008 -0800
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
hg init
echo a > a
--- a/tests/test-keyword Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-keyword Fri Jan 25 16:04:46 2008 -0800
@@ -3,6 +3,7 @@
cat <<EOF >> $HGRCPATH
[extensions]
hgext.keyword =
+hgext.mq =
[keyword]
* =
b = ignore
@@ -88,9 +89,19 @@
hg -v kwexpand
echo % compare changenodes in a c
cat a c
-echo % rollback and remove c
-hg rollback
-rm c
+
+echo % qimport
+hg qimport -r tip -n mqtest.diff
+echo % keywords should not be expanded in patch
+cat .hg/patches/mqtest.diff
+echo % qpop
+hg qpop
+echo % qgoto - should imply qpush
+hg qgoto mqtest.diff
+echo % cat
+cat c
+echo % qpop and move on
+hg qpop
echo % copy
hg cp a c
--- a/tests/test-keyword.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-keyword.out Fri Jan 25 16:04:46 2008 -0800
@@ -36,6 +36,10 @@
To force expansion after enabling it, or a configuration change, run
"hg kwexpand".
+Also, when committing with the record extension or using mq's qrecord, be aware
+that keywords cannot be updated. Again, run "hg kwexpand" on the files in
+question to update keyword expansions after all changes have been checked in.
+
Expansions spanning more than one line and incremental expansions,
like CVS' $Log$, are not supported. A keyword template map
"Log = {desc}" expands to the first line of the changeset description.
@@ -158,8 +162,31 @@
xxx $
$Id: c,v ba4426d1938e 1970/01/01 00:00:01 user $
tests for different changenodes
-% rollback and remove c
-rolling back last transaction
+% qimport
+% keywords should not be expanded in patch
+# HG changeset patch
+# User User Name <user@example.com>
+# Date 1 0
+# Node ID ba4426d1938ec9673e03ab274d88c44e24618f7f
+# Parent f782df5f9602483b4e51c31a12315f353bba380c
+cndiff
+
+diff -r f782df5f9602 -r ba4426d1938e c
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/c Thu Jan 01 00:00:01 1970 +0000
+@@ -0,0 +1,2 @@
++$Id$
++tests for different changenodes
+% qpop
+Patch queue now empty
+% qgoto - should imply qpush
+applying mqtest.diff
+Now at: mqtest.diff
+% cat
+$Id: c,v ba4426d1938e 1970/01/01 00:00:01 user $
+tests for different changenodes
+% qpop and move on
+Patch queue now empty
% copy
% kwfiles added
a
@@ -184,7 +211,7 @@
diff -r f782df5f9602 c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,3 @@
-+expand $Id: c,v 0ba462c0f077 1970/01/01 00:00:01 user $
++expand $Id$
+do not process $Id:
+xxx $
% rollback
@@ -266,7 +293,7 @@
added 1 changesets with 3 changes to 3 files
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
% incoming
-comparing with test-keyword/Test-a/../Test
+comparing with test-keyword/Test
searching for changes
changeset: 1:0729690beff6
tag: tip
--- a/tests/test-mq.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-mq.out Fri Jan 25 16:04:46 2008 -0800
@@ -310,7 +310,6 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
-(run 'hg update' to get a working copy)
Patch queue now empty
applying bar
Now at: bar
@@ -344,7 +343,6 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
-(run 'hg update' to get a working copy)
Patch queue now empty
applying bar
Now at: bar
@@ -419,7 +417,6 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
-(run 'hg update' to get a working copy)
changeset: 1:20cbbe65cff7
tag: tip
user: test
--- a/tests/test-no-symlinks.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-no-symlinks.out Fri Jan 25 16:04:46 2008 -0800
@@ -6,6 +6,7 @@
a
d/b
% bundle
+2 changesets found
pulling from ../symlinks.hg
requesting all changes
adding changesets
--- a/tests/test-non-interactive-wsgi.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-non-interactive-wsgi.out Fri Jan 25 16:04:46 2008 -0800
@@ -7,6 +7,6 @@
---- HEADERS
200 Script output follows
---- DATA
-[('content-type', 'text/html; charset=ascii')]
+[('Content-Type', 'text/html; charset=ascii')]
---- ERRORS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-paths Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,11 @@
+#!/bin/sh
+base=`pwd`
+hg init a
+hg clone a b
+cd a
+echo '[paths]' >> .hg/hgrc
+echo 'dupe = ../b' >> .hg/hgrc
+hg in dupe | sed "s!$base!<base>!g"
+cd ..
+hg -R a in dupe | sed "s!$base!<base>!g"
+true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-paths.out Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,5 @@
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+comparing with <base>/b
+no changes found
+comparing with <base>/b
+no changes found
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-qrecord Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+echo "[ui]" >> $HGRCPATH
+echo "interactive=true" >> $HGRCPATH
+echo "[extensions]" >> $HGRCPATH
+echo "record=" >> $HGRCPATH
+
+echo "% help (no mq, so no qrecord)"
+
+hg help qrecord
+
+echo "mq=" >> $HGRCPATH
+
+echo "% help (mq present)"
+
+hg help qrecord
+
+hg init a
+cd a
+
+echo % base commit
+
+cat > 1.txt <<EOF
+1
+2
+3
+4
+5
+EOF
+cat > 2.txt <<EOF
+a
+b
+c
+d
+e
+f
+EOF
+mkdir dir
+cat > dir/a.txt <<EOF
+hello world
+
+someone
+up
+there
+loves
+me
+EOF
+
+hg add 1.txt 2.txt dir/a.txt
+hg commit -d '0 0' -m 'initial checkin'
+
+echo % changing files
+
+sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
+sed -e 's/b/b b/' 2.txt > 2.txt.new
+sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
+
+mv -f 1.txt.new 1.txt
+mv -f 2.txt.new 2.txt
+mv -f dir/a.txt.new dir/a.txt
+
+echo % whole diff
+
+hg diff --nodates
+
+echo % qrecord a.patch
+
+hg qrecord -d '0 0' -m aaa a.patch <<EOF
+y
+y
+n
+y
+y
+n
+EOF
+
+echo
+echo % "after qrecord a.patch 'tip'"
+hg tip -p
+echo
+echo % "after qrecord a.patch 'diff'"
+hg diff --nodates
+
+echo % qrecord b.patch
+hg qrecord -d '0 0' -m bbb b.patch <<EOF
+y
+y
+y
+y
+EOF
+
+echo
+echo % "after qrecord b.patch 'tip'"
+hg tip -p
+echo
+echo % "after qrecord b.patch 'diff'"
+hg diff --nodates
+
+echo
+echo % --- end ---
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-qrecord.out Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,207 @@
+% help (no mq, so no qrecord)
+hg: unknown command 'qrecord'
+Mercurial Distributed SCM
+
+basic commands:
+
+ add add the specified files on the next commit
+ annotate show changeset information per file line
+ clone make a copy of an existing repository
+ commit commit the specified files or all outstanding changes
+ diff diff repository (or selected files)
+ export dump the header and diffs for one or more changesets
+ init create a new repository in the given directory
+ log show revision history of entire repository or files
+ merge merge working directory with another revision
+ parents show the parents of the working dir or revision
+ pull pull changes from the specified source
+ push push changes to the specified destination
+ remove remove the specified files on the next commit
+ serve export the repository via HTTP
+ status show changed files in the working directory
+ update update working directory
+
+use "hg help" for the full list of commands or "hg -v" for details
+% help (mq present)
+hg qrecord [OPTION]... PATCH [FILE]...
+
+interactively record a new patch
+
+ see 'hg help qnew' & 'hg help record' for more information and usage
+
+options:
+
+ -e --edit edit commit message
+ -g --git use git extended diff format
+ -I --include include names matching the given patterns
+ -X --exclude exclude names matching the given patterns
+ -m --message use <text> as commit message
+ -l --logfile read commit message from <file>
+ -U --currentuser add "From: <current user>" to patch
+ -u --user add "From: <given user>" to patch
+ -D --currentdate add "Date: <current date>" to patch
+ -d --date add "Date: <given date>" to patch
+
+use "hg -v help qrecord" to show global options
+% base commit
+% changing files
+% whole diff
+diff -r 1057167b20ef 1.txt
+--- a/1.txt
++++ b/1.txt
+@@ -1,5 +1,5 @@
+ 1
+-2
++2 2
+ 3
+-4
++4 4
+ 5
+diff -r 1057167b20ef 2.txt
+--- a/2.txt
++++ b/2.txt
+@@ -1,5 +1,5 @@
+ a
+-b
++b b
+ c
+ d
+ e
+diff -r 1057167b20ef dir/a.txt
+--- a/dir/a.txt
++++ b/dir/a.txt
+@@ -1,4 +1,4 @@
+-hello world
++hello world!
+
+ someone
+ up
+% qrecord a.patch
+diff --git a/1.txt b/1.txt
+2 hunks, 4 lines changed
+examine changes to '1.txt'? [Ynsfdaq?] @@ -1,3 +1,3 @@
+ 1
+-2
++2 2
+ 3
+record this change to '1.txt'? [Ynsfdaq?] @@ -3,3 +3,3 @@
+ 3
+-4
++4 4
+ 5
+record this change to '1.txt'? [Ynsfdaq?] diff --git a/2.txt b/2.txt
+1 hunks, 2 lines changed
+examine changes to '2.txt'? [Ynsfdaq?] @@ -1,5 +1,5 @@
+ a
+-b
++b b
+ c
+ d
+ e
+record this change to '2.txt'? [Ynsfdaq?] diff --git a/dir/a.txt b/dir/a.txt
+1 hunks, 2 lines changed
+examine changes to 'dir/a.txt'? [Ynsfdaq?]
+% after qrecord a.patch 'tip'
+changeset: 1:5d1ca63427ee
+tag: qtip
+tag: tip
+tag: a.patch
+tag: qbase
+user: test
+date: Thu Jan 01 00:00:00 1970 +0000
+summary: aaa
+
+diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
+--- a/1.txt Thu Jan 01 00:00:00 1970 +0000
++++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
+@@ -1,5 +1,5 @@
+ 1
+-2
++2 2
+ 3
+ 4
+ 5
+diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
+--- a/2.txt Thu Jan 01 00:00:00 1970 +0000
++++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
+@@ -1,5 +1,5 @@
+ a
+-b
++b b
+ c
+ d
+ e
+
+
+% after qrecord a.patch 'diff'
+diff -r 5d1ca63427ee 1.txt
+--- a/1.txt
++++ b/1.txt
+@@ -1,5 +1,5 @@
+ 1
+ 2 2
+ 3
+-4
++4 4
+ 5
+diff -r 5d1ca63427ee dir/a.txt
+--- a/dir/a.txt
++++ b/dir/a.txt
+@@ -1,4 +1,4 @@
+-hello world
++hello world!
+
+ someone
+ up
+% qrecord b.patch
+diff --git a/1.txt b/1.txt
+1 hunks, 2 lines changed
+examine changes to '1.txt'? [Ynsfdaq?] @@ -1,5 +1,5 @@
+ 1
+ 2 2
+ 3
+-4
++4 4
+ 5
+record this change to '1.txt'? [Ynsfdaq?] diff --git a/dir/a.txt b/dir/a.txt
+1 hunks, 2 lines changed
+examine changes to 'dir/a.txt'? [Ynsfdaq?] @@ -1,4 +1,4 @@
+-hello world
++hello world!
+
+ someone
+ up
+record this change to 'dir/a.txt'? [Ynsfdaq?]
+% after qrecord b.patch 'tip'
+changeset: 2:b056198bf878
+tag: qtip
+tag: tip
+tag: b.patch
+user: test
+date: Thu Jan 01 00:00:00 1970 +0000
+summary: bbb
+
+diff -r 5d1ca63427ee -r b056198bf878 1.txt
+--- a/1.txt Thu Jan 01 00:00:00 1970 +0000
++++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
+@@ -1,5 +1,5 @@
+ 1
+ 2 2
+ 3
+-4
++4 4
+ 5
+diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
+--- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
++++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
+@@ -1,4 +1,4 @@
+-hello world
++hello world!
+
+ someone
+ up
+
+
+% after qrecord b.patch 'diff'
+
+% --- end ---
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-strip-cross Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+# test stripping of filelogs where the linkrev doesn't always increase
+
+echo '[extensions]' >> $HGRCPATH
+echo 'hgext.mq =' >> $HGRCPATH
+
+hg init orig
+cd orig
+
+hidefilename()
+{
+ sed -e 's/saving bundle to .*strip-backup/saving bundle to strip-backup/'
+}
+
+commit()
+{
+ hg up -qC null
+ count=1
+ for i in "$@"; do
+ for f in $i; do
+ echo $count > $f
+ done
+ count=`expr $count + 1`
+ done
+ hg commit -qAm "$*"
+}
+
+# 2 1 0 2 0 1 2
+commit '201 210'
+
+commit '102 120' '210'
+
+commit '021'
+
+commit '201' '021 120'
+
+commit '012 021' '102 201' '120 210'
+
+commit 'manifest-file'
+
+commit '102 120' '012 210' '021 201'
+
+commit '201 210' '021 120' '012 102'
+
+HGUSER=another-user; export HGUSER
+commit 'manifest-file'
+
+commit '012' 'manifest-file'
+
+cd ..
+hg clone -q -U -r -1 -r -2 -r -3 -r -4 -r -6 orig crossed
+
+for i in crossed/.hg/store/00manifest.i crossed/.hg/store/data/*.i; do
+ echo $i
+ hg debugindex $i
+ echo
+done
+
+for i in 0 1 2 3 4; do
+ hg clone -q -U --pull crossed $i
+ echo "% Trying to strip revision $i"
+ hg --cwd $i strip $i 2>&1 | hidefilename
+ echo "% Verifying"
+ hg --cwd $i verify
+ echo
+done
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-strip-cross.out Fri Jan 25 16:04:46 2008 -0800
@@ -0,0 +1,118 @@
+crossed/.hg/store/00manifest.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 112 0 0 6f105cbb914d 000000000000 000000000000
+ 1 112 56 1 3 1b55917b3699 000000000000 000000000000
+ 2 168 123 1 1 8f3d04e263e5 000000000000 000000000000
+ 3 291 122 1 2 f0ef8726ac4f 000000000000 000000000000
+ 4 413 87 4 4 0b76e38b4070 000000000000 000000000000
+
+crossed/.hg/store/data/012.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 3 0 0 b8e02f643373 000000000000 000000000000
+ 1 3 3 1 1 5d9299349fc0 000000000000 000000000000
+ 2 6 3 2 2 2661d26c6496 000000000000 000000000000
+
+crossed/.hg/store/data/021.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 3 0 0 b8e02f643373 000000000000 000000000000
+ 1 3 3 1 2 5d9299349fc0 000000000000 000000000000
+ 2 6 3 2 1 2661d26c6496 000000000000 000000000000
+
+crossed/.hg/store/data/102.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 3 0 1 b8e02f643373 000000000000 000000000000
+ 1 3 3 1 0 5d9299349fc0 000000000000 000000000000
+ 2 6 3 2 2 2661d26c6496 000000000000 000000000000
+
+crossed/.hg/store/data/120.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 3 0 1 b8e02f643373 000000000000 000000000000
+ 1 3 3 1 2 5d9299349fc0 000000000000 000000000000
+ 2 6 3 2 0 2661d26c6496 000000000000 000000000000
+
+crossed/.hg/store/data/201.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 3 0 2 b8e02f643373 000000000000 000000000000
+ 1 3 3 1 0 5d9299349fc0 000000000000 000000000000
+ 2 6 3 2 1 2661d26c6496 000000000000 000000000000
+
+crossed/.hg/store/data/210.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 3 0 2 b8e02f643373 000000000000 000000000000
+ 1 3 3 1 1 5d9299349fc0 000000000000 000000000000
+ 2 6 3 2 0 2661d26c6496 000000000000 000000000000
+
+crossed/.hg/store/data/manifest-file.i
+ rev offset length base linkrev nodeid p1 p2
+ 0 0 3 0 3 b8e02f643373 000000000000 000000000000
+ 1 3 3 1 4 5d9299349fc0 000000000000 000000000000
+
+% Trying to strip revision 0
+saving bundle to strip-backup/cbb8c2f0a2e3-backup
+saving bundle to strip-backup/cbb8c2f0a2e3-temp
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 4 changesets with 15 changes to 7 files (+3 heads)
+% Verifying
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+7 files, 4 changesets, 15 total revisions
+
+% Trying to strip revision 1
+saving bundle to strip-backup/124ecc0cbec9-backup
+saving bundle to strip-backup/124ecc0cbec9-temp
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 3 changesets with 12 changes to 7 files (+3 heads)
+% Verifying
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+7 files, 4 changesets, 14 total revisions
+
+% Trying to strip revision 2
+saving bundle to strip-backup/f6439b304a1a-backup
+saving bundle to strip-backup/f6439b304a1a-temp
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 2 changesets with 8 changes to 6 files (+2 heads)
+% Verifying
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+7 files, 4 changesets, 14 total revisions
+
+% Trying to strip revision 3
+saving bundle to strip-backup/6e54ec5db740-backup
+saving bundle to strip-backup/6e54ec5db740-temp
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 2 files (+1 heads)
+% Verifying
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+7 files, 4 changesets, 19 total revisions
+
+% Trying to strip revision 4
+saving bundle to strip-backup/9147ea23c156-backup
+% Verifying
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+7 files, 4 changesets, 19 total revisions
+
--- a/tests/test-webraw.out Fri Jan 25 16:04:32 2008 -0800
+++ b/tests/test-webraw.out Fri Jan 25 16:04:46 2008 -0800
@@ -1,7 +1,7 @@
200 Script output follows
content-type: text/plain
content-length: 157
-content-disposition: filename=sometext.txt
+content-disposition: inline; filename=sometext.txt
This is just some random text
that will go inside the file and take a few lines.