merge with tah
authorThomas Arendsen Hein <thomas@intevation.de>
Sat, 29 Jul 2006 08:11:41 +0200
changeset 2727 51c296f61b6e
parent 2725 9ffee4f07323 (diff)
parent 2726 6bd351194a96 (current diff)
child 2730 819485c43ce3
merge with tah
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -36,9 +36,8 @@ from mercurial import ui, hg, revlog, co
 
 versionstr = "0.45"
 
-repomap = {}
+commands.norepo += " qclone qversion"
 
-commands.norepo += " qversion"
 class queue:
     def __init__(self, ui, path, patchdir=None):
         self.basepath = path
@@ -187,8 +186,7 @@ class queue:
             return (err, n)
 
         if n is None:
-            self.ui.warn("apply failed for patch %s\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("apply failed for patch %s") % patch)
 
         self.ui.warn("patch didn't work out, merging %s\n" % patch)
 
@@ -199,17 +197,14 @@ class queue:
         c = repo.changelog.read(rev)
         ret = repo.update(rev, allow=True, wlock=wlock)
         if ret:
-            self.ui.warn("update returned %d\n" % ret)
-            sys.exit(1)
+            raise util.Abort(_("update returned %d") % ret)
         n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
         if n == None:
-            self.ui.warn("repo commit failed\n")
-            sys.exit(1)
+            raise util.Abort(_("repo commit failed"))
         try:
             message, comments, user, date, patchfound = mergeq.readheaders(patch)
         except:
-            self.ui.warn("Unable to read %s\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("unable to read %s") % patch)
 
         patchf = self.opener(patch, "w")
         if comments:
@@ -280,8 +275,6 @@ class queue:
         # TODO unify with commands.py
         if not patchdir:
             patchdir = self.path
-        pwd = os.getcwd()
-        os.chdir(repo.root)
         err = 0
         if not wlock:
             wlock = repo.wlock()
@@ -308,7 +301,8 @@ class queue:
 
             try:
                 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
-                f = os.popen("%s -p1 --no-backup-if-mismatch < '%s'" % (pp, pf))
+                f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
+                             (pp, repo.root, pf))
             except:
                 self.ui.warn("patch failed, unable to continue (try -v)\n")
                 err = 1
@@ -356,8 +350,7 @@ class queue:
                             wlock=wlock)
 
             if n == None:
-                self.ui.warn("repo commit failed\n")
-                sys.exit(1)
+                raise util.Abort(_("repo commit failed"))
 
             if update_status:
                 self.applied.append(revlog.hex(n) + ":" + patch)
@@ -376,18 +369,15 @@ class queue:
                 err = 1
                 break
         tr.close()
-        os.chdir(pwd)
         return (err, n)
 
     def delete(self, repo, patch):
         patch = self.lookup(patch, strict=True)
         info = self.isapplied(patch)
         if info:
-            self.ui.warn("cannot delete applied patch %s\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("cannot delete applied patch %s") % patch)
         if patch not in self.series:
-            self.ui.warn("patch %s not in series file\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("patch %s not in series file") % patch)
         i = self.find_series(patch)
         del self.full_series[i]
         self.read_series(self.full_series)
@@ -399,23 +389,22 @@ class queue:
             top = revlog.bin(top)
             pp = repo.dirstate.parents()
             if top not in pp:
-                self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
-                sys.exit(1)
+                raise util.Abort(_("queue top not at same revision as working directory"))
             return top
         return None
     def check_localchanges(self, repo):
         (c, a, r, d, u) = repo.changes(None, None)
         if c or a or d or r:
-            self.ui.write("Local changes found, refresh first\n")
-            sys.exit(1)
+            raise util.Abort(_("local changes found, refresh first"))
     def new(self, repo, patch, msg=None, force=None):
+        if os.path.exists(os.path.join(self.path, patch)):
+            raise util.Abort(_('patch "%s" already exists') % patch)
         commitfiles = []
         (c, a, r, d, u) = repo.changes(None, None)
         if c or a or d or r:
             if not force:
-                raise util.Abort(_("Local changes found, refresh first"))
-            else:
-                commitfiles = c + a + r
+                raise util.Abort(_("local changes found, refresh first"))
+            commitfiles = c + a + r
         self.check_toppatch(repo)
         wlock = repo.wlock()
         insert = self.full_series_end()
@@ -426,8 +415,7 @@ class queue:
             n = repo.commit(commitfiles,
                             "New patch: %s" % patch, force=True, wlock=wlock)
         if n == None:
-            self.ui.warn("repo commit failed\n")
-            sys.exit(1)
+            raise util.Abort(_("repo commit failed"))
         self.full_series[insert:insert] = [patch]
         self.applied.append(revlog.hex(n) + ":" + patch)
         self.read_series(self.full_series)
@@ -532,7 +520,7 @@ class queue:
         if update:
             (c, a, r, d, u) = repo.changes(None, None)
             if c or a or d or r:
-                raise util.Abort(_("Local changes found"))
+                raise util.Abort(_("local changes found"))
             urev = self.qparents(repo, rev)
             repo.update(urev, allow=False, force=True, wlock=wlock)
             repo.dirstate.write()
@@ -672,8 +660,7 @@ class queue:
                         else:
                             if i + off < len(self.series):
                                 return self.series[i + off]
-        self.ui.warn("patch %s not in series\n" % patch)
-        sys.exit(1)
+        raise util.Abort(_("patch %s not in series") % patch)
 
     def push(self, repo, patch=None, force=False, list=False,
              mergeq=None, wlock=None):
@@ -681,10 +668,10 @@ class queue:
             wlock = repo.wlock()
         patch = self.lookup(patch)
         if patch and self.isapplied(patch):
-            self.ui.warn("patch %s is already applied\n" % patch)
+            self.ui.warn(_("patch %s is already applied\n") % patch)
             sys.exit(1)
         if self.series_end() == len(self.series):
-            self.ui.warn("File series fully applied\n")
+            self.ui.warn(_("patch series fully applied\n"))
             sys.exit(1)
         if not force:
             self.check_localchanges(repo)
@@ -733,10 +720,9 @@ class queue:
                 patch = self.lookup(patch)
             info = self.isapplied(patch)
             if not info:
-                self.ui.warn("patch %s is not applied\n" % patch)
-                sys.exit(1)
+                raise util.Abort(_("patch %s is not applied") % patch)
         if len(self.applied) == 0:
-            self.ui.warn("No patches applied\n")
+            self.ui.warn(_("no patches applied\n"))
             sys.exit(1)
 
         if not update:
@@ -910,15 +896,14 @@ class queue:
 
     def init(self, repo, create=False):
         if os.path.isdir(self.path):
-            raise util.Abort("patch queue directory already exists")
+            raise util.Abort(_("patch queue directory already exists"))
         os.mkdir(self.path)
         if create:
             return self.qrepo(create=True)
 
     def unapplied(self, repo, patch=None):
         if patch and patch not in self.series:
-            self.ui.warn("%s not in the series file\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("patch %s is not in series file") % patch)
         if not patch:
             start = self.series_end()
         else:
@@ -1070,8 +1055,7 @@ class queue:
 
     def qapplied(self, repo, patch=None):
         if patch and patch not in self.series:
-            self.ui.warn("%s not in the series file\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("patch %s is not in series file") % patch)
         if not patch:
             end = len(self.applied)
         else:
@@ -1117,8 +1101,8 @@ class queue:
 
     def qimport(self, repo, files, patch=None, existing=None, force=None):
         if len(files) > 1 and patch:
-            self.ui.warn("-n option not valid when importing multiple files\n")
-            sys.exit(1)
+            raise util.Abort(_('option "-n" not valid when importing multiple '
+                               'files'))
         i = 0
         added = []
         for filename in files:
@@ -1126,24 +1110,21 @@ class queue:
                 if not patch:
                     patch = filename
                 if not os.path.isfile(os.path.join(self.path, patch)):
-                    self.ui.warn("patch %s does not exist\n" % patch)
-                    sys.exit(1)
+                    raise util.Abort(_("patch %s does not exist") % patch)
             else:
                 try:
                     text = file(filename).read()
                 except IOError:
-                    self.ui.warn("Unable to read %s\n" % patch)
-                    sys.exit(1)
+                    raise util.Abort(_("unable to read %s") % patch)
                 if not patch:
                     patch = os.path.split(filename)[1]
-                if not force and os.path.isfile(os.path.join(self.path, patch)):
-                    self.ui.warn("patch %s already exists\n" % patch)
-                    sys.exit(1)
+                if not force and os.path.exists(os.path.join(self.path, patch)):
+                    raise util.Abort(_('patch "%s" already exists') % patch)
                 patchf = self.opener(patch, "w")
                 patchf.write(text)
             if patch in self.series:
-                self.ui.warn("patch %s is already in the series file\n" % patch)
-                sys.exit(1)
+                raise util.Abort(_('patch %s is already in the series file')
+                                 % patch)
             index = self.full_series_end() + i
             self.full_series[index:index] = [patch]
             self.read_series(self.full_series)
@@ -1158,24 +1139,24 @@ class queue:
 
 def delete(ui, repo, patch, **opts):
     """remove a patch from the series file"""
-    q = repomap[repo]
+    q = repo.mq
     q.delete(repo, patch)
     q.save_dirty()
     return 0
 
 def applied(ui, repo, patch=None, **opts):
     """print the patches already applied"""
-    repomap[repo].qapplied(repo, patch)
+    repo.mq.qapplied(repo, patch)
     return 0
 
 def unapplied(ui, repo, patch=None, **opts):
     """print the patches not yet applied"""
-    repomap[repo].unapplied(repo, patch)
+    repo.mq.unapplied(repo, patch)
     return 0
 
 def qimport(ui, repo, *filename, **opts):
     """import a patch"""
-    q = repomap[repo]
+    q = repo.mq
     q.qimport(repo, filename, patch=opts['name'],
               existing=opts['existing'], force=opts['force'])
     q.save_dirty()
@@ -1183,7 +1164,7 @@ def qimport(ui, repo, *filename, **opts)
 
 def init(ui, repo, **opts):
     """init a new queue repository"""
-    q = repomap[repo]
+    q = repo.mq
     r = q.init(repo, create=opts['create_repo'])
     q.save_dirty()
     if r:
@@ -1195,36 +1176,80 @@ def init(ui, repo, **opts):
         r.add(['.hgignore', 'series'])
     return 0
 
+def clone(ui, source, dest=None, **opts):
+    '''clone main and patch repository at same time
+
+    If source is local, destination will have no patches applied.  If
+    source is remote, this command can not check if patches are
+    applied in source, so cannot guarantee that patches are not
+    applied in destination.  If you clone remote repository, be sure
+    before that it has no patches applied.
+
+    Source patch repository is looked for in <src>/.hg/patches by
+    default.  Use -p <url> to change.
+    '''
+    ui.setconfig_remoteopts(**opts)
+    if dest is None:
+        dest = hg.defaultdest(source)
+    sr = hg.repository(ui, ui.expandpath(source))
+    qbase, destrev = None, None
+    if sr.local():
+        reposetup(ui, sr)
+        if sr.mq.applied:
+            qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
+            if not hg.islocal(dest):
+                destrev = sr.parents(qbase)[0]
+    ui.note(_('cloning main repo\n'))
+    sr, dr = hg.clone(ui, sr, dest,
+                      pull=opts['pull'],
+                      rev=destrev,
+                      update=False,
+                      stream=opts['uncompressed'])
+    ui.note(_('cloning patch repo\n'))
+    spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
+                        dr.url() + '/.hg/patches',
+                        pull=opts['pull'],
+                        update=not opts['noupdate'],
+                        stream=opts['uncompressed'])
+    if dr.local():
+        if qbase:
+            ui.note(_('stripping applied patches from destination repo\n'))
+            reposetup(ui, dr)
+            dr.mq.strip(dr, qbase, update=False, backup=None)
+        if not opts['noupdate']:
+            ui.note(_('updating destination repo\n'))
+            dr.update(dr.changelog.tip())
+
 def commit(ui, repo, *pats, **opts):
     """commit changes in the queue repository"""
-    q = repomap[repo]
+    q = repo.mq
     r = q.qrepo()
     if not r: raise util.Abort('no queue repository')
     commands.commit(r.ui, r, *pats, **opts)
 
 def series(ui, repo, **opts):
     """print the entire series file"""
-    repomap[repo].qseries(repo, missing=opts['missing'])
+    repo.mq.qseries(repo, missing=opts['missing'])
     return 0
 
 def top(ui, repo, **opts):
     """print the name of the current patch"""
-    repomap[repo].top(repo)
+    repo.mq.top(repo)
     return 0
 
 def next(ui, repo, **opts):
     """print the name of the next patch"""
-    repomap[repo].next(repo)
+    repo.mq.next(repo)
     return 0
 
 def prev(ui, repo, **opts):
     """print the name of the previous patch"""
-    repomap[repo].prev(repo)
+    repo.mq.prev(repo)
     return 0
 
 def new(ui, repo, patch, **opts):
     """create a new patch"""
-    q = repomap[repo]
+    q = repo.mq
     message=commands.logmessage(**opts)
     q.new(repo, patch, msg=message, force=opts['force'])
     q.save_dirty()
@@ -1232,7 +1257,7 @@ def new(ui, repo, patch, **opts):
 
 def refresh(ui, repo, **opts):
     """update the current patch"""
-    q = repomap[repo]
+    q = repo.mq
     message=commands.logmessage(**opts)
     q.refresh(repo, msg=message, short=opts['short'])
     q.save_dirty()
@@ -1241,7 +1266,7 @@ def refresh(ui, repo, **opts):
 def diff(ui, repo, *files, **opts):
     """diff of the current patch"""
     # deep in the dirstate code, the walkhelper method wants a list, not a tuple
-    repomap[repo].diff(repo, list(files))
+    repo.mq.diff(repo, list(files))
     return 0
 
 def lastsavename(path):
@@ -1270,7 +1295,7 @@ def savename(path):
 
 def push(ui, repo, patch=None, **opts):
     """push the next patch onto the stack"""
-    q = repomap[repo]
+    q = repo.mq
     mergeq = None
 
     if opts['all']:
@@ -1298,7 +1323,7 @@ def pop(ui, repo, patch=None, **opts):
         ui.warn('using patch queue: %s\n' % q.path)
         localupdate = False
     else:
-        q = repomap[repo]
+        q = repo.mq
     q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
     q.save_dirty()
     return 0
@@ -1306,7 +1331,7 @@ def pop(ui, repo, patch=None, **opts):
 def restore(ui, repo, rev, **opts):
     """restore the queue state saved by a rev"""
     rev = repo.lookup(rev)
-    q = repomap[repo]
+    q = repo.mq
     q.restore(repo, rev, delete=opts['delete'],
               qupdate=opts['update'])
     q.save_dirty()
@@ -1314,7 +1339,7 @@ def restore(ui, repo, rev, **opts):
 
 def save(ui, repo, **opts):
     """save current queue state"""
-    q = repomap[repo]
+    q = repo.mq
     message=commands.logmessage(**opts)
     ret = q.save(repo, msg=message)
     if ret:
@@ -1326,13 +1351,11 @@ def save(ui, repo, **opts):
             newpath = os.path.join(q.basepath, opts['name'])
             if os.path.exists(newpath):
                 if not os.path.isdir(newpath):
-                    ui.warn("destination %s exists and is not a directory\n" %
-                            newpath)
-                    sys.exit(1)
+                    raise util.Abort(_('destination %s exists and is not '
+                                       'a directory') % newpath)
                 if not opts['force']:
-                    ui.warn("destination %s exists, use -f to force\n" %
-                            newpath)
-                    sys.exit(1)
+                    raise util.Abort(_('destination %s exists, '
+                                       'use -f to force') % newpath)
         else:
             newpath = savename(path)
         ui.warn("copy %s to %s\n" % (path, newpath))
@@ -1352,7 +1375,7 @@ def strip(ui, repo, rev, **opts):
         backup = 'strip'
     elif opts['nobackup']:
         backup = 'none'
-    repomap[repo].strip(repo, rev, backup=backup)
+    repo.mq.strip(repo, rev, backup=backup)
     return 0
 
 def version(ui, q=None):
@@ -1361,34 +1384,43 @@ def version(ui, q=None):
     return 0
 
 def reposetup(ui, repo):
-    repomap[repo] = queue(ui, repo.join(""))
-    oldtags = repo.tags
+    class MqRepo(repo.__class__):
+        def tags(self):
+            if self.tagscache:
+                return self.tagscache
+
+            tagscache = super(self.__class__, self).tags()
 
-    def qtags():
-        if repo.tagscache:
-            return repo.tagscache
+            q = self.mq
+            if not q.applied:
+                return tagscache
 
-        tagscache = oldtags()
+            mqtags = [patch.split(':') for patch in q.applied]
+            mqtags.append((mqtags[-1][0], 'qtip'))
+            mqtags.append((mqtags[0][0], 'qbase'))
+            for patch in mqtags:
+                if patch[1] in tagscache:
+                    self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
+                else:
+                    tagscache[patch[1]] = revlog.bin(patch[0])
 
-        q = repomap[repo]
-        if len(q.applied) == 0:
             return tagscache
 
-        mqtags = [patch.split(':') for patch in q.applied]
-        mqtags.append((mqtags[-1][0], 'qtip'))
-        mqtags.append((mqtags[0][0], 'qbase'))
-        for patch in mqtags:
-            if patch[1] in tagscache:
-                repo.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
-            else:
-                tagscache[patch[1]] = revlog.bin(patch[0])
-
-        return tagscache
-
-    repo.tags = qtags
+    repo.__class__ = MqRepo
+    repo.mq = queue(ui, repo.join(""))
 
 cmdtable = {
     "qapplied": (applied, [], 'hg qapplied [PATCH]'),
+    "qclone": (clone,
+               [('', 'pull', None, _('use pull protocol to copy metadata')),
+                ('U', 'noupdate', None, _('do not update the new working directories')),
+                ('', 'uncompressed', None,
+                 _('use uncompressed transfer (fast over LAN)')),
+                ('e', 'ssh', '', _('specify ssh command to use')),
+                ('p', 'patches', '', _('location of source patch repo')),
+                ('', 'remotecmd', '',
+                 _('specify hg command to run on the remote side'))],
+               'hg qclone [OPTION]... SOURCE [DEST]'),
     "qcommit|qci":
         (commit,
          commands.table["^commit|ci"][1],
--- a/hgext/patchbomb.py
+++ b/hgext/patchbomb.py
@@ -45,6 +45,7 @@ demandload(globals(), '''email.MIMEMulti
                          mercurial:commands,hg,ui
                          os errno popen2 socket sys tempfile time''')
 from mercurial.i18n import gettext as _
+from mercurial.node import *
 
 try:
     # readline gives raw_input editing capabilities, but is not
@@ -130,8 +131,26 @@ def patchbomb(ui, repo, *revs, **opts):
             while patch and not patch[0].strip(): patch.pop(0)
         if opts['diffstat']:
             body += cdiffstat('\n'.join(desc), patch) + '\n\n'
-        body += '\n'.join(patch)
-        msg = email.MIMEText.MIMEText(body)
+        if opts['attach']:
+            msg = email.MIMEMultipart.MIMEMultipart()
+            if body: msg.attach(email.MIMEText.MIMEText(body, 'plain'))
+            p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch')
+            binnode = bin(node)
+            # if node is mq patch, it will have patch file name as tag
+            patchname = [t for t in repo.nodetags(binnode)
+                         if t.endswith('.patch') or t.endswith('.diff')]
+            if patchname:
+                patchname = patchname[0]
+            elif total > 1:
+                patchname = commands.make_filename(repo, '%b-%n.patch',
+                                                   binnode, idx, total)
+            else:
+                patchname = commands.make_filename(repo, '%b.patch', binnode)
+            p['Content-Disposition'] = 'inline; filename=' + patchname
+            msg.attach(p)
+        else:
+            body += '\n'.join(patch)
+            msg = email.MIMEText.MIMEText(body)
         if total == 1:
             subj = '[PATCH] ' + desc[0].strip()
         else:
@@ -274,7 +293,8 @@ def patchbomb(ui, repo, *revs, **opts):
 cmdtable = {
     'email':
     (patchbomb,
-     [('', 'bcc', [], 'email addresses of blind copy recipients'),
+     [('a', 'attach', None, 'send patches as inline attachments'),
+      ('', 'bcc', [], 'email addresses of blind copy recipients'),
       ('c', 'cc', [], 'email addresses of copy recipients'),
       ('d', 'diffstat', None, 'add diffstat output to messages'),
       ('f', 'from', '', 'email address of sender'),
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -432,25 +432,16 @@ def dodiff(fp, ui, repo, node1, node2, f
                      diffopts['ignorewsamount']
     ignoreblanklines = opts.get('ignore_blank_lines') or \
                      diffopts['ignoreblanklines']
-    for f in modified:
+
+    all = modified + added + removed
+    all.sort()
+    for f in all:
         to = None
+        tn = None
         if f in mmap:
             to = repo.file(f).read(mmap[f])
-        tn = read(f)
-        fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
-                               showfunc=showfunc, ignorews=ignorews,
-                               ignorewsamount=ignorewsamount,
-                               ignoreblanklines=ignoreblanklines))
-    for f in added:
-        to = None
-        tn = read(f)
-        fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
-                               showfunc=showfunc, ignorews=ignorews,
-                               ignorewsamount=ignorewsamount,
-                               ignoreblanklines=ignoreblanklines))
-    for f in removed:
-        to = repo.file(f).read(mmap[f])
-        tn = None
+        if f not in removed:
+            tn = read(f)
         fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
                                showfunc=showfunc, ignorews=ignorews,
                                ignorewsamount=ignorewsamount,
@@ -3440,6 +3431,11 @@ def dispatch(args):
             else:
                 d = lambda: func(u, *args, **cmdoptions)
 
+            # reupdate the options, repo/.hg/hgrc may have changed them
+            u.updateopts(options["verbose"], options["debug"], options["quiet"],
+                         not options["noninteractive"], options["traceback"],
+                         options["config"])
+
             try:
                 if options['profile']:
                     import hotshot, hotshot.stats
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -57,6 +57,23 @@ schemes = {
     'static-http': static_http,
     }
 
+remote_schemes = [
+    'bundle',
+    'hg',
+    'http',
+    'https',
+    'old-http',
+    'ssh',
+    'static-http',
+    ]
+
+def islocal(repo):
+    '''return true if repo or path is local'''
+    if isinstance(repo, str):
+        c = repo.find(':')
+        return c <= 0 or repo[:c] not in remote_schemes
+    return repo.local()
+
 def repository(ui, path=None, create=0):
     scheme = None
     if path:
@@ -74,6 +91,10 @@ def repository(ui, path=None, create=0):
                              scheme)
     return ctor(ui, path)
 
+def defaultdest(source):
+    '''return default destination of clone if none is given'''
+    return os.path.basename(os.path.normpath(source))
+    
 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
           stream=False):
     """Make a copy of an existing repository.
@@ -90,7 +111,9 @@ def clone(ui, source, dest=None, pull=Fa
     If an exception is raised, the partly cloned/updated destination
     repository will be deleted.
 
-    Keyword arguments:
+    Arguments:
+
+    source: repository object or URL
 
     dest: URL of destination repository to create (defaults to base
     name of source repository)
@@ -105,8 +128,24 @@ def clone(ui, source, dest=None, pull=Fa
     update: update working directory after clone completes, if
     destination is local repository
     """
+    if isinstance(source, str):
+        src_repo = repository(ui, source)
+    else:
+        src_repo = source
+        source = src_repo.url()
+
     if dest is None:
-        dest = os.path.basename(os.path.normpath(source))
+        dest = defaultdest(source)
+
+    def localpath(path):
+        if path.startswith('file://'):
+            return path[7:]
+        if path.startswith('file:'):
+            return path[5:]
+        return path
+
+    dest = localpath(dest)
+    source = localpath(source)
 
     if os.path.exists(dest):
         raise util.Abort(_("destination '%s' already exists"), dest)
@@ -121,8 +160,6 @@ def clone(ui, source, dest=None, pull=Fa
             if self.dir_:
                 self.rmtree(self.dir_, True)
 
-    src_repo = repository(ui, source)
-
     dest_repo = None
     try:
         dest_repo = repository(ui, dest)
@@ -133,7 +170,7 @@ def clone(ui, source, dest=None, pull=Fa
     dest_path = None
     dir_cleanup = None
     if dest_repo.local():
-        dest_path = os.path.realpath(dest)
+        dest_path = os.path.realpath(dest_repo.root)
         dir_cleanup = DirCleanup(dest_path)
 
     abspath = source
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -201,6 +201,11 @@ def run(cmd):
     return ret, splitnewlines(output)
 
 def run_one(test):
+    '''tristate output:
+    None -> skipped
+    True -> passed
+    False -> failed'''
+
     vlog("# Test", test)
     if not verbose:
         sys.stdout.write('.')
@@ -217,15 +222,28 @@ def run_one(test):
     os.mkdir(tmpd)
     os.chdir(tmpd)
 
-    if test.endswith(".py"):
-        cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
-    else:
-        cmd = '"%s"' % (os.path.join(TESTDIR, test))
+    lctest = test.lower()
 
-    # To reliably get the error code from batch files on WinXP,
-    # the "cmd /c call" prefix is needed. Grrr
-    if os.name == 'nt' and test.endswith(".bat"):
+    if lctest.endswith('.py'):
+        cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
+    elif lctest.endswith('.bat'):
+        # do not run batch scripts on non-windows
+        if os.name != 'nt':
+            print '\nSkipping %s: batch script' % test
+            return None
+        # To reliably get the error code from batch files on WinXP,
+        # the "cmd /c call" prefix is needed. Grrr
         cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
+    else:
+        # do not run shell scripts on windows
+        if os.name == 'nt':
+            print '\nSkipping %s: shell script' % test
+            return None
+        # do not try to run non-executable programs
+        if not os.access(os.path.join(TESTDIR, test), os.X_OK):
+            print '\nSkipping %s: not executable' % test
+            return None
+        cmd = '"%s"' % (os.path.join(TESTDIR, test))
 
     if options.timeout > 0:
         signal.alarm(options.timeout)
@@ -330,6 +348,7 @@ try:
 
         tests = 0
         failed = 0
+        skipped = 0
 
         if len(args) == 0:
             args = os.listdir(".")
@@ -337,11 +356,15 @@ try:
             if (test.startswith("test-") and '~' not in test and
                 ('.' not in test or test.endswith('.py') or 
                  test.endswith('.bat'))):
-                if not run_one(test):
+                ret = run_one(test)
+                if ret is None:
+                    skipped += 1
+                elif not ret:
                     failed += 1
                 tests += 1
 
-        print "\n# Ran %d tests, %d failed." % (tests, failed)
+        print "\n# Ran %d tests, %d skipped, %d failed." % (tests, skipped,
+                                                            failed)
         if coverage:
             output_coverage()
     except KeyboardInterrupt:
new file mode 100755
--- /dev/null
+++ b/tests/test-mq-qnew-twice
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+
+hg init a
+cd a
+hg qnew first.patch
+hg qnew first.patch
+
+touch ../first.patch
+hg qimport ../first.patch
+
+exit 0
new file mode 100644
--- /dev/null
+++ b/tests/test-mq-qnew-twice.out
@@ -0,0 +1,2 @@
+abort: patch "first.patch" already exists
+abort: patch "first.patch" already exists
--- a/tests/test-mq-qrefresh-replace-log-message
+++ b/tests/test-mq-qrefresh-replace-log-message
@@ -1,9 +1,9 @@
 #!/bin/sh
 
 # Environement setup for MQ
-export HGRCPATH=./hgrc
-echo "[extensions]" >> ./hgrc
-echo "mq=" >> ./hgrc
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
 
 #Repo init
 hg init