--- a/contrib/bash_completion Fri Nov 04 11:51:01 2005 -0800
+++ b/contrib/bash_completion Sun Dec 11 15:38:42 2005 -0800
@@ -2,18 +2,25 @@
_hg_commands()
{
- local commands="$(hg -v help | sed -e '1,/^list of commands:/d' \
- -e '/^global options:/,$d' \
- -e '/^ [^ ]/!d; s/[,:]//g;')"
+ local all commands result
+
+ all=($(hg --debug help | sed -e '1,/^list of commands:/d' \
+ -e '/^global options:/,$d' \
+ -e '/^ [^ ]/!d; s/^ //; s/[,:]//g;'))
+
+ commands="${all[*]##debug*}"
+ result=$(compgen -W "${commands[*]}" -- "$cur")
# hide debug commands from users, but complete them if
- # specifically asked for
- if [[ "$cur" == de* ]]; then
- commands="$commands debugcheckstate debugstate debugindex"
- commands="$commands debugindexdot debugwalk debugdata"
- commands="$commands debugancestor debugconfig debugrename"
+ # there is no other possible command
+ if [ "$result" = "" ]; then
+ local debug
+ debug=(${all[*]##!(debug*)})
+ debug="${debug[*]/g/debug}"
+ result=$(compgen -W "$debug" -- "$cur")
fi
- COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "$commands" -- "$cur") )
+
+ COMPREPLY=(${COMPREPLY[@]:-} $result)
}
_hg_paths()
@@ -161,7 +168,7 @@
fi
;;
*)
- COMPREPLY=(${COMPREPLY[@]:-} $( compgen -f -- "$cur" ))
+ COMPREPLY=(${COMPREPLY[@]:-} $( compgen -f -- "$cur" ))
;;
esac
--- a/contrib/hbisect.py Fri Nov 04 11:51:01 2005 -0800
+++ b/contrib/hbisect.py Sun Dec 11 15:38:42 2005 -0800
@@ -26,7 +26,7 @@
ui.warn("Repository is not clean, please commit or revert\n")
sys.exit(1)
-class bisect:
+class bisect(object):
"""dichotomic search in the DAG of changesets"""
def __init__(self, ui, repo):
self.repo = repo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/hg-ssh Sun Dec 11 15:38:42 2005 -0800
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 by Intevation GmbH <intevation@intevation.de>
+# Author(s):
+# Thomas Arendsen Hein <thomas@intevation.de>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+"""
+hg-ssh - a wrapper for ssh access to a limited set of mercurial repos
+
+To be used in ~/.ssh/authorized_keys with the "command" option, see sshd(8):
+command="hg-ssh path/to/repo1 /path/to/repo2 ~/repo3 ~user/repo4" ssh-dss ...
+(probably together with these other useful options:
+ no-port-forwarding,no-X11-forwarding,no-agent-forwarding)
+
+This allows pull/push over ssh to to the repositories given as arguments.
+
+If all your repositories are subdirectories of a common directory, you can
+allow shorter paths with:
+command="cd path/to/my/repositories && hg-ssh repo1 subdir/repo2"
+"""
+
+from mercurial import commands
+
+import sys, os
+
+cwd = os.getcwd()
+allowed_paths = [os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
+ for path in sys.argv[1:]]
+orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?')
+
+if orig_cmd.startswith('hg -R ') and orig_cmd.endswith(' serve --stdio'):
+ path = orig_cmd[6:-14]
+ repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
+ if repo in allowed_paths:
+ commands.dispatch(['-R', repo, 'serve', '--stdio'])
+ else:
+ sys.stderr.write("Illegal repository %r\n" % repo)
+ sys.exit(-1)
+else:
+ sys.stderr.write("Illegal command %r\n" % orig_cmd)
+ sys.exit(-1)
+
--- a/contrib/zsh_completion Fri Nov 04 11:51:01 2005 -0800
+++ b/contrib/zsh_completion Sun Dec 11 15:38:42 2005 -0800
@@ -116,7 +116,7 @@
'*:file:_files'
;;
- (status)
+ (status|st)
_arguments $includeExclude \
'(--no-status)-n[hide status prefix]' \
'(-n)--no-status[hide status prefix]' \
--- a/doc/hg.1.txt Fri Nov 04 11:51:01 2005 -0800
+++ b/doc/hg.1.txt Sun Dec 11 15:38:42 2005 -0800
@@ -87,7 +87,7 @@
New files are ignored if they match any of the patterns in .hgignore. As
with add, these changes take effect at the next commit.
-annotate [-r <rev> -u -n -c] [files ...]::
+annotate [-r <rev> -u -n -c -d] [files ...]::
List changes in files, showing the revision id responsible for each line
This command is useful to discover who did a change or when a change took
@@ -103,6 +103,7 @@
-X, --exclude <pat> exclude names matching the given patterns
-r, --revision <rev> annotate the specified revision
-u, --user list the author
+ -d, --date list the commit date
-c, --changeset list the changeset
-n, --number list the revision number (default)
--- a/mercurial/bdiff.c Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/bdiff.c Sun Dec 11 15:38:42 2005 -0800
@@ -147,7 +147,7 @@
break;
a[i].e = j; /* use equivalence class for quick compare */
- if(h[j].len <= t)
+ if (h[j].len <= t)
a[i].n = h[j].pos; /* point to head of match list */
else
a[i].n = -1; /* too popular */
@@ -270,7 +270,7 @@
if (!l.head || !rl)
goto nomem;
- for(h = l.base; h != l.head; h++) {
+ for (h = l.base; h != l.head; h++) {
m = Py_BuildValue("iiii", h->a1, h->a2, h->b1, h->b2);
PyList_SetItem(rl, pos, m);
pos++;
@@ -305,7 +305,7 @@
goto nomem;
/* calculate length of output */
- for(h = l.base; h != l.head; h++) {
+ for (h = l.base; h != l.head; h++) {
if (h->a1 != la || h->b1 != lb)
len += 12 + bl[h->b1].l - bl[lb].l;
la = h->a2;
@@ -320,7 +320,7 @@
rb = PyString_AsString(result);
la = lb = 0;
- for(h = l.base; h != l.head; h++) {
+ for (h = l.base; h != l.head; h++) {
if (h->a1 != la || h->b1 != lb) {
len = bl[h->b1].l - bl[lb].l;
*(uint32_t *)(encode) = htonl(al[la].l - al->l);
@@ -353,3 +353,4 @@
{
Py_InitModule3("bdiff", methods, mdiff_doc);
}
+
--- a/mercurial/commands.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/commands.py Sun Dec 11 15:38:42 2005 -0800
@@ -15,6 +15,8 @@
class UnknownCommand(Exception):
"""Exception raised if command is not in the command table."""
+class AmbiguousCommand(Exception):
+ """Exception raised if command shortcut matches more than one command."""
def filterfiles(filters, files):
l = [x for x in files if x in filters]
@@ -31,25 +33,29 @@
return [util.normpath(os.path.join(cwd, x)) for x in args]
return args
-def matchpats(repo, cwd, pats=[], opts={}, head=''):
+def matchpats(repo, pats=[], opts={}, head=''):
+ cwd = repo.getcwd()
+ if not pats and cwd:
+ opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
+ opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
+ cwd = ''
return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
- opts.get('exclude'), head)
+ opts.get('exclude'), head) + (cwd,)
-def makewalk(repo, pats, opts, head=''):
- cwd = repo.getcwd()
- files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
+def makewalk(repo, pats, opts, node=None, head=''):
+ files, matchfn, anypats, cwd = matchpats(repo, pats, opts, head)
exact = dict(zip(files, files))
def walk():
- for src, fn in repo.walk(files=files, match=matchfn):
+ for src, fn in repo.walk(node=node, files=files, match=matchfn):
yield src, fn, util.pathto(cwd, fn), fn in exact
return files, matchfn, walk()
-def walk(repo, pats, opts, head=''):
- files, matchfn, results = makewalk(repo, pats, opts, head)
+def walk(repo, pats, opts, node=None, head=''):
+ files, matchfn, results = makewalk(repo, pats, opts, node, head)
for r in results:
yield r
-def walkchangerevs(ui, repo, cwd, pats, opts):
+def walkchangerevs(ui, repo, pats, opts):
'''Iterate over files and the revs they changed in.
Callers most commonly need to iterate backwards over the history
@@ -79,12 +85,7 @@
if repo.changelog.count() == 0:
return [], False
- cwd = repo.getcwd()
- if not pats and cwd:
- opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
- opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
- files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
- pats, opts)
+ files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
wanted = {}
slowpath = anypats
@@ -387,7 +388,7 @@
if with_version:
show_version(ui)
ui.write('\n')
- key, i = find(cmd)
+ aliases, i = find(cmd)
# synopsis
ui.write("%s\n\n" % i[2])
@@ -399,9 +400,8 @@
if not ui.quiet:
# aliases
- aliases = ', '.join(key.split('|')[1:])
- if aliases:
- ui.write(_("\naliases: %s\n") % aliases)
+ if len(aliases) > 1:
+ ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
# options
if i[1]:
@@ -482,8 +482,7 @@
The files will be added to the repository at the next commit.
- If no names are given, add all files in the current directory and
- its subdirectories.
+ If no names are given, add all files in the repository.
"""
names = []
@@ -537,11 +536,20 @@
cl = repo.changelog.read(repo.changelog.node(rev))
return trimuser(ui, cl[1], rev, ucache)
+ dcache = {}
+ def getdate(rev):
+ datestr = dcache.get(rev)
+ if datestr is None:
+ cl = repo.changelog.read(repo.changelog.node(rev))
+ datestr = dcache[rev] = util.datestr(cl[2])
+ return datestr
+
if not pats:
raise util.Abort(_('at least one file name or pattern required'))
- opmap = [['user', getname], ['number', str], ['changeset', getnode]]
- if not opts['user'] and not opts['changeset']:
+ opmap = [['user', getname], ['number', str], ['changeset', getnode],
+ ['date', getdate]]
+ if not opts['user'] and not opts['changeset'] and not opts['date']:
opts['number'] = 1
if opts['rev']:
@@ -624,21 +632,16 @@
%p root-relative path name of file being printed
"""
mf = {}
- if opts['rev']:
- change = repo.changelog.read(repo.lookup(opts['rev']))
- mf = repo.manifest.read(change[0])
- for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
+ rev = opts['rev']
+ if rev:
+ node = repo.lookup(rev)
+ else:
+ node = repo.changelog.tip()
+ change = repo.changelog.read(node)
+ mf = repo.manifest.read(change[0])
+ for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
r = repo.file(abs)
- if opts['rev']:
- try:
- n = mf[abs]
- except (hg.RepoError, KeyError):
- try:
- n = r.lookup(rev)
- except KeyError, inst:
- raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
- else:
- n = r.tip()
+ n = mf[abs]
fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
fp.write(r.read(n))
@@ -667,7 +670,7 @@
dest = os.path.realpath(dest)
- class Dircleanup:
+ class Dircleanup(object):
def __init__(self, dir_):
self.rmtree = shutil.rmtree
self.dir_ = dir_
@@ -735,6 +738,7 @@
f = repo.opener("hgrc", "w", text=True)
f.write("[paths]\n")
f.write("default = %s\n" % abspath)
+ f.close()
if not opts['noupdate']:
update(ui, repo)
@@ -747,7 +751,7 @@
Commit changes to the given files into the repository.
If a list of files is omitted, all changes reported by "hg status"
- from the root of the repository will be commited.
+ will be commited.
The HGEDITOR or EDITOR environment variables are used to start an
editor to add a commit comment.
@@ -770,12 +774,7 @@
if opts['addremove']:
addremove(ui, repo, *pats, **opts)
- cwd = repo.getcwd()
- if not pats and cwd:
- opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
- opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
- fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
- pats, opts)
+ fns, match, anypats, cwd = matchpats(repo, pats, opts)
if pats:
c, a, d, u = repo.changes(files=fns, match=match)
files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
@@ -787,14 +786,10 @@
raise util.Abort(str(inst))
def docopy(ui, repo, pats, opts):
- if not pats:
- raise util.Abort(_('no source or destination specified'))
- elif len(pats) == 1:
- raise util.Abort(_('no destination specified'))
- pats = list(pats)
- dest = pats.pop()
- sources = []
- dir2dir = len(pats) == 1 and os.path.isdir(pats[0])
+ cwd = repo.getcwd()
+ errors = 0
+ copied = []
+ targets = {}
def okaytocopy(abs, rel, exact):
reasons = {'?': _('is not managed'),
@@ -805,74 +800,133 @@
else:
return True
- for src, abs, rel, exact in walk(repo, pats, opts):
- if okaytocopy(abs, rel, exact):
- sources.append((abs, rel, exact))
- if not sources:
- raise util.Abort(_('no files to copy'))
-
- cwd = repo.getcwd()
- absdest = util.canonpath(repo.root, cwd, dest)
- reldest = util.pathto(cwd, absdest)
- if os.path.exists(reldest):
- destisfile = not os.path.isdir(reldest)
- else:
- destisfile = not dir2dir and (len(sources) == 1
- or repo.dirstate.state(absdest) != '?')
-
- if destisfile and len(sources) > 1:
- raise util.Abort(_('with multiple sources, destination must be a '
- 'directory'))
-
- srcpfxlen = 0
- if dir2dir:
- srcpfx = util.pathto(cwd, util.canonpath(repo.root, cwd, pats[0]))
- if os.path.exists(reldest):
- srcpfx = os.path.split(srcpfx)[0]
- if srcpfx:
- srcpfx += os.sep
- srcpfxlen = len(srcpfx)
-
- errs, copied = 0, []
- for abs, rel, exact in sources:
- if destisfile:
- mydest = reldest
- elif dir2dir:
- mydest = os.path.join(dest, rel[srcpfxlen:])
+ def copy(abssrc, relsrc, target, exact):
+ abstarget = util.canonpath(repo.root, cwd, target)
+ reltarget = util.pathto(cwd, abstarget)
+ prevsrc = targets.get(abstarget)
+ if prevsrc is not None:
+ ui.warn(_('%s: not overwriting - %s collides with %s\n') %
+ (reltarget, abssrc, prevsrc))
+ return
+ if (not opts['after'] and os.path.exists(reltarget) or
+ opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
+ if not opts['force']:
+ ui.warn(_('%s: not overwriting - file exists\n') %
+ reltarget)
+ return
+ if not opts['after']:
+ os.unlink(reltarget)
+ if opts['after']:
+ if not os.path.exists(reltarget):
+ return
else:
- mydest = os.path.join(dest, os.path.basename(rel))
- myabsdest = util.canonpath(repo.root, cwd, mydest)
- myreldest = util.pathto(cwd, myabsdest)
- if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
- ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
- continue
- mydestdir = os.path.dirname(myreldest) or '.'
- if not opts['after']:
+ targetdir = os.path.dirname(reltarget) or '.'
+ if not os.path.isdir(targetdir):
+ os.makedirs(targetdir)
try:
- if dir2dir: os.makedirs(mydestdir)
- elif not destisfile: os.mkdir(mydestdir)
- except OSError, inst:
- if inst.errno != errno.EEXIST: raise
- if ui.verbose or not exact:
- ui.status(_('copying %s to %s\n') % (rel, myreldest))
- if not opts['after']:
- try:
- shutil.copyfile(rel, myreldest)
- shutil.copymode(rel, myreldest)
+ shutil.copyfile(relsrc, reltarget)
+ shutil.copymode(relsrc, reltarget)
except shutil.Error, inst:
raise util.Abort(str(inst))
except IOError, inst:
if inst.errno == errno.ENOENT:
- ui.warn(_('%s: deleted in working copy\n') % rel)
+ ui.warn(_('%s: deleted in working copy\n') % relsrc)
else:
- ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
- errs += 1
- continue
- repo.copy(abs, myabsdest)
- copied.append((abs, rel, exact))
- if errs:
+ ui.warn(_('%s: cannot copy - %s\n') %
+ (relsrc, inst.strerror))
+ errors += 1
+ return
+ if ui.verbose or not exact:
+ ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
+ targets[abstarget] = abssrc
+ repo.copy(abssrc, abstarget)
+ copied.append((abssrc, relsrc, exact))
+
+ def targetpathfn(pat, dest, srcs):
+ if os.path.isdir(pat):
+ if pat.endswith(os.sep):
+ pat = pat[:-len(os.sep)]
+ if destdirexists:
+ striplen = len(os.path.split(pat)[0])
+ else:
+ striplen = len(pat)
+ if striplen:
+ striplen += len(os.sep)
+ res = lambda p: os.path.join(dest, p[striplen:])
+ elif destdirexists:
+ res = lambda p: os.path.join(dest, os.path.basename(p))
+ else:
+ res = lambda p: dest
+ return res
+
+ def targetpathafterfn(pat, dest, srcs):
+ if util.patkind(pat, None)[0]:
+ # a mercurial pattern
+ res = lambda p: os.path.join(dest, os.path.basename(p))
+ elif len(util.canonpath(repo.root, cwd, pat)) < len(srcs[0][0]):
+ # A directory. Either the target path contains the last
+ # component of the source path or it does not.
+ def evalpath(striplen):
+ score = 0
+ for s in srcs:
+ t = os.path.join(dest, s[1][striplen:])
+ if os.path.exists(t):
+ score += 1
+ return score
+
+ if pat.endswith(os.sep):
+ pat = pat[:-len(os.sep)]
+ striplen = len(pat) + len(os.sep)
+ if os.path.isdir(os.path.join(dest, os.path.split(pat)[1])):
+ score = evalpath(striplen)
+ striplen1 = len(os.path.split(pat)[0])
+ if striplen1:
+ striplen1 += len(os.sep)
+ if evalpath(striplen1) > score:
+ striplen = striplen1
+ res = lambda p: os.path.join(dest, p[striplen:])
+ else:
+ # a file
+ if destdirexists:
+ res = lambda p: os.path.join(dest, os.path.basename(p))
+ else:
+ res = lambda p: dest
+ return res
+
+
+ pats = list(pats)
+ if not pats:
+ raise util.Abort(_('no source or destination specified'))
+ if len(pats) == 1:
+ raise util.Abort(_('no destination specified'))
+ dest = pats.pop()
+ destdirexists = os.path.isdir(dest)
+ if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
+ raise util.Abort(_('with multiple sources, destination must be an '
+ 'existing directory'))
+ if opts['after']:
+ tfn = targetpathafterfn
+ else:
+ tfn = targetpathfn
+ copylist = []
+ for pat in pats:
+ srcs = []
+ for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
+ if okaytocopy(abssrc, relsrc, exact):
+ srcs.append((abssrc, relsrc, exact))
+ if not srcs:
+ continue
+ copylist.append((tfn(pat, dest, srcs), srcs))
+ if not copylist:
+ raise util.Abort(_('no files to copy'))
+
+ for targetpath, srcs in copylist:
+ for abssrc, relsrc, exact in srcs:
+ copy(abssrc, relsrc, targetpath(relsrc), exact)
+
+ if errors:
ui.warn(_('(consider using --after)\n'))
- return errs, copied
+ return errors, copied
def copy(ui, repo, *pats, **opts):
"""mark files as copied for the next commit
@@ -1007,7 +1061,7 @@
change = repo.changelog.read(n)
m = repo.manifest.read(change[0])
n = m[relpath(repo, [file])[0]]
- except hg.RepoError, KeyError:
+ except (hg.RepoError, KeyError):
n = r.lookup(rev)
else:
n = r.tip()
@@ -1030,7 +1084,7 @@
ui.write("%s\n" % line.rstrip())
def diff(ui, repo, *pats, **opts):
- """diff working directory (or selected files)
+ """diff repository (or selected files)
Show differences between revisions for the specified files.
@@ -1056,7 +1110,7 @@
if len(revs) > 2:
raise util.Abort(_("too many revisions to diff"))
- fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
+ fns, matchfn, anypats, cwd = matchpats(repo, pats, opts)
dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
text=opts['text'])
@@ -1177,7 +1231,7 @@
yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
begin = lend + 1
- class linestate:
+ class linestate(object):
def __init__(self, line, linenum, colstart, colend):
self.line = line
self.linenum = linenum
@@ -1227,7 +1281,7 @@
fstate = {}
skip = {}
- changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
+ changeiter, getchange = walkchangerevs(ui, repo, pats, opts)
count = 0
incrementing = False
for st, rev, fns in changeiter:
@@ -1275,11 +1329,14 @@
changesets. They are where development generally takes place and
are the usual targets for update and merge operations.
"""
- heads = repo.changelog.heads()
+ if opts['rev']:
+ heads = repo.heads(repo.lookup(opts['rev']))
+ else:
+ heads = repo.heads()
br = None
if opts['branches']:
br = repo.branchlookup(heads)
- for n in repo.changelog.heads():
+ for n in heads:
show_changeset(ui, repo, changenode=n, brinfo=br)
def identify(ui, repo):
@@ -1461,11 +1518,11 @@
Print the revision history of the specified files or the entire project.
By default this command outputs: changeset id and hash, tags,
- parents, user, date and time, and a summary for each commit. The
- -v switch adds some more detail, such as changed files, manifest
- hashes or message signatures.
+ non-trivial parents, user, date and time, and a summary for each
+ commit. When the -v/--verbose switch is used, the list of changed
+ files and full commit message is shown.
"""
- class dui:
+ class dui(object):
# Implement and delegate some ui protocol. Save hunks of
# output for later display in the desired order.
def __init__(self, ui):
@@ -1487,12 +1544,7 @@
self.write(*args)
def __getattr__(self, key):
return getattr(self.ui, key)
- cwd = repo.getcwd()
- if not pats and cwd:
- opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
- opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
- changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
- pats, opts)
+ changeiter, getchange = walkchangerevs(ui, repo, pats, opts)
for st, rev, fns in changeiter:
if st == 'window':
du = dui(ui)
@@ -1733,7 +1785,9 @@
This command tries to fix the repository status after an interrupted
operation. It should only be necessary when Mercurial suggests it.
"""
- repo.recover()
+ if repo.recover():
+ return repo.verify()
+ return False
def remove(ui, repo, pat, *pats, **opts):
"""remove the specified files on the next commit
@@ -1799,13 +1853,12 @@
If names are given, all files matching the names are reverted.
- If no names are given, all files in the current directory and
- its subdirectories are reverted.
+ If no arguments are given, all files in the repository are reverted.
"""
node = opts['rev'] and repo.lookup(opts['rev']) or \
repo.dirstate.parents()[0]
- files, choose, anypats = matchpats(repo, repo.getcwd(), pats, opts)
+ files, choose, anypats, cwd = matchpats(repo, pats, opts)
(c, a, d, u) = repo.changes(match=choose)
repo.forget(a)
repo.undelete(d)
@@ -1928,9 +1981,8 @@
def status(ui, repo, *pats, **opts):
"""show changed files in the working directory
- Show changed files in the working directory. If no names are
- given, all files are shown. Otherwise, only files matching the
- given names are shown.
+ Show changed files in the repository. If names are
+ given, only files that match are shown.
The codes used to show the status of files are:
M = modified
@@ -1939,8 +1991,7 @@
? = not tracked
"""
- cwd = repo.getcwd()
- files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
+ files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
(c, a, d, u) = [[util.pathto(cwd, x) for x in n]
for n in repo.changes(files=files, match=matchfn)]
@@ -1986,8 +2037,10 @@
else:
r = hex(repo.changelog.tip())
- if name.find(revrangesep) >= 0:
- raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
+ disallowed = (revrangesep, '\r', '\n')
+ for c in disallowed:
+ if name.find(c) >= 0:
+ raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
if opts['local']:
repo.opener("localtags", "a").write("%s %s\n" % (r, name))
@@ -2138,6 +2191,7 @@
[('r', 'rev', '', _('annotate the specified revision')),
('a', 'text', None, _('treat all files as text')),
('u', 'user', None, _('list the author')),
+ ('d', 'date', None, _('list the date')),
('n', 'number', None, _('list the revision number (default)')),
('c', 'changeset', None, _('list the changeset')),
('I', 'include', [], _('include names matching the given patterns')),
@@ -2223,8 +2277,9 @@
"hg grep [OPTION]... PATTERN [FILE]..."),
"heads":
(heads,
- [('b', 'branches', None, _('find branch info'))],
- _('hg heads [-b]')),
+ [('b', 'branches', None, _('find branch info')),
+ ('r', 'rev', "", _('show only heads which are descendants of rev'))],
+ _('hg heads [-b] [-r <rev>]')),
"help": (help_, [], _('hg help [COMMAND]')),
"identify|id": (identify, [], _('hg identify')),
"import|patch":
@@ -2374,17 +2429,21 @@
" debugindex debugindexdot paths")
def find(cmd):
- choice = []
+ """Return (aliases, command table entry) for command string."""
+ choice = None
for e in table.keys():
aliases = e.lstrip("^").split("|")
if cmd in aliases:
- return e, table[e]
+ return aliases, table[e]
for a in aliases:
if a.startswith(cmd):
- choice.append(e)
- if len(choice) == 1:
- e = choice[0]
- return e, table[e]
+ if choice:
+ raise AmbiguousCommand(cmd)
+ else:
+ choice = aliases, table[e]
+ break
+ if choice:
+ return choice
raise UnknownCommand(cmd)
@@ -2411,18 +2470,11 @@
if args:
cmd, args = args[0], args[1:]
+ aliases, i = find(cmd)
+ cmd = aliases[0]
defaults = ui.config("defaults", cmd)
if defaults:
- # reparse with command defaults added
- args = [cmd] + defaults.split() + args
- try:
- args = fancyopts.fancyopts(args, globalopts, options)
- except fancyopts.getopt.GetoptError, inst:
- raise ParseError(None, inst)
-
- cmd, args = args[0], args[1:]
-
- i = find(cmd)[1]
+ args = defaults.split() + args
c = list(i[1])
else:
cmd = None
@@ -2460,7 +2512,7 @@
external = []
for x in u.extensions():
- def on_exception(Exception, inst):
+ def on_exception(exc, inst):
u.warn(_("*** failed to import extension %s\n") % x[1])
u.warn("%s\n" % inst)
if "--traceback" in sys.argv[1:]:
@@ -2502,6 +2554,9 @@
u.warn(_("hg: %s\n") % inst.args[1])
help_(u, 'shortlist')
sys.exit(-1)
+ except AmbiguousCommand, inst:
+ u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
+ sys.exit(1)
except UnknownCommand, inst:
u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
help_(u, 'shortlist')
@@ -2620,6 +2675,9 @@
u.debug(inst, "\n")
u.warn(_("%s: invalid arguments\n") % cmd)
help_(u, cmd)
+ except AmbiguousCommand, inst:
+ u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
+ help_(u, 'shortlist')
except UnknownCommand, inst:
u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
help_(u, 'shortlist')
@@ -2629,6 +2687,8 @@
except:
u.warn(_("** unknown exception encountered, details follow\n"))
u.warn(_("** report bug details to mercurial@selenic.com\n"))
+ u.warn(_("** Mercurial Distributed SCM (version %s)\n")
+ % version.get_version())
raise
sys.exit(-1)
--- a/mercurial/dirstate.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/dirstate.py Sun Dec 11 15:38:42 2005 -0800
@@ -13,7 +13,7 @@
from demandload import *
demandload(globals(), "time bisect stat util re errno")
-class dirstate:
+class dirstate(object):
def __init__(self, opener, ui, root):
self.opener = opener
self.root = root
@@ -101,16 +101,15 @@
try:
return self.map[key]
except TypeError:
- self.read()
+ self.lazyread()
return self[key]
def __contains__(self, key):
- if not self.map: self.read()
+ self.lazyread()
return key in self.map
def parents(self):
- if not self.pl:
- self.read()
+ self.lazyread()
return self.pl
def markdirty(self):
@@ -118,8 +117,7 @@
self.dirty = 1
def setparents(self, p1, p2=nullid):
- if not self.pl:
- self.read()
+ self.lazyread()
self.markdirty()
self.pl = p1, p2
@@ -129,9 +127,11 @@
except KeyError:
return "?"
+ def lazyread(self):
+ if self.map is None:
+ self.read()
+
def read(self):
- if self.map is not None: return self.map
-
self.map = {}
self.pl = [nullid, nullid]
try:
@@ -154,7 +154,7 @@
pos += l
def copy(self, source, dest):
- self.read()
+ self.lazyread()
self.markdirty()
self.copies[dest] = source
@@ -169,13 +169,13 @@
a marked for addition'''
if not files: return
- self.read()
+ self.lazyread()
self.markdirty()
for f in files:
if state == "r":
self.map[f] = ('r', 0, 0, 0)
else:
- s = os.lstat(os.path.join(self.root, f))
+ s = os.lstat(self.wjoin(f))
st_size = kw.get('st_size', s.st_size)
st_mtime = kw.get('st_mtime', s.st_mtime)
self.map[f] = (state, s.st_mode, st_size, st_mtime)
@@ -184,7 +184,7 @@
def forget(self, files):
if not files: return
- self.read()
+ self.lazyread()
self.markdirty()
for f in files:
try:
@@ -198,7 +198,7 @@
self.markdirty()
def write(self):
- st = self.opener("dirstate", "w")
+ st = self.opener("dirstate", "w", atomic=True)
st.write("".join(self.pl))
for f, e in self.map.items():
c = self.copied(f)
@@ -213,7 +213,7 @@
unknown = []
for x in files:
- if x is '.':
+ if x == '.':
return self.map.copy()
if x not in self.map:
unknown.append(x)
@@ -241,7 +241,7 @@
bs += 1
return ret
- def supported_type(self, f, st, verbose=True):
+ def supported_type(self, f, st, verbose=False):
if stat.S_ISREG(st.st_mode):
return True
if verbose:
@@ -258,7 +258,7 @@
return False
def statwalk(self, files=None, match=util.always, dc=None):
- self.read()
+ self.lazyread()
# walk all files by default
if not files:
@@ -296,7 +296,6 @@
def walkhelper(self, files, statmatch, dc):
# recursion free walker, faster than os.walk.
def findfiles(s):
- retfiles = []
work = [s]
while work:
top = work.pop()
@@ -306,7 +305,7 @@
nd = util.normpath(top[len(self.root) + 1:])
if nd == '.': nd = ''
for f in names:
- np = os.path.join(nd, f)
+ np = util.pconvert(os.path.join(nd, f))
if seen(np):
continue
p = os.path.join(top, f)
@@ -317,12 +316,12 @@
if statmatch(ds, st):
work.append(p)
if statmatch(np, st) and np in dc:
- yield 'm', util.pconvert(np), st
+ yield 'm', np, st
elif statmatch(np, st):
if self.supported_type(np, st):
- yield 'f', util.pconvert(np), st
+ yield 'f', np, st
elif np in dc:
- yield 'm', util.pconvert(np), st
+ yield 'm', np, st
known = {'.hg': 1}
def seen(fn):
@@ -332,13 +331,20 @@
# step one, find all files that match our criteria
files.sort()
for ff in util.unique(files):
- f = os.path.join(self.root, ff)
+ f = self.wjoin(ff)
try:
st = os.lstat(f)
except OSError, inst:
- if ff not in dc: self.ui.warn('%s: %s\n' % (
- util.pathto(self.getcwd(), ff),
- inst.strerror))
+ nf = util.normpath(ff)
+ found = False
+ for fn in dc:
+ if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
+ found = True
+ break
+ if not found:
+ self.ui.warn('%s: %s\n' % (
+ util.pathto(self.getcwd(), ff),
+ inst.strerror))
continue
if stat.S_ISDIR(st.st_mode):
cmp1 = (lambda x, y: cmp(x[1], y[1]))
@@ -352,7 +358,7 @@
continue
self.blockignore = True
if statmatch(ff, st):
- if self.supported_type(ff, st):
+ if self.supported_type(ff, st, verbose=True):
yield 'f', ff, st
elif ff in dc:
yield 'm', ff, st
@@ -380,7 +386,7 @@
nonexistent = True
if not st:
try:
- f = os.path.join(self.root, fn)
+ f = self.wjoin(fn)
st = os.lstat(f)
except OSError, inst:
if inst.errno != errno.ENOENT:
--- a/mercurial/fancyopts.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/fancyopts.py Sun Dec 11 15:38:42 2005 -0800
@@ -1,10 +1,10 @@
import getopt
def fancyopts(args, options, state):
- long=[]
- short=''
- map={}
- dt={}
+ long = []
+ short = ''
+ map = {}
+ dt = {}
for s, l, d, c in options:
pl = l.replace('-', '_')
--- a/mercurial/filelog.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/filelog.py Sun Dec 11 15:38:42 2005 -0800
@@ -54,11 +54,11 @@
mt = ""
if meta:
mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
- text = "\1\n" + "".join(mt) + "\1\n" + text
+ text = "\1\n%s\1\n%s" % ("".join(mt), text)
return self.addrevision(text, transaction, link, p1, p2)
def renamed(self, node):
- if 0 and self.parents(node)[0] != nullid:
+ if 0 and self.parents(node)[0] != nullid: # XXX
return False
m = self.readmeta(node)
if m and m.has_key("copy"):
--- a/mercurial/hgweb.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/hgweb.py Sun Dec 11 15:38:42 2005 -0800
@@ -71,7 +71,7 @@
else:
return os.stat(hg_path).st_mtime
-class hgrequest:
+class hgrequest(object):
def __init__(self, inp=None, out=None, env=None):
self.inp = inp or sys.stdin
self.out = out or sys.stdout
@@ -104,7 +104,7 @@
headers.append(('Content-length', str(size)))
self.header(headers)
-class templater:
+class templater(object):
def __init__(self, mapfile, filters={}, defaults={}):
self.cache = {}
self.map = {}
@@ -165,7 +165,6 @@
common_filters = {
"escape": cgi.escape,
"strip": lambda x: x.strip(),
- "rstrip": lambda x: x.rstrip(),
"age": age,
"date": lambda x: util.datestr(x),
"addbreaks": nl2br,
@@ -176,7 +175,7 @@
"rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
}
-class hgweb:
+class hgweb(object):
def __init__(self, repo, name=None):
if type(repo) == type(""):
self.repo = hg.repository(ui.ui(), repo)
@@ -952,14 +951,8 @@
else:
return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
-def server(path, name, templates, address, port, use_ipv6=False,
- accesslog=sys.stdout, errorlog=sys.stderr):
- httpd = create_server(path, name, templates, address, port, use_ipv6,
- accesslog, errorlog)
- httpd.serve_forever()
-
# This is a stopgap
-class hgwebdir:
+class hgwebdir(object):
def __init__(self, config):
def cleannames(items):
return [(name.strip('/'), path) for name, path in items]
@@ -1000,7 +993,10 @@
.replace("//", "/"))
# update time with local timezone
- d = (get_mtime(path), util.makedate()[1])
+ try:
+ d = (get_mtime(path), util.makedate()[1])
+ except OSError:
+ continue
yield dict(contact=(get("ui", "username") or # preferred
get("web", "contact") or # deprecated
@@ -1017,7 +1013,12 @@
if virtual:
real = dict(self.repos).get(virtual)
if real:
- hgweb(real).run(req)
+ try:
+ hgweb(real).run(req)
+ except IOError, inst:
+ req.write(tmpl("error", error=inst.strerror))
+ except hg.RepoError, inst:
+ req.write(tmpl("error", error=str(inst)))
else:
req.write(tmpl("notfound", repo=virtual))
else:
--- a/mercurial/httprangereader.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/httprangereader.py Sun Dec 11 15:38:42 2005 -0800
@@ -7,7 +7,7 @@
import byterange, urllib2
-class httprangereader:
+class httprangereader(object):
def __init__(self, url):
self.url = url
self.pos = 0
--- a/mercurial/localrepo.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/localrepo.py Sun Dec 11 15:38:42 2005 -0800
@@ -12,7 +12,7 @@
from demandload import *
demandload(globals(), "re lock transaction tempfile stat mdiff errno")
-class localrepository:
+class localrepository(object):
def __init__(self, ui, path=None, create=0):
if not path:
p = os.getcwd()
@@ -43,7 +43,7 @@
self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
try:
- self.ui.readconfig(os.path.join(self.path, "hgrc"))
+ self.ui.readconfig(self.join("hgrc"))
except IOError: pass
def hook(self, name, **args):
@@ -225,18 +225,20 @@
lock = self.lock()
if os.path.exists(self.join("journal")):
self.ui.status(_("rolling back interrupted transaction\n"))
- return transaction.rollback(self.opener, self.join("journal"))
+ transaction.rollback(self.opener, self.join("journal"))
+ return True
else:
self.ui.warn(_("no interrupted transaction available\n"))
+ return False
def undo(self):
+ wlock = self.wlock()
lock = self.lock()
if os.path.exists(self.join("undo")):
self.ui.status(_("rolling back last transaction\n"))
transaction.rollback(self.opener, self.join("undo"))
- self.dirstate = None
util.rename(self.join("undo.dirstate"), self.join("dirstate"))
- self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
+ self.dirstate.read()
else:
self.ui.warn(_("no undo information available\n"))
@@ -249,6 +251,17 @@
return lock.lock(self.join("lock"), wait)
raise inst
+ def wlock(self, wait=1):
+ try:
+ wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
+ except lock.LockHeld, inst:
+ if not wait:
+ raise inst
+ self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
+ wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
+ self.dirstate.read()
+ return wlock
+
def rawcommit(self, files, text, user, date, p1=None, p2=None):
orig_parent = self.dirstate.parents()[0] or nullid
p1 = p1 or self.dirstate.parents()[0] or nullid
@@ -265,6 +278,8 @@
else:
update_dirstate = 0
+ wlock = self.wlock()
+ lock = self.lock()
tr = self.transaction()
mm = m1.copy()
mfm = mf1.copy()
@@ -353,6 +368,7 @@
if not self.hook("precommit"):
return None
+ wlock = self.wlock()
lock = self.lock()
tr = self.transaction()
@@ -446,8 +462,14 @@
def walk(self, node=None, files=[], match=util.always):
if node:
+ fdict = dict.fromkeys(files)
for fn in self.manifest.read(self.changelog.read(node)[0]):
- if match(fn): yield 'm', fn
+ fdict.pop(fn, None)
+ if match(fn):
+ yield 'm', fn
+ for fn in fdict:
+ self.ui.warn(_('%s: No such file in rev %s\n') % (
+ util.pathto(self.getcwd(), fn), short(node)))
else:
for src, fn in self.dirstate.walk(files, match):
yield src, fn
@@ -470,6 +492,10 @@
# are we comparing the working directory?
if not node2:
+ try:
+ wlock = self.wlock(wait=0)
+ except lock.LockHeld:
+ wlock = None
l, c, a, d, u = self.dirstate.changes(files, match)
# are we comparing working dir against its parent?
@@ -481,6 +507,8 @@
for f in l:
if fcmp(f, mf2):
c.append(f)
+ elif wlock is not None:
+ self.dirstate.update([f], "n")
for l in c, a, d, u:
l.sort()
@@ -524,6 +552,7 @@
return (c, a, d, u)
def add(self, list):
+ wlock = self.wlock()
for f in list:
p = self.wjoin(f)
if not os.path.exists(p):
@@ -536,6 +565,7 @@
self.dirstate.update([f], "a")
def forget(self, list):
+ wlock = self.wlock()
for f in list:
if self.dirstate.state(f) not in 'ai':
self.ui.warn(_("%s not added!\n") % f)
@@ -549,6 +579,7 @@
util.unlink(self.wjoin(f))
except OSError, inst:
if inst.errno != errno.ENOENT: raise
+ wlock = self.wlock()
for f in list:
p = self.wjoin(f)
if os.path.exists(p):
@@ -566,6 +597,7 @@
mn = self.changelog.read(p)[0]
mf = self.manifest.readflags(mn)
m = self.manifest.read(mn)
+ wlock = self.wlock()
for f in list:
if self.dirstate.state(f) not in "r":
self.ui.warn("%s not removed!\n" % f)
@@ -582,12 +614,17 @@
elif not os.path.isfile(p):
self.ui.warn(_("copy failed: %s is not a file\n") % dest)
else:
+ wlock = self.wlock()
if self.dirstate.state(dest) == '?':
self.dirstate.update([dest], "a")
self.dirstate.copy(source, dest)
- def heads(self):
- return self.changelog.heads()
+ def heads(self, start=None):
+ heads = self.changelog.heads(start)
+ # sort the output in rev descending order
+ heads = [(-self.changelog.rev(h), h) for h in heads]
+ heads.sort()
+ return [n for (r, n) in heads]
# branchlookup returns a dict giving a list of branches for
# each head. A branch is defined as the tag of a node or
@@ -1372,6 +1409,9 @@
mw[f] = ""
mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
+ if moddirstate:
+ wlock = self.wlock()
+
for f in d:
if f in mw: del mw[f]
--- a/mercurial/lock.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/lock.py Sun Dec 11 15:38:42 2005 -0800
@@ -11,11 +11,12 @@
class LockHeld(Exception):
pass
-class lock:
- def __init__(self, file, wait=1):
+class lock(object):
+ def __init__(self, file, wait=1, releasefn=None):
self.f = file
self.held = 0
self.wait = wait
+ self.releasefn = releasefn
self.lock()
def __del__(self):
@@ -43,6 +44,8 @@
def release(self):
if self.held:
self.held = 0
+ if self.releasefn:
+ self.releasefn()
try:
os.unlink(self.f)
except: pass
--- a/mercurial/manifest.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/manifest.py Sun Dec 11 15:38:42 2005 -0800
@@ -5,17 +5,16 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import sys, struct
+import struct
from revlog import *
from i18n import gettext as _
from demandload import *
-demandload(globals(), "bisect")
+demandload(globals(), "bisect array")
class manifest(revlog):
def __init__(self, opener):
self.mapcache = None
self.listcache = None
- self.addlist = None
revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
def read(self, node):
@@ -25,8 +24,9 @@
text = self.revision(node)
map = {}
flag = {}
- self.listcache = (text, text.splitlines(1))
- for l in self.listcache[1]:
+ self.listcache = array.array('c', text)
+ lines = text.splitlines(1)
+ for l in lines:
(f, n) = l.split('\0')
map[f] = bin(n[:40])
flag[f] = (n[40:-1] == "x")
@@ -39,57 +39,67 @@
self.read(node)
return self.mapcache[2]
+ def diff(self, a, b):
+ return mdiff.textdiff(str(a), str(b))
+
def add(self, map, flags, transaction, link, p1=None, p2=None,
changed=None):
- # directly generate the mdiff delta from the data collected during
- # the bisect loop below
- def gendelta(delta):
- i = 0
- result = []
- while i < len(delta):
- start = delta[i][2]
- end = delta[i][3]
- l = delta[i][4]
- if l == None:
- l = ""
- while i < len(delta) - 1 and start <= delta[i+1][2] \
- and end >= delta[i+1][2]:
- if delta[i+1][3] > end:
- end = delta[i+1][3]
- if delta[i+1][4]:
- l += delta[i+1][4]
+
+ # returns a tuple (start, end). If the string is found
+ # m[start:end] are the line containing that string. If start == end
+ # the string was not found and they indicate the proper sorted
+ # insertion point. This was taken from bisect_left, and modified
+ # to find line start/end as it goes along.
+ #
+ # m should be a buffer or a string
+ # s is a string
+ #
+ def manifestsearch(m, s, lo=0, hi=None):
+ def advance(i, c):
+ while i < lenm and m[i] != c:
i += 1
- result.append(struct.pack(">lll", start, end, len(l)) + l)
- i += 1
- return result
+ return i
+ lenm = len(m)
+ if not hi:
+ hi = lenm
+ while lo < hi:
+ mid = (lo + hi) // 2
+ start = mid
+ while start > 0 and m[start-1] != '\n':
+ start -= 1
+ end = advance(start, '\0')
+ if m[start:end] < s:
+ # we know that after the null there are 40 bytes of sha1
+ # this translates to the bisect lo = mid + 1
+ lo = advance(end + 40, '\n') + 1
+ else:
+ # this translates to the bisect hi = mid
+ hi = start
+ end = advance(lo, '\0')
+ found = m[lo:end]
+ if cmp(s, found) == 0:
+ # we know that after the null there are 40 bytes of sha1
+ end = advance(end + 40, '\n')
+ return (lo, end+1)
+ else:
+ return (lo, lo)
# apply the changes collected during the bisect loop to our addlist
- def addlistdelta(addlist, delta):
- # apply the deltas to the addlist. start from the bottom up
+ # return a delta suitable for addrevision
+ def addlistdelta(addlist, x):
+ # start from the bottom up
# so changes to the offsets don't mess things up.
- i = len(delta)
+ i = len(x)
while i > 0:
i -= 1
- start = delta[i][0]
- end = delta[i][1]
- if delta[i][4]:
- addlist[start:end] = [delta[i][4]]
+ start = x[i][0]
+ end = x[i][1]
+ if x[i][2]:
+ addlist[start:end] = array.array('c', x[i][2])
else:
del addlist[start:end]
- return addlist
-
- # calculate the byte offset of the start of each line in the
- # manifest
- def calcoffsets(addlist):
- offsets = [0] * (len(addlist) + 1)
- offset = 0
- i = 0
- while i < len(addlist):
- offsets[i] = offset
- offset += len(addlist[i])
- i += 1
- offsets[i] = offset
- return offsets
+ return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2] \
+ for d in x ])
# if we're using the listcache, make sure it is valid and
# parented by the same node we're diffing against
@@ -98,15 +108,13 @@
files = map.keys()
files.sort()
- self.addlist = ["%s\000%s%s\n" %
+ text = ["%s\000%s%s\n" %
(f, hex(map[f]), flags[f] and "x" or '')
for f in files]
+ self.listcache = array.array('c', "".join(text))
cachedelta = None
else:
- addlist = self.listcache[1]
-
- # find the starting offset for each line in the add list
- offsets = calcoffsets(addlist)
+ addlist = self.listcache
# combine the changed lists into one list for sorting
work = [[x, 0] for x in changed[0]]
@@ -114,45 +122,52 @@
work.sort()
delta = []
- bs = 0
+ dstart = None
+ dend = None
+ dline = [""]
+ start = 0
+ # zero copy representation of addlist as a buffer
+ addbuf = buffer(addlist)
+ # start with a readonly loop that finds the offset of
+ # each line and creates the deltas
for w in work:
f = w[0]
# bs will either be the index of the item or the insert point
- bs = bisect.bisect(addlist, f, bs)
- if bs < len(addlist):
- fn = addlist[bs][:addlist[bs].index('\0')]
- else:
- fn = None
+ start, end = manifestsearch(addbuf, f, start)
if w[1] == 0:
l = "%s\000%s%s\n" % (f, hex(map[f]),
flags[f] and "x" or '')
else:
- l = None
- start = bs
- if fn != f:
- # item not found, insert a new one
- end = bs
- if w[1] == 1:
- raise AssertionError(
+ l = ""
+ if start == end and w[1] == 1:
+ # item we want to delete was not found, error out
+ raise AssertionError(
_("failed to remove %s from manifest\n") % f)
+ if dstart != None and dstart <= start and dend >= start:
+ if dend < end:
+ dend = end
+ if l:
+ dline.append(l)
else:
- # item is found, replace/delete the existing line
- end = bs + 1
- delta.append([start, end, offsets[start], offsets[end], l])
+ if dstart != None:
+ delta.append([dstart, dend, "".join(dline)])
+ dstart = start
+ dend = end
+ dline = [l]
- self.addlist = addlistdelta(addlist, delta)
- if self.mapcache[0] == self.tip():
- cachedelta = "".join(gendelta(delta))
- else:
- cachedelta = None
+ if dstart != None:
+ delta.append([dstart, dend, "".join(dline)])
+ # apply the delta to the addlist, and get a delta for addrevision
+ cachedelta = addlistdelta(addlist, delta)
- text = "".join(self.addlist)
- if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
- raise AssertionError(_("manifest delta failure\n"))
- n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
+ # the delta is only valid if we've been processing the tip revision
+ if self.mapcache[0] != self.tip():
+ cachedelta = None
+ self.listcache = addlist
+
+ n = self.addrevision(buffer(self.listcache), transaction, link, p1, \
+ p2, cachedelta)
self.mapcache = (n, map, flags)
- self.listcache = (text, self.addlist)
- self.addlist = None
return n
--- a/mercurial/mdiff.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/mdiff.py Sun Dec 11 15:38:42 2005 -0800
@@ -32,8 +32,8 @@
l = list(difflib.unified_diff(a, b, "a/" + fn, "b/" + fn))
if not l: return ""
# difflib uses a space, rather than a tab
- l[0] = l[0][:-2] + "\t" + ad + "\n"
- l[1] = l[1][:-2] + "\t" + bd + "\n"
+ l[0] = "%s\t%s\n" % (l[0][:-2], ad)
+ l[1] = "%s\t%s\n" % (l[1][:-2], bd)
for ln in xrange(len(l)):
if l[ln][-1] != '\n':
--- a/mercurial/node.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/node.py Sun Dec 11 15:38:42 2005 -0800
@@ -7,7 +7,7 @@
of the GNU General Public License, incorporated herein by reference.
"""
-import sha, binascii
+import binascii
nullid = "\0" * 20
--- a/mercurial/remoterepo.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/remoterepo.py Sun Dec 11 15:38:42 2005 -0800
@@ -5,11 +5,11 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-class remoterepository:
+class remoterepository(object):
def local(self):
return False
-class remotelock:
+class remotelock(object):
def __init__(self, repo):
self.repo = repo
def release(self):
--- a/mercurial/revlog.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/revlog.py Sun Dec 11 15:38:42 2005 -0800
@@ -31,15 +31,15 @@
def compress(text):
""" generate a possibly-compressed representation of text """
- if not text: return text
+ if not text: return ("", text)
if len(text) < 44:
- if text[0] == '\0': return text
- return 'u' + text
+ if text[0] == '\0': return ("", text)
+ return ('u', text)
bin = zlib.compress(text)
if len(bin) > len(text):
- if text[0] == '\0': return text
- return 'u' + text
- return bin
+ if text[0] == '\0': return ("", text)
+ return ('u', text)
+ return ("", bin)
def decompress(bin):
""" decompress the given input """
@@ -52,7 +52,7 @@
indexformat = ">4l20s20s20s"
-class lazyparser:
+class lazyparser(object):
"""
this class avoids the need to parse the entirety of large indices
@@ -71,6 +71,9 @@
self.all = 0
self.revlog = revlog
+ def trunc(self, pos):
+ self.l = pos/self.s
+
def load(self, pos=None):
if self.all: return
if pos is not None:
@@ -91,7 +94,7 @@
self.map[e[6]] = i
i += 1
-class lazyindex:
+class lazyindex(object):
"""a lazy version of the index array"""
def __init__(self, parser):
self.p = parser
@@ -104,10 +107,14 @@
return self.p.index[pos]
def __getitem__(self, pos):
return self.p.index[pos] or self.load(pos)
+ def __delitem__(self, pos):
+ del self.p.index[pos]
def append(self, e):
self.p.index.append(e)
+ def trunc(self, pos):
+ self.p.trunc(pos)
-class lazymap:
+class lazymap(object):
"""a lazy version of the node map"""
def __init__(self, parser):
self.p = parser
@@ -140,10 +147,12 @@
raise KeyError("node " + hex(key))
def __setitem__(self, key, val):
self.p.map[key] = val
+ def __delitem__(self, key):
+ del self.p.map[key]
class RevlogError(Exception): pass
-class revlog:
+class revlog(object):
"""
the underlying revision storage object
@@ -400,25 +409,28 @@
assert heads
return (orderedout, roots, heads)
- def heads(self, stop=None):
- """return the list of all nodes that have no children"""
- p = {}
- h = []
- stoprev = 0
- if stop and stop in self.nodemap:
- stoprev = self.rev(stop)
+ def heads(self, start=None):
+ """return the list of all nodes that have no children
+
+ if start is specified, only heads that are descendants of
+ start will be returned
- for r in range(self.count() - 1, -1, -1):
+ """
+ if start is None:
+ start = nullid
+ reachable = {start: 1}
+ heads = {start: 1}
+ startrev = self.rev(start)
+
+ for r in xrange(startrev + 1, self.count()):
n = self.node(r)
- if n not in p:
- h.append(n)
- if n == stop:
- break
- if r < stoprev:
- break
for pn in self.parents(n):
- p[pn] = 1
- return h
+ if pn in reachable:
+ reachable[n] = 1
+ heads[n] = 1
+ if pn in heads:
+ del heads[pn]
+ return heads.keys()
def children(self, node):
"""find the children of a given node"""
@@ -543,14 +555,16 @@
end = self.end(t)
if not d:
prev = self.revision(self.tip())
- d = self.diff(prev, text)
+ d = self.diff(prev, str(text))
data = compress(d)
- dist = end - start + len(data)
+ l = len(data[1]) + len(data[0])
+ dist = end - start + l
# full versions are inserted when the needed deltas
# become comparable to the uncompressed text
if not n or dist > len(text) * 2:
data = compress(text)
+ l = len(data[1]) + len(data[0])
base = n
else:
base = self.base(t)
@@ -559,14 +573,17 @@
if t >= 0:
offset = self.end(t)
- e = (offset, len(data), base, link, p1, p2, node)
+ e = (offset, l, base, link, p1, p2, node)
self.index.append(e)
self.nodemap[node] = n
entry = struct.pack(indexformat, *e)
transaction.add(self.datafile, e[0])
- self.opener(self.datafile, "a").write(data)
+ f = self.opener(self.datafile, "a")
+ if data[0]:
+ f.write(data[0])
+ f.write(data[1])
transaction.add(self.indexfile, n * len(entry))
self.opener(self.indexfile, "a").write(entry)
@@ -784,6 +801,10 @@
continue
delta = chunk[80:]
+ for p in (p1, p2):
+ if not p in self.nodemap:
+ raise RevlogError(_("unknown parent %s") % short(p1))
+
if not chain:
# retrieve the parent revision of the delta chain
chain = p1
@@ -797,7 +818,8 @@
# current size.
if chain == prev:
- cdelta = compress(delta)
+ tempd = compress(delta)
+ cdelta = tempd[0] + tempd[1]
if chain != prev or (end - start + len(cdelta)) > measure * 2:
# flush our writes here so we can read it in revision
@@ -824,6 +846,36 @@
ifh.close()
return node
+ def strip(self, rev, minlink):
+ if self.count() == 0 or rev >= self.count():
+ return
+
+ # 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][3]:
+ rev += 1
+ if rev >= self.count():
+ return
+
+ # first truncate the files on disk
+ end = self.start(rev)
+ self.opener(self.datafile, "a").truncate(end)
+ end = rev * struct.calcsize(indexformat)
+ self.opener(self.indexfile, "a").truncate(end)
+
+ # then reset internal state in memory to forget those revisions
+ self.cache = None
+ for p in self.index[rev:]:
+ del self.nodemap[p[6]]
+ del self.index[rev:]
+
+ # truncating the lazyindex also truncates the lazymap.
+ if isinstance(self.index, lazyindex):
+ self.index.trunc(end)
+
+
def checksize(self):
expected = 0
if self.count():
--- a/mercurial/transaction.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/transaction.py Sun Dec 11 15:38:42 2005 -0800
@@ -12,10 +12,9 @@
# of the GNU General Public License, incorporated herein by reference.
import os
-import util
from i18n import gettext as _
-class transaction:
+class transaction(object):
def __init__(self, report, opener, journal, after=None):
self.journal = None
--- a/mercurial/ui.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/ui.py Sun Dec 11 15:38:42 2005 -0800
@@ -10,7 +10,7 @@
from demandload import *
demandload(globals(), "re socket sys util")
-class ui:
+class ui(object):
def __init__(self, verbose=False, debug=False, quiet=False,
interactive=True):
self.overlay = {}
--- a/mercurial/util.py Fri Nov 04 11:51:01 2005 -0800
+++ b/mercurial/util.py Sun Dec 11 15:38:42 2005 -0800
@@ -106,6 +106,13 @@
def always(fn): return True
def never(fn): return False
+def patkind(name, dflt_pat='glob'):
+ """Split a string into an optional pattern kind prefix and the
+ actual pattern."""
+ for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
+ if name.startswith(prefix + ':'): return name.split(':', 1)
+ return dflt_pat, name
+
def globre(pat, head='^', tail='$'):
"convert a glob pattern into a regexp"
i, n = 0, len(pat)
@@ -158,15 +165,20 @@
this returns a path in the form used by the local filesystem, not hg.'''
if not n1: return localpath(n2)
a, b = n1.split('/'), n2.split('/')
- a.reverse(), b.reverse()
+ a.reverse()
+ b.reverse()
while a and b and a[-1] == b[-1]:
- a.pop(), b.pop()
+ a.pop()
+ b.pop()
b.reverse()
return os.sep.join((['..'] * len(a)) + b)
def canonpath(root, cwd, myname):
"""return the canonical path of myname, given cwd and root"""
- rootsep = root + os.sep
+ if root == os.sep:
+ rootsep = os.sep
+ else:
+ rootsep = root + os.sep
name = myname
if not name.startswith(os.sep):
name = os.path.join(root, cwd, name)
@@ -218,11 +230,6 @@
make head regex a rooted bool
"""
- def patkind(name, dflt_pat='glob'):
- for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
- if name.startswith(prefix + ':'): return name.split(':', 1)
- return dflt_pat, name
-
def contains_glob(name):
for c in name:
if c in _globchars: return True
@@ -253,7 +260,7 @@
try:
pat = '(?:%s)' % regex(k, p, tail)
matches.append(re.compile(pat).match)
- except re.error, inst:
+ except re.error:
raise Abort("invalid pattern: %s:%s" % (k, p))
def buildfn(text):
@@ -362,7 +369,36 @@
remote file access from higher level code.
"""
p = base
- def o(path, mode="r", text=False):
+
+ def mktempcopy(name):
+ d, fn = os.path.split(name)
+ fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
+ fp = os.fdopen(fd, "wb")
+ try:
+ fp.write(file(name, "rb").read())
+ except:
+ try: os.unlink(temp)
+ except: pass
+ raise
+ fp.close()
+ st = os.lstat(name)
+ os.chmod(temp, st.st_mode)
+ return temp
+
+ class atomicfile(file):
+ """the file will only be copied on close"""
+ def __init__(self, name, mode, atomic=False):
+ self.__name = name
+ self.temp = mktempcopy(name)
+ file.__init__(self, self.temp, mode)
+ def close(self):
+ if not self.closed:
+ file.close(self)
+ rename(self.temp, self.__name)
+ def __del__(self):
+ self.close()
+
+ def o(path, mode="r", text=False, atomic=False):
f = os.path.join(p, path)
if not text:
@@ -376,19 +412,10 @@
if not os.path.isdir(d):
os.makedirs(d)
else:
+ if atomic:
+ return atomicfile(f, mode)
if nlink > 1:
- d, fn = os.path.split(f)
- fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
- fp = os.fdopen(fd, "wb")
- try:
- fp.write(file(f, "rb").read())
- except:
- try: os.unlink(temp)
- except: pass
- raise
- fp.close()
- rename(temp, f)
-
+ rename(mktempcopy(f), f)
return file(f, mode)
return o
@@ -484,6 +511,7 @@
nulldev = '/dev/null'
def rcfiles(path):
+ print 'checking', path
rcs = [os.path.join(path, 'hgrc')]
rcdir = os.path.join(path, 'hgrc.d')
try:
--- a/setup.py Fri Nov 04 11:51:01 2005 -0800
+++ b/setup.py Sun Dec 11 15:38:42 2005 -0800
@@ -72,8 +72,10 @@
try:
mercurial.version.remember_version(version)
cmdclass = {'install_data': install_package_data}
+ py2exe_opts = {}
if py2exe_for_demandload is not None:
cmdclass['py2exe'] = py2exe_for_demandload
+ py2exe_opts['console'] = ['hg']
setup(name='mercurial',
version=mercurial.version.get_version(),
author='Matt Mackall',
@@ -90,6 +92,6 @@
glob.glob('templates/*.tmpl'))],
cmdclass=cmdclass,
scripts=['hg', 'hgmerge'],
- console = ['hg'])
+ **py2exe_opts)
finally:
mercurial.version.forget_version()
--- a/templates/changelogentry-rss.tmpl Fri Nov 04 11:51:01 2005 -0800
+++ b/templates/changelogentry-rss.tmpl Sun Dec 11 15:38:42 2005 -0800
@@ -1,5 +1,5 @@
<item>
- <title>#desc|strip|firstline|rstrip|escape#</title>
+ <title>#desc|strip|firstline|strip|escape#</title>
<link>#url#?cs=#node|short#</link>
<description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
<author>#author|obfuscate#</author>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/error.tmpl Sun Dec 11 15:38:42 2005 -0800
@@ -0,0 +1,15 @@
+#header#
+<title>Mercurial Error</title>
+</head>
+<body>
+
+<h2>Mercurial Error</h2>
+
+<p>
+An error occured while processing your request:
+</p>
+<p>
+#error|escape#
+</p>
+
+#footer#
--- a/templates/filelogentry-rss.tmpl Fri Nov 04 11:51:01 2005 -0800
+++ b/templates/filelogentry-rss.tmpl Sun Dec 11 15:38:42 2005 -0800
@@ -1,5 +1,5 @@
<item>
- <title>#desc|strip|firstline|rstrip|escape#</title>
+ <title>#desc|strip|firstline|strip|escape#</title>
<link>#url#?f=#filenode|short#;file=#file#</link>
<description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
<author>#author|obfuscate#</author>
--- a/templates/map Fri Nov 04 11:51:01 2005 -0800
+++ b/templates/map Sun Dec 11 15:38:42 2005 -0800
@@ -39,3 +39,4 @@
index = index.tmpl
archiveentry = "<a href="?ca=#node|short#;type=#type#">#type#</a> "
notfound = notfound.tmpl
+error = error.tmpl
--- a/templates/notfound.tmpl Fri Nov 04 11:51:01 2005 -0800
+++ b/templates/notfound.tmpl Sun Dec 11 15:38:42 2005 -0800
@@ -5,7 +5,7 @@
<h2>Mercurial Repositories</h2>
-The specified repository "#repo#" is unknown, sorry.
+The specified repository "#repo|escape#" is unknown, sorry.
Please go back to the main repository list page.
--- a/templates/tags.tmpl Fri Nov 04 11:51:01 2005 -0800
+++ b/templates/tags.tmpl Sun Dec 11 15:38:42 2005 -0800
@@ -1,5 +1,5 @@
#header#
-<title>#repo#: tags</title>
+<title>#repo|escape#: tags</title>
</head>
<body>
--- a/tests/run-tests Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/run-tests Sun Dec 11 15:38:42 2005 -0800
@@ -40,16 +40,11 @@
}
TESTDIR="$PWD"
-
-if [ -d /usr/lib64 ]; then
- lib=lib64
-else
- lib=lib
-fi
-
INST="$HGTMP/install"
+PYTHONDIR="$INST/lib/python"
cd ..
-if ${PYTHON-python} setup.py install --home="$INST" > tests/install.err 2>&1
+if ${PYTHON-python} setup.py install --home="$INST" \
+ --install-lib="$PYTHONDIR" > tests/install.err 2>&1
then
rm tests/install.err
else
@@ -59,8 +54,7 @@
cd "$TESTDIR"
PATH="$INST/bin:$PATH"; export PATH
-PYTHONPATH="$INST/$lib/python"; export PYTHONPATH
-
+PYTHONPATH="$PYTHONDIR"; export PYTHONPATH
run_one() {
rm -f "$1.err"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-cat Sun Dec 11 15:38:42 2005 -0800
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+mkdir t
+cd t
+hg init
+echo 0 > a
+echo 0 > b
+hg ci -A -m m -d "0 0"
+hg rm a
+hg cat a
+sleep 1 # make sure mtime is changed
+echo 1 > b
+hg ci -m m -d "0 0"
+echo 2 > b
+hg cat -r 0 a
+hg cat -r 0 b
+hg cat -r 1 a
+hg cat -r 1 b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-cat.out Sun Dec 11 15:38:42 2005 -0800
@@ -0,0 +1,7 @@
+adding a
+adding b
+0
+0
+0
+a: No such file in rev 551e7cb14b32
+1
--- a/tests/test-grep Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-grep Sun Dec 11 15:38:42 2005 -0800
@@ -14,7 +14,7 @@
hg commit -m 2 -u spam -d '2 0'
echo 'import/export' >> port
hg commit -m 3 -u eggs -d '3 0'
-head -3 port > port1
+head -n 3 port > port1
mv port1 port
hg commit -m 4 -u spam -d '4 0'
hg grep port port
--- a/tests/test-help.out Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-help.out Sun Dec 11 15:38:42 2005 -0800
@@ -6,7 +6,7 @@
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 working directory (or selected files)
+ 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
@@ -22,7 +22,7 @@
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 working directory (or selected files)
+ 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
@@ -46,7 +46,7 @@
clone make a copy of an existing repository
commit commit the specified files or all outstanding changes
copy mark files as copied for the next commit
- diff diff working directory (or selected files)
+ diff diff repository (or selected files)
export dump the header and diffs for one or more changesets
forget don't add the specified files on the next commit
grep search for a pattern in specified files and revisions
@@ -88,7 +88,7 @@
clone make a copy of an existing repository
commit commit the specified files or all outstanding changes
copy mark files as copied for the next commit
- diff diff working directory (or selected files)
+ diff diff repository (or selected files)
export dump the header and diffs for one or more changesets
forget don't add the specified files on the next commit
grep search for a pattern in specified files and revisions
@@ -130,8 +130,7 @@
The files will be added to the repository at the next commit.
- If no names are given, add all files in the current directory and
- its subdirectories.
+ If no names are given, add all files in the repository.
options:
@@ -146,8 +145,7 @@
The files will be added to the repository at the next commit.
- If no names are given, add all files in the current directory and
- its subdirectories.
+ If no names are given, add all files in the repository.
options:
@@ -155,7 +153,7 @@
-X --exclude exclude names matching the given patterns
hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
-diff working directory (or selected files)
+diff repository (or selected files)
Show differences between revisions for the specified files.
@@ -181,9 +179,8 @@
show changed files in the working directory
- Show changed files in the working directory. If no names are
- given, all files are shown. Otherwise, only files matching the
- given names are shown.
+ Show changed files in the repository. If names are
+ given, only files that match are shown.
The codes used to show the status of files are:
M = modified
@@ -191,6 +188,8 @@
R = removed
? = not tracked
+aliases: st
+
options:
-m --modified show only modified files
@@ -213,7 +212,7 @@
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 working directory (or selected files)
+ 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
@@ -234,7 +233,7 @@
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 working directory (or selected files)
+ 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
--- a/tests/test-hgignore Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-hgignore Sun Dec 11 15:38:42 2005 -0800
@@ -42,4 +42,4 @@
echo "--" ; hg status
cd dir
-echo "--" ; hg status
+echo "--" ; hg status .
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rename Sun Dec 11 15:38:42 2005 -0800
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+hg init
+mkdir d1 d1/d11 d2
+echo d1/a > d1/a
+echo d1/ba > d1/ba
+echo d1/a1 > d1/d11/a1
+echo d1/b > d1/b
+echo d2/b > d2/b
+hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
+hg commit -m "1" -d "0 0"
+
+echo "# rename a single file"
+hg rename d1/d11/a1 d2/c
+hg status
+hg update -C
+
+echo "# rename --after a single file"
+mv d1/d11/a1 d2/c
+hg rename --after d1/d11/a1 d2/c
+hg status
+hg update -C
+
+echo "# move a single file to an existing directory"
+hg rename d1/d11/a1 d2
+hg status
+hg update -C
+
+echo "# move --after a single file to an existing directory"
+mv d1/d11/a1 d2
+hg rename --after d1/d11/a1 d2
+hg status
+hg update -C
+
+echo "# rename a file using a relative path"
+(cd d1/d11; hg rename ../../d2/b e)
+hg status
+hg update -C
+
+echo "# rename --after a file using a relative path"
+(cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
+hg status
+hg update -C
+
+echo "# rename directory d1 as d3"
+hg rename d1/ d3
+hg status
+hg update -C
+
+echo "# rename --after directory d1 as d3"
+mv d1 d3
+hg rename --after d1 d3
+hg status
+hg update -C
+
+echo "# move a directory using a relative path"
+(cd d2; mkdir d3; hg rename ../d1/d11 d3)
+hg status
+hg update -C
+
+echo "# move --after a directory using a relative path"
+(cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
+hg status
+hg update -C
+
+echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
+hg rename d1/d11/ d2
+hg status
+hg update -C
+
+echo "# move directories d1 and d2 to a new directory d3"
+mkdir d3
+hg rename d1 d2 d3
+hg status
+hg update -C
+
+echo "# move --after directories d1 and d2 to a new directory d3"
+mkdir d3
+mv d1 d2 d3
+hg rename --after d1 d2 d3
+hg status
+hg update -C
+
+echo "# move everything under directory d1 to existing directory d2, do not"
+echo "# overwrite existing files (d2/b)"
+hg rename d1/* d2
+hg status
+diff d1/b d2/b
+hg update -C
+
+echo "# attempt to move potentially more than one file into a non-existent"
+echo "# directory"
+hg rename 'glob:d1/**' dx
+
+echo "# move every file under d1 to d2/d21 (glob)"
+mkdir d2/d21
+hg rename 'glob:d1/**' d2/d21
+hg status
+hg update -C
+
+echo "# move --after some files under d1 to d2/d21 (glob)"
+mkdir d2/d21
+mv d1/a d1/d11/a1 d2/d21
+hg rename --after 'glob:d1/**' d2/d21
+hg status
+hg update -C
+
+echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
+mkdir d2/d21
+hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
+hg status
+hg update -C
+
+echo "# attempt to overwrite an existing file"
+echo "ca" > d1/ca
+hg rename d1/ba d1/ca
+hg status
+hg update -C
+
+echo "# forced overwrite of an existing file"
+echo "ca" > d1/ca
+hg rename --force d1/ba d1/ca
+hg status
+hg update -C
+
+echo "# replace a symlink with a file"
+ln -s ba d1/ca
+hg rename --force d1/ba d1/ca
+hg status
+hg update -C
+
+echo "# do not copy more than one source file to the same destination file"
+mkdir d3
+hg rename d1/* d2/* d3
+hg status
+hg update -C
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rename.out Sun Dec 11 15:38:42 2005 -0800
@@ -0,0 +1,183 @@
+# rename a single file
+A d2/c
+R d1/d11/a1
+# rename --after a single file
+A d2/c
+R d1/d11/a1
+# move a single file to an existing directory
+A d2/a1
+R d1/d11/a1
+# move --after a single file to an existing directory
+A d2/a1
+R d1/d11/a1
+# rename a file using a relative path
+A d1/d11/e
+R d2/b
+# rename --after a file using a relative path
+A d1/d11/e
+R d2/b
+# rename directory d1 as d3
+copying d1/a to d3/a
+copying d1/b to d3/b
+copying d1/ba to d3/ba
+copying d1/d11/a1 to d3/d11/a1
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+A d3/a
+A d3/b
+A d3/ba
+A d3/d11/a1
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
+# rename --after directory d1 as d3
+copying d1/a to d3/a
+copying d1/b to d3/b
+copying d1/ba to d3/ba
+copying d1/d11/a1 to d3/d11/a1
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+A d3/a
+A d3/b
+A d3/ba
+A d3/d11/a1
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
+# move a directory using a relative path
+copying ../d1/d11/a1 to d3/d11/a1
+removing ../d1/d11/a1
+A d2/d3/d11/a1
+R d1/d11/a1
+# move --after a directory using a relative path
+copying ../d1/d11/a1 to d3/d11/a1
+removing ../d1/d11/a1
+A d2/d3/d11/a1
+R d1/d11/a1
+# move directory d1/d11 to an existing directory d2 (removes empty d1)
+copying d1/d11/a1 to d2/d11/a1
+removing d1/d11/a1
+A d2/d11/a1
+R d1/d11/a1
+# move directories d1 and d2 to a new directory d3
+copying d1/a to d3/d1/a
+copying d1/b to d3/d1/b
+copying d1/ba to d3/d1/ba
+copying d1/d11/a1 to d3/d1/d11/a1
+copying d2/b to d3/d2/b
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+removing d2/b
+A d3/d1/a
+A d3/d1/b
+A d3/d1/ba
+A d3/d1/d11/a1
+A d3/d2/b
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
+R d2/b
+# move --after directories d1 and d2 to a new directory d3
+copying d1/a to d3/d1/a
+copying d1/b to d3/d1/b
+copying d1/ba to d3/d1/ba
+copying d1/d11/a1 to d3/d1/d11/a1
+copying d2/b to d3/d2/b
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+removing d2/b
+A d3/d1/a
+A d3/d1/b
+A d3/d1/ba
+A d3/d1/d11/a1
+A d3/d2/b
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
+R d2/b
+# move everything under directory d1 to existing directory d2, do not
+# overwrite existing files (d2/b)
+d2/b: not overwriting - file exists
+copying d1/d11/a1 to d2/d11/a1
+removing d1/d11/a1
+A d2/a
+A d2/ba
+A d2/d11/a1
+R d1/a
+R d1/ba
+R d1/d11/a1
+1c1
+< d1/b
+---
+> d2/b
+# attempt to move potentially more than one file into a non-existent
+# directory
+abort: with multiple sources, destination must be an existing directory
+# move every file under d1 to d2/d21 (glob)
+copying d1/a to d2/d21/a
+copying d1/b to d2/d21/b
+copying d1/ba to d2/d21/ba
+copying d1/d11/a1 to d2/d21/a1
+removing d1/a
+removing d1/b
+removing d1/ba
+removing d1/d11/a1
+A d2/d21/a
+A d2/d21/a1
+A d2/d21/b
+A d2/d21/ba
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
+# move --after some files under d1 to d2/d21 (glob)
+copying d1/a to d2/d21/a
+copying d1/d11/a1 to d2/d21/a1
+removing d1/a
+removing d1/d11/a1
+A d2/d21/a
+A d2/d21/a1
+R d1/a
+R d1/d11/a1
+# move every file under d1 starting with an 'a' to d2/d21 (regexp)
+copying d1/a to d2/d21/a
+copying d1/d11/a1 to d2/d21/a1
+removing d1/a
+removing d1/d11/a1
+A d2/d21/a
+A d2/d21/a1
+R d1/a
+R d1/d11/a1
+# attempt to overwrite an existing file
+d1/ca: not overwriting - file exists
+? d1/ca
+# forced overwrite of an existing file
+A d1/ca
+R d1/ba
+# replace a symlink with a file
+A d1/ca
+R d1/ba
+# do not copy more than one source file to the same destination file
+copying d1/d11/a1 to d3/d11/a1
+d3/b: not overwriting - d2/b collides with d1/b
+removing d1/d11/a1
+A d3/a
+A d3/b
+A d3/ba
+A d3/d11/a1
+R d1/a
+R d1/b
+R d1/ba
+R d1/d11/a1
--- a/tests/test-symlinks Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-symlinks Sun Dec 11 15:38:42 2005 -0800
@@ -39,3 +39,4 @@
mkfifo a.c
# it should show a.c, dir/a.o and dir/b.o removed
hg status
+hg status a.c
--- a/tests/test-symlinks.out Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-symlinks.out Sun Dec 11 15:38:42 2005 -0800
@@ -1,15 +1,11 @@
-bar: unsupported file type (type is symbolic link)
adding foo
-bar: unsupported file type (type is symbolic link)
-bar: unsupported file type (type is symbolic link)
adding bomb
-bar: unsupported file type (type is symbolic link)
adding a.c
adding dir/a.o
adding dir/b.o
-a.c: unsupported file type (type is fifo)
-dir/b.o: unsupported file type (type is symbolic link)
R a.c
R dir/a.o
R dir/b.o
? .hgignore
+a.c: unsupported file type (type is fifo)
+R a.c
--- a/tests/test-tag Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-tag Sun Dec 11 15:38:42 2005 -0800
@@ -11,3 +11,7 @@
echo foo >> .hgtags
hg tag -d "0 0" "bleah2" || echo "failed"
+hg tag -l 'xx
+newline'
+hg tag -l 'xx:xx'
+true
--- a/tests/test-tag.out Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-tag.out Sun Dec 11 15:38:42 2005 -0800
@@ -18,3 +18,5 @@
abort: working copy of .hgtags is changed (please commit .hgtags manually)
failed
+abort: '\n' cannot be used in a tag name
+abort: ':' cannot be used in a tag name
--- a/tests/test-walk Fri Nov 04 11:51:01 2005 -0800
+++ b/tests/test-walk Sun Dec 11 15:38:42 2005 -0800
@@ -20,14 +20,14 @@
hg commit -m "commit #0" -d "0 0"
hg debugwalk
cd mammals
-hg debugwalk
+hg debugwalk .
hg debugwalk Procyonidae
cd Procyonidae
-hg debugwalk
+hg debugwalk .
hg debugwalk ..
cd ..
hg debugwalk ../beans
-hg debugwalk
+hg debugwalk .
cd ..
hg debugwalk -Ibeans
hg debugwalk 'glob:mammals/../beans/b*'