changeset:   215:b5ef9a404f5d
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Mar 27 16:56:07 2009 -0500
summary:     Stopped idiotic filtering of revisions that did not edit something branches/tags/trunk.

diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -85,6 +85,8 @@
     branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
     pickle.dump(branchinfo, branchinfofile)
     branchinfofile.close()
+
+    # now handle tags
     tagsinfo = {}
     realtags = svn.tags
     tagsleft = realtags.items()
@@ -103,10 +105,11 @@
                 older_tags = svn.tags_at_rev(rev)
                 newsrc, newrev = older_tags[src]
                 tagsleft.append((tag, (newsrc, newrev)))
-        if source.startswith('branches/') or source == 'trunk':
+            continue
+        else:
             source = determinebranch(source)
-            if rev <= last_rev:
-                tagsinfo[tag] = source, rev
+        if rev <= last_rev:
+            tagsinfo[tag] = source, rev
     tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
     pickle.dump(tagsinfo, tagsinfofile)
     tagsinfofile.close()
@@ -114,10 +117,11 @@
 rebuildmeta = util.command_needs_no_url(rebuildmeta)
 
 def determinebranch(branch):
-    if branch.startswith('branches'):
+    assert not branch.startswith('tags/'), "Tags can't be tags of other tags."
+    if branch.startswith('branches/'):
         branch = branch[len('branches/'):]
     elif branch == 'trunk':
         branch = None
     else:
-        assert False, 'Unhandled case while regenerating metadata.'
+        branch = '../' + branch
     return branch
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -211,6 +211,8 @@
         source = hist.paths[path].copyfrom_path
         source_rev = 0
         for p in hist.paths:
+            if not p.startswith(path):
+                continue
             if hist.paths[p].copyfrom_rev:
                 # We assume that the revision of the source tree as it was
                 # copied was actually the revision of the highest revision
@@ -247,26 +249,8 @@
         The reason this is lazy is so that you can use the same repo object
         to perform RA calls to get deltas.
         """
-        # NB: you'd think this would work, but you'd be wrong. I'm pretty
-        # convinced there must be some kind of svn bug here.
-        #return self.fetch_history_at_paths(['tags', 'trunk', 'branches'],
-        #                                   start=start)
-        # this does the same thing, but at the repo root + filtering. It's
-        # kind of tough cookies, sadly.
-        for r in self.fetch_history_at_paths([''], start=start,
-                                             chunk_size=chunk_size):
-            should_yield = False
-            i = 0
-            paths = list(r.paths.keys())
-            while i < len(paths) and not should_yield:
-                p = paths[i]
-                if (p.startswith('trunk') or p.startswith('tags')
-                    or p.startswith('branches')):
-                    should_yield = True
-                i += 1
-            if should_yield:
-                yield r
-
+        return self.fetch_history_at_paths([''], start=start,
+                                           chunk_size=chunk_size)
 
     def fetch_history_at_paths(self, paths, start=None, stop=None,
                                chunk_size=1000):

changeset:   216:3c87de482011
user:        Augie Fackler <durin42@gmail.com>
date:        Sat Mar 28 14:26:35 2009 -0500
summary:     tools: fixes for wc outputting with spaces.

diff --git a/tools/common.sh b/tools/common.sh
--- a/tools/common.sh
+++ b/tools/common.sh
@@ -9,7 +9,7 @@
         echo 'export failed!'
         return 255
     fi
-    if [[ "`hg st | wc -l`" == "0" ]] ; then
+    if [[ "`hg st | wc -l | python -c 'import sys; print sys.stdin.read().strip()'`" == "0" ]] ; then
         return 0
     else
         if [[ $1 != "keep" ]] ; then
diff --git a/tools/verify-all-heads.sh b/tools/verify-all-heads.sh
--- a/tools/verify-all-heads.sh
+++ b/tools/verify-all-heads.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 . $(dirname $0)/common.sh
 
-for b in `hg branches | cut -f 1 -d ' '` ; do
+for b in `hg branches -a | cut -f 1 -d ' ' | grep -v closed-branches` ; do
     hg co $b || break
     echo Verifying $b
     verify_current_revision keep > /dev/null || break

changeset:   217:6eb691a163cd
user:        Augie Fackler <durin42@gmail.com>
date:        Sat Mar 28 14:27:27 2009 -0500
summary:     hg_delta_editor: stop using foobaz as a bogus filename.

diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -346,6 +346,8 @@
         return True
 
     def _is_path_valid(self, path):
+        if path is None:
+            return False
         subpath = self._split_branch_path(path)[0]
         if subpath is None:
             return False
@@ -825,7 +827,7 @@
     delete_entry = stash_exception_on_self(delete_entry)
 
     def open_file(self, path, parent_baton, base_revision, p=None):
-        self.current_file = 'foobaz'
+        self.current_file = None
         fpath, branch = self._path_and_branch_for_path(path)
         if fpath:
             self.current_file = path
@@ -868,7 +870,7 @@
 
     def add_file(self, path, parent_baton, copyfrom_path,
                  copyfrom_revision, file_pool=None):
-        self.current_file = 'foobaz'
+        self.current_file = None
         self.base_revision = None
         if path in self.deleted_files:
             del self.deleted_files[path]
@@ -930,6 +932,7 @@
             source_rev = copyfrom_revision
             cp_f, source_branch = self._path_and_branch_for_path(copyfrom_path)
             if cp_f == '' and br_path == '':
+                assert br_path is not None
                 self.branches[branch] = source_branch, source_rev, self.current_rev.revnum
         new_hash = self.get_parent_revision(source_rev + 1,
                                             source_branch)

changeset:   218:a360ddc97719
user:        Augie Fackler <durin42@gmail.com>
date:        Sat Mar 28 16:38:57 2009 -0500
summary:     branches: change handling again, but this time a little less magic.

diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -115,6 +115,7 @@
     if count % 50 == 0:
         svn.init_ra_and_client()
 
+
 def replay_convert_rev(hg_editor, svn, r):
     hg_editor.set_current_rev(r)
     svn.get_replay(r.revnum, hg_editor)
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -225,6 +225,8 @@
                     if branchpath.startswith('trunk/'):
                         branches[self._localname('trunk')] = 'trunk'
                         continue
+                    if branchname and branchname.startswith('../'):
+                        continue
                     branches[branchname] = branchpath
 
         return branches
@@ -276,10 +278,17 @@
         if path.startswith('trunk/'):
             path = test.split('/')[1:]
             test = 'trunk'
+        elif path.startswith('branches/'):
+            elts = path.split('/')
+            test = '/'.join(elts[:2])
+            path = '/'.join(elts[2:])
         else:
             path = test.split('/')[-1]
             test = '/'.join(test.split('/')[:-1])
-        return path, self._localname(test), test
+        ln =  self._localname(test)
+        if ln and ln.startswith('../'):
+            return None, None, None
+        return path, ln, test
 
     def set_current_rev(self, rev):
         """Set the revision we're currently converting.
diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -108,7 +108,7 @@
             continue
         else:
             source = determinebranch(source)
-        if rev <= last_rev:
+        if rev <= last_rev and (source or 'default') in repo.branchtags():
             tagsinfo[tag] = source, rev
     tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
     pickle.dump(tagsinfo, tagsinfofile)

changeset:   219:794f473b9b49
user:        Augie Fackler <durin42@gmail.com>
date:        Sat Mar 28 17:38:48 2009 -0500
summary:     info: stop failing if the rev is not a child of an svn rev.

diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -72,7 +72,11 @@
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
     parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
-    r, br = svn_commit_hashes[parent.node()]
+    pn = parent.node()
+    if pn not in svn_commit_hashes:
+        ui.status('Not a child of an svn revision.\n')
+        return 0
+    r, br = svn_commit_hashes[pn]
     subdir = parent.extra()['convert_revision'][40:].split('@')[0]
     if br == None:
         branchpath = '/trunk'

changeset:   220:06eb60f9a026
user:        Augie Fackler <durin42@gmail.com>
date:        Sat Mar 28 17:50:00 2009 -0500
summary:     push: Do not attempt to push empty revisions.

diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -11,6 +11,15 @@
 import utility_commands
 
 
+class BaseException(Exception):
+    pass
+
+
+class NoFilesException(BaseException):
+    """Exception raised when you try and commit without files.
+    """
+
+
 def push_revisions_to_subversion(ui, repo, hg_repo_path, svn_url,
                                  stupid=False, **opts):
     """push revisions starting at a specified head back to Subversion.
@@ -50,7 +59,12 @@
                                   and c.node() in svn_commit_hashes]
         # 2. Commit oldest revision that needs to be pushed
         base_revision = svn_commit_hashes[base_n][0]
-        commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision)
+        try:
+            commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision)
+        except NoFilesException:
+            ui.warn("Could not push revision %s because it had no changes in svn.\n" %
+                     old_ctx)
+            return 1
         # 3. Fetch revisions from svn
         r = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path,
                                           stupid=stupid)
@@ -268,6 +282,8 @@
     addeddirs = [svnpath(d) for d in addeddirs]
     deleteddirs = [svnpath(d) for d in deleteddirs]
     new_target_files += addeddirs + deleteddirs + changeddirs
+    if not new_target_files:
+        raise NoFilesException()
     try:
         svn.commit(new_target_files, rev_ctx.description(), file_data,
                    base_revision, set(addeddirs), set(deleteddirs),

changeset:   221:ced45b753ba7
user:        Augie Fackler <durin42@gmail.com>
date:        Sat Mar 28 17:59:55 2009 -0500
summary:     util: better error messages when finding svn parent of a revision.

diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -3,6 +3,7 @@
 
 from mercurial import hg
 from mercurial import node
+from mercurial import util
 
 svn_subcommands = { }
 def register_subcommand(name):
@@ -94,7 +95,8 @@
            and sourcerev.node() != node.nullid):
         outgoing_rev_hashes.append(sourcerev.node())
         sourcerev = sourcerev.parents()
-        assert len(sourcerev) == 1
+        if len(sourcerev) != 1:
+            raise util.Abort("Sorry, can't find svn parent of a merge revision.")
         sourcerev = sourcerev[0]
     if sourcerev.node() != node.nullid:
         return outgoing_rev_hashes

changeset:   222:f2c65dd3d5c0
user:        Luke Opperman <luke@loppear.com>
date:        Tue Mar 31 15:23:38 2009 -0500
summary:     Avoid problems in _localname for commits to tags by ignoring them

diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -221,6 +221,8 @@
                     parentdir = '/'.join(path[:-1])
                     filepaths = [p for p in filepaths if not '/'.join(p).startswith(parentdir)]
                     branchpath = self._normalize_path(parentdir)
+                    if branchpath.startswith('tags/'):
+                        continue
                     branchname = self._localname(branchpath)
                     if branchpath.startswith('trunk/'):
                         branches[self._localname('trunk')] = 'trunk'

changeset:   223:330f0b15d417
user:        Luke Opperman <luke@loppear.com>
date:        Tue Apr 07 13:30:05 2009 -0500
summary:     issue67: mercurial 1.3 util incompatibility with encoding swap

diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -29,8 +29,7 @@
     """pull new revisions from Subversion
     """
     svn_url = util.normalize_url(svn_url)
-    old_encoding = merc_util._encoding
-    merc_util._encoding = 'UTF-8'
+    old_encoding = util.swap_out_encoding()
     skipto_rev=int(skipto_rev)
     have_replay = not stupid
     if have_replay and not callable(
@@ -107,7 +106,8 @@
                         ui.status('Got a 502, retrying (%s)\n' % tries)
                     else:
                         raise
-    merc_util._encoding = old_encoding
+    util.swap_out_encoding(old_encoding)
+
 fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
 
 
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -24,8 +24,7 @@
                                  stupid=False, **opts):
     """push revisions starting at a specified head back to Subversion.
     """
-    oldencoding = merc_util._encoding
-    merc_util._encoding = 'UTF-8'
+    old_encoding = util.swap_out_encoding()
     hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
@@ -101,7 +100,7 @@
                         rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
         hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
         svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
-    merc_util._encoding = oldencoding
+    util.swap_out_encoding(old_encoding)
     return 0
 push_revisions_to_subversion = util.register_subcommand('push')(push_revisions_to_subversion)
 # for git expats
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -4,6 +4,10 @@
 from mercurial import hg
 from mercurial import node
 from mercurial import util
+try:
+    from mercurial import encoding
+except ImportError:
+    encoding = None
 
 svn_subcommands = { }
 def register_subcommand(name):
@@ -135,3 +139,14 @@
 
 def describe_commit(ui, h, b):
     ui.note(' committed to "%s" as %s\n' % ((b or 'default'), node.short(h)))
+
+
+def swap_out_encoding(new_encoding="UTF-8"):
+    """ Utility for mercurial incompatibility changes, can be removed after 1.3"""
+    if encoding is None:
+        old = util._encoding
+        util._encoding = new_encoding
+    else:
+        old = encoding.encoding
+        encoding.encoding = new_encoding
+    return old

changeset:   227:d79843a3d42c
user:        Augie Fackler <durin42@gmail.com>
date:        Mon Mar 30 10:24:27 2009 -0500
summary:     Fixing syntax highlighting in emacs.

diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -60,12 +60,12 @@
         self.revmap[revnum, branch] = node_hash
 
     def last_known_revision(self):
-        ''' Obtain the highest numbered -- i.e. latest -- revision known.
+        """Obtain the highest numbered -- i.e. latest -- revision known.
 
         Currently, this function just iterates over the entire revision map
         using the max() builtin. This may be slow for extremely large
         repositories, but for now, it's fast enough.
-        '''
+        """
         try:
             return max(k[0] for k in self.revmap.iterkeys())
         except ValueError:

changeset:   228:f71af18c4379
parent:      223:330f0b15d417
parent:      227:d79843a3d42c
user:        Augie Fackler <durin42@gmail.com>
date:        Tue Apr 07 13:48:58 2009 -0500
summary:     Merge with crew.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -37,7 +37,7 @@
     try:
         return svncommand.svncmd(ui, repo, subcommand, *args, **opts)
     except core.SubversionException, e:
-        if e.apr_err == 230001:
+        if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
             raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
                      'Please try running svn ls on that url first.')
         raise
@@ -60,7 +60,7 @@
     try:
         res = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path, **opts)
     except core.SubversionException, e:
-        if e.apr_err == 230001:
+        if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
             raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
                      'Please try running svn ls on that url first.')
         raise
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -54,11 +54,10 @@
     if os.path.exists(hg_editor.uuid_file):
         uuid = open(hg_editor.uuid_file).read()
         assert uuid == svn.uuid
-        start = int(open(hg_editor.last_revision_handled_file, 'r').read())
+        start = hg_editor.last_known_revision()
     else:
         open(hg_editor.uuid_file, 'w').write(svn.uuid)
         open(hg_editor.svn_url_file, 'w').write(svn_url)
-        open(hg_editor.last_revision_handled_file, 'w').write(str(0))
         initializing_repo = True
         start = skipto_rev
 
@@ -92,20 +91,12 @@
                     else:
                         stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
                     converted = True
-                    tmpfile = '%s_tmp' % hg_editor.last_revision_handled_file
-                    fp = open(tmpfile, 'w')
-                    fp.write(str(r.revnum))
-                    fp.close()
-                    merc_util.rename(tmpfile,
-                                     hg_editor.last_revision_handled_file)
                 except core.SubversionException, e: #pragma: no cover
-                    if hasattr(e, 'message') and (
-                        'Server sent unexpected return value (502 Bad Gateway)'
-                        ' in response to PROPFIND') in e.message:
+                    if e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED:
                         tries += 1
                         ui.status('Got a 502, retrying (%s)\n' % tries)
                     else:
-                        raise
+                        raise merc_util.Abort(*e.args)
     util.swap_out_encoding(old_encoding)
 
 fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
@@ -249,7 +240,7 @@
     except svnwrap.SubversionRepoCanNotDiff:
         raise BadPatchApply('subversion diffing code is not supported')
     except core.SubversionException, e:
-        if (hasattr(e, 'apr_err') and e.apr_err != 160013):
+        if (hasattr(e, 'apr_err') and e.apr_err != core.SVN_ERR_FS_NOT_FOUND):
             raise
         raise BadPatchApply('previous revision does not exist')
     if '\0' in d:
@@ -610,7 +601,7 @@
                     ui, svn, hg_editor, b, r, parentctx)
             except BadPatchApply, e:
                 # Either this revision or the previous one does not exist.
-                ui.status("fetching entire rev: %s.\n" % e.message)
+                ui.status("Fetching entire revision: %s.\n" % e.args[0])
                 files_touched, filectxfn2 = stupid_fetch_branchrev(
                     svn, hg_editor, b, branches[b], r, parentctx)
 
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -59,6 +59,18 @@
         f.close()
         self.revmap[revnum, branch] = node_hash
 
+    def last_known_revision(self):
+        """Obtain the highest numbered -- i.e. latest -- revision known.
+
+        Currently, this function just iterates over the entire revision map
+        using the max() builtin. This may be slow for extremely large
+        repositories, but for now, it's fast enough.
+        """
+        try:
+            return max(k[0] for k in self.revmap.iterkeys())
+        except ValueError:
+            return 0
+
     def __init__(self, path=None, repo=None, ui_=None,
                  subdir='', author_host='',
                  tag_locations=['tags'],
@@ -133,7 +145,6 @@
             assert os.path.isfile(self.revmap_file)
             assert os.path.isfile(self.svn_url_file)
             assert os.path.isfile(self.uuid_file)
-            assert os.path.isfile(self.last_revision_handled_file)
         else:
             self.repo = hg.repository(self.ui, repo_path, create=True)
             os.makedirs(os.path.dirname(self.uuid_file))
@@ -523,6 +534,7 @@
         branches = {}
         for path, entry in self.externals.iteritems():
             if not self._is_path_valid(path):
+                self.ui.warn('WARNING: Invalid path %s in externals\n' % path)
                 continue
             p, b, bp = self._split_branch_path(path)
             if bp not in branches:
@@ -692,6 +704,14 @@
             return self.authors[author]
         return '%s%s' %(author, self.author_host)
 
+    def svnauthorforauthor(self, author):
+        for svnauthor, hgauthor in self.authors.iteritems():
+            if author == hgauthor:
+                return svnauthor
+        else:
+            # return the original svn-side author
+            return author.rsplit('@', 1)[0]
+
     def readauthors(self, authorfile):
         self.ui.note(('Reading authormap from %s\n') % authorfile)
         f = open(authorfile, 'r')
@@ -777,10 +797,6 @@
         return self.meta_file_named('uuid')
     uuid_file = property(uuid_file)
 
-    def last_revision_handled_file(self):
-        return self.meta_file_named('last_rev')
-    last_revision_handled_file = property(last_revision_handled_file)
-
     def branch_info_file(self):
         return self.meta_file_named('branch_info')
     branch_info_file = property(branch_info_file)
@@ -853,6 +869,8 @@
                     baserev = self.current_rev.revnum - 1
                 parent = self.get_parent_revision(baserev + 1, branch)
                 self.load_base_from_ctx(path, fpath, self.repo.changectx(parent))
+        else:
+            self.ui.warn('WARNING: Opening non-existant file %s\n' % path)
     open_file = stash_exception_on_self(open_file)
 
     def aresamefiles(self, parentctx, childctx, files):
@@ -1038,11 +1056,10 @@
                 if not window:
                     self.current_files[self.current_file] = target.getvalue()
             except core.SubversionException, e: #pragma: no cover
-                if e.message == 'Delta source ended unexpectedly':
+                if e.apr_err == core.SVN_ERR_INCOMPLETE_DATA:
                     self.missing_plaintexts.add(self.current_file)
                 else: #pragma: no cover
-                    self._exception_info = sys.exc_info()
-                    raise
+                    raise util.Abort(*e.args)
             except: #pragma: no cover
                 print len(base), self.current_file
                 self._exception_info = sys.exc_info()
diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -79,9 +79,6 @@
                 if c.branch() == 'closed-branches':
                     if branch in branchinfo:
                         del branchinfo[branch]
-    lastrevfile = open(os.path.join(svnmetadir, 'last_rev'), 'w')
-    lastrevfile.write(str(last_rev))
-    lastrevfile.close()
     branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
     pickle.dump(branchinfo, branchinfofile)
     branchinfofile.close()
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -5,7 +5,6 @@
 
 from mercurial import hg
 from mercurial import node
-from mercurial import util as merc_util
 
 import svnwrap
 import util
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -189,7 +189,7 @@
         try:
             tags = self.list_dir('tags', revision=revision).keys()
         except core.SubversionException, e:
-            if e.apr_err == 160013:
+            if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
                 return {}
             raise
         tag_info = {}
@@ -370,10 +370,8 @@
                       e_baton, self.pool)
         except core.SubversionException, e: #pragma: no cover
             # can I depend on this number being constant?
-            if (e.message == "Server doesn't support the replay command"
-                or e.apr_err == 170003
-                or e.message == 'The requested report is unknown.'
-                or e.apr_err == 200007):
+            if (e.apr_err == core.SVN_ERR_RA_NOT_IMPLEMENTED or
+                e.apr_err == core.SVN_ERR_UNSUPPORTED_FEATURE):
                 raise SubversionRepoCanNotReplay, ('This Subversion server '
                    'is older than 1.4.0, and cannot satisfy replay requests.')
             else:
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -89,7 +89,7 @@
     if url[-1] == '/':
         url = url[:-1]
     url = '%s%s' % (url, branchpath)
-    author = '@'.join(parent.user().split('@')[:-1])
+    author = hge.svnauthorforauthor(parent.user())
     # cleverly figure out repo root w/o actually contacting the server
     reporoot = url[:len(url)-len(subdir)]
     ui.status('''URL: %(url)s

changeset:   229:7f20914e52e8
user:        Augie Fackler <durin42@gmail.com>
date:        Tue Mar 31 15:53:41 2009 -0500
summary:     Better 502 logic that does not catch non-502 errors.

diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -77,7 +77,7 @@
             # got a 502? Try more than once!
             tries = 0
             converted = False
-            while not converted and tries < 3:
+            while not converted:
                 try:
                     util.describe_revision(ui, r)
                     if have_replay:
@@ -92,7 +92,9 @@
                         stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
                     converted = True
                 except core.SubversionException, e: #pragma: no cover
-                    if e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED:
+                    if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
+                        and '502' in str(e)
+                        and tries < 3):
                         tries += 1
                         ui.status('Got a 502, retrying (%s)\n' % tries)
                     else:

changeset:   230:4c3bad24f950
user:        Augie Fackler <durin42@gmail.com>
date:        Mon Mar 30 16:11:45 2009 -0500
summary:     rebase: cleanup slightly.

diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -82,7 +82,7 @@
                 if ctx.node() == oldest:
                     return
                 extra['branch'] = ctx.branch()
-            utility_commands.rebase_commits(ui, repo, hg_repo_path,
+            utility_commands.rebase_commits(ui, repo,
                                             extrafn=extrafn,
                                             sourcerev=needs_transplant,
                                             **opts)
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -130,7 +130,7 @@
 print_parent_revision = util.register_subcommand('parent')(print_parent_revision)
 
 
-def rebase_commits(ui, repo, hg_repo_path, extrafn=None, sourcerev=None, **opts):
+def rebase_commits(ui, repo, extrafn=None, sourcerev=None, **opts):
     """rebase current unpushed revisions onto the Subversion head
 
     This moves a line of development from making its own head to the top of
@@ -146,8 +146,7 @@
         extrafn = extrafn2
     if sourcerev is None:
         sourcerev = repo.parents()[0].node()
-    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                           ui_=ui)
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
     o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, sourcerev=sourcerev)

changeset:   232:c0063328587f
user:        Augie Fackler <durin42@gmail.com>
date:        Tue Apr 07 17:38:13 2009 -0500
summary:     Fix and test for directory deletes during branch creation.

diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -845,7 +845,7 @@
                 # assuming it is a directory
                 self.externals[path] = None
                 map(self.delete_file, [pat for pat in self.current_files.iterkeys()
-                                       if pat.startswith(path)])
+                                       if pat.startswith(path+'/')])
                 for f in ctx.walk(our_util.PrefixMatch(br_path2)):
                     f_p = '%s/%s' % (path, f[len(br_path2):])
                     if f_p not in self.current_files:

changeset:   233:80a700398995
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Mon Apr 06 02:50:22 2009 -0400
summary:     Fix SubversionRepo to actually use provided username

diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -120,7 +120,7 @@
     """
     def __init__(self, url='', username=''):
         self.svn_url = url
-        self.uname = username
+        self.username = username
         self.auth_baton_pool = core.Pool()
         self.auth_baton = _create_auth_baton(self.auth_baton_pool)
 
@@ -139,6 +139,10 @@
         """
         # while we're in here we'll recreate our pool
         self.pool = core.Pool()
+        if self.username:
+            core.svn_auth_set_parameter(self.auth_baton,
+                                        core.SVN_AUTH_PARAM_DEFAULT_USERNAME,
+                                        self.username)
         self.client_context = client.create_context()
 
         self.client_context.auth_baton = self.auth_baton

changeset:   234:33e885f5f86a
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Mon Apr 06 02:52:14 2009 -0400
summary:     Add --username and --password options to all commands

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -79,6 +79,8 @@
           ('', 'filemap', '',
            'remap file to exclude paths or include only certain paths'),
           ('', 'force', False, 'force an operation to happen'),
+          ('', 'username', '', 'username for authentication'),
+          ('', 'password', '', 'password for authentication'),
           ],
          svncommand.generate_help(),
          ),
@@ -90,6 +92,8 @@
           ('A', 'authors', '', 'username mapping filename'),
           ('', 'filemap', '',
            'remap file to exclude paths or include only certain paths'),
+          ('', 'username', '', 'username for authentication'),
+          ('', 'password', '', 'password for authentication'),
          ],
          'hg svnclone source [dest]'),
 }
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -41,7 +41,9 @@
                   ' of SWIG.\n')
         have_replay = False
     initializing_repo = False
-    svn = svnwrap.SubversionRepo(svn_url, username=merc_util.getuser())
+    user = opts.get('username', merc_util.getuser())
+    passwd = opts.get('password', '')
+    svn = svnwrap.SubversionRepo(svn_url, user, passwd)
     author_host = "@%s" % svn.uuid
     tag_locations = tag_locations.split(',')
     hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -29,6 +29,8 @@
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
+    user = opts.get('username', merc_util.getuser())
+    passwd = opts.get('password', '')
     # Strategy:
     # 1. Find all outgoing commits from this head
     if len(repo.parents()) != 1:
@@ -59,14 +61,16 @@
         # 2. Commit oldest revision that needs to be pushed
         base_revision = svn_commit_hashes[base_n][0]
         try:
-            commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision)
+            commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision,
+                            user, passwd)
         except NoFilesException:
             ui.warn("Could not push revision %s because it had no changes in svn.\n" %
                      old_ctx)
             return 1
         # 3. Fetch revisions from svn
         r = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path,
-                                          stupid=stupid)
+                                          stupid=stupid, username=user,
+                                          password=passwd)
         assert not r or r == 0
         # 4. Find the new head of the target branch
         repo = hg.repository(ui, hge.path)
@@ -182,11 +186,12 @@
         ext.read(ctx['.hgsvnexternals'].data())
     return ext
 
-def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision):
+def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision,
+                    username, password):
     """Build and send a commit from Mercurial to Subversion.
     """
     file_data = {}
-    svn = svnwrap.SubversionRepo(svn_url, username=merc_util.getuser())
+    svn = svnwrap.SubversionRepo(svn_url, username, password)
     parent = rev_ctx.parents()[0]
     parent_branch = rev_ctx.parents()[0].branch()
     branch_path = 'trunk'
diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -14,7 +14,9 @@
         raise mutil.Abort('You must pass the svn URI used to create this repo.')
     uuid = None
     url = args[0].rstrip('/')
-    svn = svnwrap.SubversionRepo(url=url)
+    user = opts.get('username', mutil.getuser())
+    passwd = opts.get('password', '')
+    svn = svnwrap.SubversionRepo(url, user, passwd)
     subdir = svn.subdir
     svnmetadir = os.path.join(repo.path, 'svn')
     if not os.path.exists(svnmetadir):
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -118,9 +118,10 @@
     This uses the SWIG Python bindings, and will only work on svn >= 1.4.
     It takes a required param, the URL.
     """
-    def __init__(self, url='', username=''):
+    def __init__(self, url='', username='', password=''):
         self.svn_url = url
         self.username = username
+        self.password = password
         self.auth_baton_pool = core.Pool()
         self.auth_baton = _create_auth_baton(self.auth_baton_pool)
 
@@ -143,6 +144,10 @@
             core.svn_auth_set_parameter(self.auth_baton,
                                         core.SVN_AUTH_PARAM_DEFAULT_USERNAME,
                                         self.username)
+        if self.password:
+            core.svn_auth_set_parameter(self.auth_baton,
+                                        core.SVN_AUTH_PARAM_DEFAULT_PASSWORD,
+                                        self.password)
         self.client_context = client.create_context()
 
         self.client_context.auth_baton = self.auth_baton
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -50,7 +50,9 @@
     url = hge.url
     if url[-1] == '/':
         url = url[:-1]
-    svn = svnwrap.SubversionRepo(url)
+    user = opts.get('username', mutil.getuser())
+    passwd = opts.get('passwd', '')
+    svn = svnwrap.SubversionRepo(url, user, passwd)
     dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd']
     for dir in dirs:
         props = svn.list_props('%s/%s/' % (branchpath,dir), r)

changeset:   235:2969a20e0eef
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Mon Apr 06 11:19:51 2009 -0400
summary:     Add support for user:pass@url repositories to be hg-like

diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -5,6 +5,7 @@
 import sys
 import tempfile
 import hashlib
+import urlparse
 
 from svn import client
 from svn import core
@@ -91,6 +92,20 @@
 
     return core.svn_auth_open(providers, pool)
 
+def parse_url(url):
+    """Parse a URL and return a tuple (username, password, url)
+    """
+    scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
+    user, passwd = None, None
+    if '@' in netloc:
+        userpass, netloc = netloc.split('@')
+        if ':' in userpass:
+            user, passwd = userpass.split(':')
+            user, passwd = urlparse.unquote(user) or None, urlparse.unquote(passwd) or None
+        else:
+            user = urlparse.unquote(userpass) or None
+    url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
+    return (user, passwd, url)
 
 class Revision(object):
     """Wrapper for a Subversion revision.
@@ -119,9 +134,11 @@
     It takes a required param, the URL.
     """
     def __init__(self, url='', username='', password=''):
-        self.svn_url = url
-        self.username = username
-        self.password = password
+        parsed = parse_url(url)
+        # --username and --password override URL credentials
+        self.username = username or parsed[0]
+        self.password = password or parsed[1]
+        self.svn_url = parsed[2]
         self.auth_baton_pool = core.Pool()
         self.auth_baton = _create_auth_baton(self.auth_baton_pool)
 

changeset:   236:c34abd2448b7
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Tue Apr 07 22:15:22 2009 -0400
summary:     Issue #60: Add a svn sub-command to list all authors in a Subversion repository

diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -198,6 +198,26 @@
 show_outgoing_to_svn = util.register_subcommand('outgoing')(show_outgoing_to_svn)
 
 
+def list_authors(ui, args, authors=None, **opts):
+    """list all authors in a Subversion repository
+    """
+    if not len(args):
+        ui.status('No repository specified.\n')
+        return
+    svn = svnwrap.SubversionRepo(util.normalize_url(args[0]))
+    author_set = set()
+    for rev in svn.revisions():
+        author_set.add(str(rev.author)) # So None becomes 'None'
+    if authors:
+        authorfile = open(authors, 'w')
+        authorfile.write('%s=\n' % '=\n'.join(sorted(author_set)))
+        authorfile.close()
+    else:
+        ui.status('%s\n' % '\n'.join(sorted(author_set)))
+list_authors = util.register_subcommand('listauthors')(list_authors)
+list_authors = util.command_needs_no_url(list_authors)
+
+
 def version(ui, **opts):
     """Show current version of hg and hgsubversion.
     """

changeset:   237:c90cfa665b81
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 13:07:23 2009 +0200
summary:     Cope with date-less revisions.

diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -586,8 +586,8 @@
                         break
                 if not is_closed:
                     deleted_branches[branch] = branchtip
-    date = r.date.replace('T', ' ').replace('Z', '').split('.')[0]
-    date += ' -0000'
+
+    date = hg_editor.fixdate(r.date)
     check_deleted_branches = set()
     for b in branches:
         parentctx = hg_editor.repo[hg_editor.get_parent_revision(r.revnum, b)]
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -129,11 +129,22 @@
             self.readauthors(authors)
         if self.authors:
             self.writeauthors()
+
+        self.lastdate = '1970-01-01 00:00:00'
         self.includepaths = {}
         self.excludepaths = {}
         if filemap and os.path.exists(filemap):
             self.readfilemap(filemap)
 
+    def fixdate(self, date):
+        if date is not None:
+            date = date.replace('T', ' ').replace('Z', '').split('.')[0]
+            date += ' -0000'
+            self.lastdate = date
+        else:
+            date = self.lastdate
+        return date
+
     def __setup_repo(self, repo_path):
         """Verify the repo is going to work out for us.
 
@@ -573,8 +584,7 @@
         files_to_commit.sort()
         branch_batches = {}
         rev = self.current_rev
-        date = rev.date.replace('T', ' ').replace('Z', '').split('.')[0]
-        date += ' -0000'
+        date = self.fixdate(rev.date)
 
         # build up the branches that have files on them
         for f in files_to_commit:

changeset:   238:e8b3ca865f93
user:        Augie Fackler <durin42@gmail.com>
date:        Wed Apr 08 10:28:56 2009 -0500
summary:     Fix a boneheaded mistake I made when I touched up a previous patch.

diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -4,8 +4,8 @@
 import shutil
 import sys
 import tempfile
-import hashlib
 import urlparse
+import urllib
 
 from svn import client
 from svn import core
@@ -101,9 +101,9 @@
         userpass, netloc = netloc.split('@')
         if ':' in userpass:
             user, passwd = userpass.split(':')
-            user, passwd = urlparse.unquote(user) or None, urlparse.unquote(passwd) or None
+            user, passwd = urllib.unquote(user) or None, urllib.unquote(passwd) or None
         else:
-            user = urlparse.unquote(userpass) or None
+            user = urllib.unquote(userpass) or None
     url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
     return (user, passwd, url)
 

changeset:   239:e2214c8fc91f
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 17:34:01 2009 +0200
summary:     Put all stupid stuff in it's own module (separate from fetch-command).

diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -1,26 +1,15 @@
-import cStringIO
-import re
 import os
 
-from mercurial import patch
-from mercurial import node
-from mercurial import context
-from mercurial import revlog
 from mercurial import util as merc_util
 from svn import core
 from svn import delta
 
 import hg_delta_editor
 import svnwrap
-import svnexternals
+import stupid as stupidmod
 import util
 
 
-def print_your_svn_is_old_message(ui): #pragma: no cover
-    ui.status("In light of that, I'll fall back and do diffs, but it won't do "
-              "as good a job. You should really upgrade your server.\n")
-
-
 def fetch_revisions(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
                     tag_locations='tags',
                     authors=None,
@@ -87,11 +76,11 @@
                             replay_convert_rev(hg_editor, svn, r)
                         except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
                             ui.status('%s\n' % e.message)
-                            print_your_svn_is_old_message(ui)
+                            stupidmod.print_your_svn_is_old_message(ui)
                             have_replay = False
-                            stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
+                            stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
                     else:
-                        stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
+                        stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
                     converted = True
                 except core.SubversionException, e: #pragma: no cover
                     if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
@@ -141,552 +130,3 @@
         hg_editor.missing_plaintexts = set()
         hg_editor.ui.note('\n')
     hg_editor.commit_current_delta()
-
-
-binary_file_re = re.compile(r'''Index: ([^\n]*)
-=*
-Cannot display: file marked as a binary type.''')
-
-property_exec_set_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Added|Name): svn:executable
-   \+''')
-
-property_exec_removed_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Deleted|Name): svn:executable
-   -''')
-
-empty_file_patch_wont_make_re = re.compile(r'''Index: ([^\n]*)\n=*\n(?=Index:)''')
-
-any_file_re = re.compile(r'''^Index: ([^\n]*)\n=*\n''', re.MULTILINE)
-
-property_special_set_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Added|Name): svn:special
-   \+''')
-
-property_special_removed_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Deleted|Name): svn:special
-   \-''')
-
-def mempatchproxy(parentctx, files):
-    # Avoid circular references patch.patchfile -> mempatch
-    patchfile = patch.patchfile
-
-    class mempatch(patchfile):
-        def __init__(self, ui, fname, opener, missing=False):
-            patchfile.__init__(self, ui, fname, None, False)
-
-        def readlines(self, fname):
-            if fname not in parentctx:
-                raise IOError('Cannot find %r to patch' % fname)
-            fctx = parentctx[fname]
-            data = fctx.data()
-            if 'l' in fctx.flags():
-                data = 'link ' + data
-            return cStringIO.StringIO(data).readlines()
-
-        def writelines(self, fname, lines):
-            files[fname] = ''.join(lines)
-
-        def unlink(self, fname):
-            files[fname] = None
-
-    return mempatch
-
-
-def filteriterhunks(hg_editor):
-    iterhunks = patch.iterhunks
-    def filterhunks(ui, fp, sourcefile=None):
-        applycurrent = False
-        for data in iterhunks(ui, fp, sourcefile):
-            if data[0] == 'file':
-                if hg_editor._is_file_included(data[1][1]):
-                    applycurrent = True
-                else:
-                    applycurrent = False
-            assert data[0] != 'git', 'Filtering git hunks not supported.'
-            if applycurrent:
-                yield data
-    return filterhunks
-
-def stupid_diff_branchrev(ui, svn, hg_editor, branch, r, parentctx):
-    """Extract all 'branch' content at a given revision.
-
-    Return a tuple (files, filectxfn) where 'files' is the list of all files
-    in the branch at the given revision, and 'filectxfn' is a memctx compatible
-    callable to retrieve individual file information. Raise BadPatchApply upon
-    error.
-    """
-    def make_diff_path(branch):
-        if branch == 'trunk' or branch is None:
-            return 'trunk'
-        elif branch.startswith('../'):
-            return branch[3:]
-        return 'branches/%s' % branch
-    parent_rev, br_p = hg_editor.get_parent_svn_branch_and_rev(r.revnum, branch)
-    diff_path = make_diff_path(branch)
-    try:
-        if br_p == branch:
-            # letting patch handle binaries sounded
-            # cool, but it breaks patch in sad ways
-            d = svn.get_unified_diff(diff_path, r.revnum, deleted=False,
-                                     ignore_type=False)
-        else:
-            d = svn.get_unified_diff(diff_path, r.revnum,
-                                     other_path=make_diff_path(br_p),
-                                     other_rev=parent_rev,
-                                     deleted=True, ignore_type=True)
-            if d:
-                raise BadPatchApply('branch creation with mods')
-    except svnwrap.SubversionRepoCanNotDiff:
-        raise BadPatchApply('subversion diffing code is not supported')
-    except core.SubversionException, e:
-        if (hasattr(e, 'apr_err') and e.apr_err != core.SVN_ERR_FS_NOT_FOUND):
-            raise
-        raise BadPatchApply('previous revision does not exist')
-    if '\0' in d:
-        raise BadPatchApply('binary diffs are not supported')
-    files_data = {}
-    binary_files = {}
-    touched_files = {}
-    for m in binary_file_re.findall(d):
-        # we have to pull each binary file by hand as a fulltext,
-        # which sucks but we've got no choice
-        binary_files[m] = 1
-        touched_files[m] = 1
-    d2 = empty_file_patch_wont_make_re.sub('', d)
-    d2 = property_exec_set_re.sub('', d2)
-    d2 = property_exec_removed_re.sub('', d2)
-    for f in any_file_re.findall(d):
-        # Here we ensure that all files, including the new empty ones
-        # are marked as touched. Content is loaded on demand.
-        touched_files[f] = 1
-    if d2.strip() and len(re.findall('\n[-+]', d2.strip())) > 0:
-        try:
-            oldpatchfile = patch.patchfile
-            olditerhunks = patch.iterhunks
-            patch.patchfile = mempatchproxy(parentctx, files_data)
-            patch.iterhunks = filteriterhunks(hg_editor)
-            try:
-                # We can safely ignore the changed list since we are
-                # handling non-git patches. Touched files are known
-                # by our memory patcher.
-                patch_st = patch.applydiff(ui, cStringIO.StringIO(d2),
-                                           {}, strip=0)
-            finally:
-                patch.patchfile = oldpatchfile
-                patch.iterhunks = olditerhunks
-        except patch.PatchError:
-            # TODO: this happens if the svn server has the wrong mime
-            # type stored and doesn't know a file is binary. It would
-            # be better to do one file at a time and only do a
-            # full fetch on files that had problems.
-            raise BadPatchApply('patching failed')
-        for x in files_data.iterkeys():
-            ui.note('M  %s\n' % x)
-        # if this patch didn't apply right, fall back to exporting the
-        # entire rev.
-        if patch_st == -1:
-            assert False, ('This should only happen on case-insensitive'
-                           ' volumes.')
-        elif patch_st == 1:
-            # When converting Django, I saw fuzz on .po files that was
-            # causing revisions to end up failing verification. If that
-            # can be fixed, maybe this won't ever be reached.
-            raise BadPatchApply('patching succeeded with fuzz')
-    else:
-        ui.status('Not using patch for %s, diff had no hunks.\n' %
-                  r.revnum)
-
-    exec_files = {}
-    for m in property_exec_removed_re.findall(d):
-        exec_files[m] = False
-    for m in property_exec_set_re.findall(d):
-        exec_files[m] = True
-    for m in exec_files:
-        touched_files[m] = 1
-    link_files = {}
-    for m in property_special_set_re.findall(d):
-        # TODO(augie) when a symlink is removed, patching will fail.
-        # We're seeing that above - there's gotta be a better
-        # workaround than just bailing like that.
-        assert m in files_data
-        link_files[m] = True
-    for m in property_special_removed_re.findall(d):
-        assert m in files_data
-        link_files[m] = False
-
-    for p in r.paths:
-        if p.startswith(diff_path) and r.paths[p].action == 'D':
-            p2 = p[len(diff_path)+1:].strip('/')
-            if p2 in parentctx:
-                files_data[p2] = None
-                continue
-            # If this isn't in the parent ctx, it must've been a dir
-            files_data.update([(f, None) for f in parentctx if f.startswith(p2 + '/')])
-
-    for f in files_data:
-        touched_files[f] = 1
-
-    copies = getcopies(svn, hg_editor, branch, diff_path, r, touched_files,
-                       parentctx)
-
-    def filectxfn(repo, memctx, path):
-        if path in files_data and files_data[path] is None:
-            raise IOError()
-
-        if path in binary_files:
-            data, mode = svn.get_file(diff_path + '/' + path, r.revnum)
-            isexe = 'x' in mode
-            islink = 'l' in mode
-        else:
-            isexe = exec_files.get(path, 'x' in parentctx.flags(path))
-            islink = link_files.get(path, 'l' in parentctx.flags(path))
-            data = ''
-            if path in files_data:
-                data = files_data[path]
-                if islink:
-                    data = data[len('link '):]
-            elif path in parentctx:
-                data = parentctx[path].data()
-
-        copied = copies.get(path)
-        return context.memfilectx(path=path, data=data, islink=islink,
-                                  isexec=isexe, copied=copied)
-
-    return list(touched_files), filectxfn
-
-def makecopyfinder(r, branchpath, rootdir):
-    """Return a function detecting copies.
-
-    Returned copyfinder(path) returns None if no copy information can
-    be found or ((source, sourcerev), sourcepath) where "sourcepath" is the
-    copy source path, "sourcerev" the source svn revision and "source" is the
-    copy record path causing the copy to occur. If a single file was copied
-    "sourcepath" and "source" are the same, while file copies dectected from
-    directory copies return the copied source directory in "source".
-    """
-    # filter copy information for current branch
-    branchpath = branchpath + '/'
-    fullbranchpath = rootdir + branchpath
-    copies = []
-    for path, e in r.paths.iteritems():
-        if not e.copyfrom_path:
-            continue
-        if not path.startswith(branchpath):
-            continue
-        if not e.copyfrom_path.startswith(fullbranchpath):
-            # ignore cross branch copies
-            continue
-        dest = path[len(branchpath):]
-        source = e.copyfrom_path[len(fullbranchpath):]
-        copies.append((dest, (source, e.copyfrom_rev)))
-
-    copies.sort(reverse=True)
-    exactcopies = dict(copies)
-
-    def finder(path):
-        if path in exactcopies:
-            return exactcopies[path], exactcopies[path][0]
-        # look for parent directory copy, longest first
-        for dest, (source, sourcerev) in copies:
-            dest = dest + '/'
-            if not path.startswith(dest):
-                continue
-            sourcepath = source + '/' + path[len(dest):]
-            return (source, sourcerev), sourcepath
-        return None
-
-    return finder
-
-def getcopies(svn, hg_editor, branch, branchpath, r, files, parentctx):
-    """Return a mapping {dest: source} for every file copied into r.
-    """
-    if parentctx.node() == revlog.nullid:
-        return {}
-
-    # Extract svn copy information, group them by copy source.
-    # The idea is to duplicate the replay behaviour where copies are
-    # evaluated per copy event (one event for all files in a directory copy,
-    # one event for single file copy). We assume that copy events match
-    # copy sources in revision info.
-    svncopies = {}
-    finder = makecopyfinder(r, branchpath, svn.subdir)
-    for f in files:
-        copy = finder(f)
-        if copy:
-            svncopies.setdefault(copy[0], []).append((f, copy[1]))
-    if not svncopies:
-        return {}
-
-    # cache changeset contexts and map them to source svn revisions
-    ctxs = {}
-    def getctx(svnrev):
-        if svnrev in ctxs:
-            return ctxs[svnrev]
-        changeid = hg_editor.get_parent_revision(svnrev + 1, branch)
-        ctx = None
-        if changeid != revlog.nullid:
-            ctx = hg_editor.repo.changectx(changeid)
-        ctxs[svnrev] = ctx
-        return ctx
-
-    # check svn copies really make sense in mercurial
-    hgcopies = {}
-    for (sourcepath, rev), copies in svncopies.iteritems():
-        sourcectx = getctx(rev)
-        if sourcectx is None:
-            continue
-        sources = [s[1] for s in copies]
-        if not hg_editor.aresamefiles(sourcectx, parentctx, sources):
-            continue
-        hgcopies.update(copies)
-    return hgcopies
-
-def stupid_fetch_externals(svn, branchpath, r, parentctx):
-    """Extract svn:externals for the current revision and branch
-
-    Return an externalsfile instance or None if there are no externals
-    to convert and never were.
-    """
-    externals = svnexternals.externalsfile()
-    if '.hgsvnexternals' in parentctx:
-        externals.read(parentctx['.hgsvnexternals'].data())
-    # Detect property additions only, changes are handled by checking
-    # existing entries individually. Projects are unlikely to store
-    # externals on many different root directories, so we trade code
-    # duplication and complexity for a constant lookup price at every
-    # revision in the common case.
-    dirs = set(externals)
-    if parentctx.node() == revlog.nullid:
-        dirs.update([p for p,k in svn.list_files(branchpath, r.revnum) if k == 'd'])
-        dirs.add('')
-    else:
-        branchprefix = branchpath + '/'
-        for path, e in r.paths.iteritems():
-            if e.action == 'D':
-                continue
-            if not path.startswith(branchprefix) and path != branchpath:
-                continue
-            kind = svn.checkpath(path, r.revnum)
-            if kind != 'd':
-                continue
-            path = path[len(branchprefix):]
-            dirs.add(path)
-            if e.action == 'M' or (e.action == 'A' and e.copyfrom_path):
-                # Do not recurse in copied directories, changes are marked
-                # as 'M', except for the copied one.
-                continue
-            for child, k in svn.list_files(branchprefix + path, r.revnum):
-                if k == 'd':
-                    dirs.add((path + '/' + child).strip('/'))
-
-    # Retrieve new or updated values
-    for dir in dirs:
-        try:
-            values = svn.list_props(branchpath + '/' + dir, r.revnum)
-            externals[dir] = values.get('svn:externals', '')
-        except IOError:
-            externals[dir] = ''
-
-    if not externals and '.hgsvnexternals' not in parentctx:
-        # Do not create empty externals files
-        return None
-    return externals
-
-def stupid_fetch_branchrev(svn, hg_editor, branch, branchpath, r, parentctx):
-    """Extract all 'branch' content at a given revision.
-
-    Return a tuple (files, filectxfn) where 'files' is the list of all files
-    in the branch at the given revision, and 'filectxfn' is a memctx compatible
-    callable to retrieve individual file information.
-    """
-    files = []
-    if parentctx.node() == revlog.nullid:
-        # Initial revision, fetch all files
-        for path, kind in svn.list_files(branchpath, r.revnum):
-            if kind == 'f':
-                files.append(path)
-    else:
-        branchprefix = branchpath + '/'
-        for path, e in r.paths.iteritems():
-            if not path.startswith(branchprefix):
-                continue
-            if not hg_editor._is_path_valid(path):
-                continue
-            kind = svn.checkpath(path, r.revnum)
-            path = path[len(branchprefix):]
-            if kind == 'f':
-                files.append(path)
-            elif kind == 'd':
-                if e.action == 'M':
-                    continue
-                dirpath = branchprefix + path
-                for child, k in svn.list_files(dirpath, r.revnum):
-                    if k == 'f':
-                        files.append(path + '/' + child)
-            else:
-                if path in parentctx:
-                    files.append(path)
-                    continue
-                # Assume it's a deleted directory
-                path = path + '/'
-                deleted = [f for f in parentctx if f.startswith(path)]
-                files += deleted
-
-    copies = getcopies(svn, hg_editor, branch, branchpath, r, files, parentctx)
-
-    def filectxfn(repo, memctx, path):
-        data, mode = svn.get_file(branchpath + '/' + path, r.revnum)
-        isexec = 'x' in mode
-        islink = 'l' in mode
-        copied = copies.get(path)
-        return context.memfilectx(path=path, data=data, islink=islink,
-                                  isexec=isexec, copied=copied)
-
-    return files, filectxfn
-
-def stupid_svn_server_pull_rev(ui, svn, hg_editor, r):
-    # this server fails at replay
-    branches = hg_editor.branches_in_paths(r.paths, r.revnum, svn.checkpath, svn.list_files)
-    deleted_branches = {}
-    brpaths = branches.values()
-    bad_branch_paths = {}
-    for br, bp in branches.iteritems():
-        bad_branch_paths[br] = []
-
-        # This next block might be needed, but for now I'm omitting it until it can be
-        # proven necessary.
-        # for bad in brpaths:
-        #     if bad.startswith(bp) and len(bad) > len(bp):
-        #         bad_branch_paths[br].append(bad[len(bp)+1:])
-
-        # We've go a branch that contains other branches. We have to be careful to
-        # get results similar to real replay in this case.
-        for existingbr in hg_editor.branches:
-            bad = hg_editor._remotename(existingbr)
-            if bad.startswith(bp) and len(bad) > len(bp):
-                bad_branch_paths[br].append(bad[len(bp)+1:])
-    for p in r.paths:
-        if hg_editor._is_path_tag(p):
-            continue
-        branch = hg_editor._localname(p)
-        if r.paths[p].action == 'R' and branch in hg_editor.branches:
-            branchedits = sorted(filter(lambda x: x[0][1] == branch and x[0][0] < r.revnum,
-                                        hg_editor.revmap.iteritems()), reverse=True)
-            is_closed = False
-            if len(branchedits) > 0:
-                branchtip = branchedits[0][1]
-                for child in hg_editor.repo[branchtip].children():
-                    if child.branch() == 'closed-branches':
-                        is_closed = True
-                        break
-                if not is_closed:
-                    deleted_branches[branch] = branchtip
-
-    date = hg_editor.fixdate(r.date)
-    check_deleted_branches = set()
-    for b in branches:
-        parentctx = hg_editor.repo[hg_editor.get_parent_revision(r.revnum, b)]
-        if parentctx.branch() != (b or 'default'):
-            check_deleted_branches.add(b)
-        kind = svn.checkpath(branches[b], r.revnum)
-        if kind != 'd':
-            # Branch does not exist at this revision. Get parent revision and
-            # remove everything.
-            deleted_branches[b] = parentctx.node()
-            continue
-        else:
-            try:
-                files_touched, filectxfn2 = stupid_diff_branchrev(
-                    ui, svn, hg_editor, b, r, parentctx)
-            except BadPatchApply, e:
-                # Either this revision or the previous one does not exist.
-                ui.status("Fetching entire revision: %s.\n" % e.args[0])
-                files_touched, filectxfn2 = stupid_fetch_branchrev(
-                    svn, hg_editor, b, branches[b], r, parentctx)
-
-            externals = stupid_fetch_externals(svn, branches[b], r, parentctx)
-            if externals is not None:
-                files_touched.append('.hgsvnexternals')
-
-            def filectxfn(repo, memctx, path):
-                if path == '.hgsvnexternals':
-                    if not externals:
-                        raise IOError()
-                    return context.memfilectx(path=path, data=externals.write(),
-                                              islink=False, isexec=False, copied=None)
-                for bad in bad_branch_paths[b]:
-                    if path.startswith(bad):
-                        raise IOError()
-                return filectxfn2(repo, memctx, path)
-
-        extra = util.build_extra(r.revnum, b, svn.uuid, svn.subdir)
-        if '' in files_touched:
-            files_touched.remove('')
-        excluded = [f for f in files_touched
-                    if not hg_editor._is_file_included(f)]
-        for f in excluded:
-            files_touched.remove(f)
-        if parentctx.node() != node.nullid or files_touched:
-            # TODO(augie) remove this debug code? Or maybe it's sane to have it.
-            for f in files_touched:
-                if f:
-                    assert f[0] != '/'
-            current_ctx = context.memctx(hg_editor.repo,
-                                         [parentctx.node(), revlog.nullid],
-                                         r.message or util.default_commit_msg,
-                                         files_touched,
-                                         filectxfn,
-                                         hg_editor.authorforsvnauthor(r.author),
-                                         date,
-                                         extra)
-            ha = hg_editor.repo.commitctx(current_ctx)
-            branch = extra.get('branch', None)
-            if not branch in hg_editor.branches:
-                hg_editor.branches[branch] = None, 0, r.revnum
-            hg_editor.add_to_revmap(r.revnum, b, ha)
-            hg_editor._save_metadata()
-            util.describe_commit(ui, ha, b)
-    # These are branches which would have an 'R' status in svn log. This means they were
-    # replaced by some other branch, so we need to verify they get marked as closed.
-    for branch in check_deleted_branches:
-        branchedits = sorted(filter(lambda x: x[0][1] == branch and x[0][0] < r.revnum,
-                                    hg_editor.revmap.iteritems()), reverse=True)
-        is_closed = False
-        if len(branchedits) > 0:
-            branchtip = branchedits[0][1]
-            for child in hg_editor.repo[branchtip].children():
-                if child.branch() == 'closed-branches':
-                    is_closed = True
-                    break
-            if not is_closed:
-                deleted_branches[branch] = branchtip
-    for b, parent in deleted_branches.iteritems():
-        if parent == node.nullid:
-            continue
-        parentctx = hg_editor.repo[parent]
-        files_touched = parentctx.manifest().keys()
-        def filectxfn(repo, memctx, path):
-            raise IOError()
-        closed = node.nullid
-        if 'closed-branches' in hg_editor.repo.branchtags():
-            closed = hg_editor.repo['closed-branches'].node()
-        parents = (parent, closed)
-        current_ctx = context.memctx(hg_editor.repo,
-                                     parents,
-                                     r.message or util.default_commit_msg,
-                                     files_touched,
-                                     filectxfn,
-                                     hg_editor.authorforsvnauthor(r.author),
-                                     date,
-                                     {'branch': 'closed-branches'})
-        ha = hg_editor.repo.commitctx(current_ctx)
-        ui.status('Marked branch %s as closed.\n' % (b or 'default'))
-        hg_editor._save_metadata()
-
-class BadPatchApply(Exception):
-    pass
diff --git a/fetch_command.py b/stupid.py
copy from fetch_command.py
copy to stupid.py
--- a/fetch_command.py
+++ b/stupid.py
@@ -16,133 +16,6 @@
 import util
 
 
-def print_your_svn_is_old_message(ui): #pragma: no cover
-    ui.status("In light of that, I'll fall back and do diffs, but it won't do "
-              "as good a job. You should really upgrade your server.\n")
-
-
-def fetch_revisions(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
-                    tag_locations='tags',
-                    authors=None,
-                    filemap=None,
-                    **opts):
-    """pull new revisions from Subversion
-    """
-    svn_url = util.normalize_url(svn_url)
-    old_encoding = util.swap_out_encoding()
-    skipto_rev=int(skipto_rev)
-    have_replay = not stupid
-    if have_replay and not callable(
-        delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
-        ui.status('You are using old Subversion SWIG bindings. Replay will not'
-                  ' work until you upgrade to 1.5.0 or newer. Falling back to'
-                  ' a slower method that may be buggier. Please upgrade, or'
-                  ' contribute a patch to use the ctypes bindings instead'
-                  ' of SWIG.\n')
-        have_replay = False
-    initializing_repo = False
-    user = opts.get('username', merc_util.getuser())
-    passwd = opts.get('password', '')
-    svn = svnwrap.SubversionRepo(svn_url, user, passwd)
-    author_host = "@%s" % svn.uuid
-    tag_locations = tag_locations.split(',')
-    hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                                 ui_=ui,
-                                                 subdir=svn.subdir,
-                                                 author_host=author_host,
-                                                 tag_locations=tag_locations,
-                                                 authors=authors,
-                                                 filemap=filemap)
-    if os.path.exists(hg_editor.uuid_file):
-        uuid = open(hg_editor.uuid_file).read()
-        assert uuid == svn.uuid
-        start = hg_editor.last_known_revision()
-    else:
-        open(hg_editor.uuid_file, 'w').write(svn.uuid)
-        open(hg_editor.svn_url_file, 'w').write(svn_url)
-        initializing_repo = True
-        start = skipto_rev
-
-    if initializing_repo and start > 0:
-        raise merc_util.Abort('Revision skipping at repository initialization '
-                              'remains unimplemented.')
-
-    # start converting revisions
-    for r in svn.revisions(start=start):
-        valid = True
-        hg_editor.update_branch_tag_map_for_rev(r)
-        for p in r.paths:
-            if hg_editor._is_path_valid(p):
-                valid = True
-                break
-        if valid:
-            # got a 502? Try more than once!
-            tries = 0
-            converted = False
-            while not converted:
-                try:
-                    util.describe_revision(ui, r)
-                    if have_replay:
-                        try:
-                            replay_convert_rev(hg_editor, svn, r)
-                        except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
-                            ui.status('%s\n' % e.message)
-                            print_your_svn_is_old_message(ui)
-                            have_replay = False
-                            stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
-                    else:
-                        stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
-                    converted = True
-                except core.SubversionException, e: #pragma: no cover
-                    if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
-                        and '502' in str(e)
-                        and tries < 3):
-                        tries += 1
-                        ui.status('Got a 502, retrying (%s)\n' % tries)
-                    else:
-                        raise merc_util.Abort(*e.args)
-    util.swap_out_encoding(old_encoding)
-
-fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
-
-
-def cleanup_file_handles(svn, count):
-    if count % 50 == 0:
-        svn.init_ra_and_client()
-
-
-def replay_convert_rev(hg_editor, svn, r):
-    hg_editor.set_current_rev(r)
-    svn.get_replay(r.revnum, hg_editor)
-    i = 1
-    if hg_editor.missing_plaintexts:
-        hg_editor.ui.debug('Fetching %s files that could not use replay.\n' %
-                           len(hg_editor.missing_plaintexts))
-        files_to_grab = set()
-        rootpath = svn.subdir and svn.subdir[1:] or ''
-        for p in hg_editor.missing_plaintexts:
-            hg_editor.ui.note('.')
-            hg_editor.ui.flush()
-            if p[-1] == '/':
-                dirpath = p[len(rootpath):]
-                files_to_grab.update([dirpath + f for f,k in
-                                      svn.list_files(dirpath, r.revnum)
-                                      if k == 'f'])
-            else:
-                files_to_grab.add(p[len(rootpath):])
-        hg_editor.ui.note('\nFetching files...\n')
-        for p in files_to_grab:
-            hg_editor.ui.note('.')
-            hg_editor.ui.flush()
-            cleanup_file_handles(svn, i)
-            i += 1
-            data, mode = svn.get_file(p, r.revnum)
-            hg_editor.set_file(p, data, 'x' in mode, 'l' in mode)
-        hg_editor.missing_plaintexts = set()
-        hg_editor.ui.note('\n')
-    hg_editor.commit_current_delta()
-
-
 binary_file_re = re.compile(r'''Index: ([^\n]*)
 =*
 Cannot display: file marked as a binary type.''')
@@ -171,6 +44,16 @@
 (?:Deleted|Name): svn:special
    \-''')
 
+
+class BadPatchApply(Exception):
+    pass
+
+
+def print_your_svn_is_old_message(ui): #pragma: no cover
+    ui.status("In light of that, I'll fall back and do diffs, but it won't do "
+              "as good a job. You should really upgrade your server.\n")
+
+
 def mempatchproxy(parentctx, files):
     # Avoid circular references patch.patchfile -> mempatch
     patchfile = patch.patchfile
@@ -212,7 +95,8 @@
                 yield data
     return filterhunks
 
-def stupid_diff_branchrev(ui, svn, hg_editor, branch, r, parentctx):
+
+def diff_branchrev(ui, svn, hg_editor, branch, r, parentctx):
     """Extract all 'branch' content at a given revision.
 
     Return a tuple (files, filectxfn) where 'files' is the list of all files
@@ -446,7 +330,7 @@
         hgcopies.update(copies)
     return hgcopies
 
-def stupid_fetch_externals(svn, branchpath, r, parentctx):
+def fetch_externals(svn, branchpath, r, parentctx):
     """Extract svn:externals for the current revision and branch
 
     Return an externalsfile instance or None if there are no externals
@@ -497,7 +381,8 @@
         return None
     return externals
 
-def stupid_fetch_branchrev(svn, hg_editor, branch, branchpath, r, parentctx):
+
+def fetch_branchrev(svn, hg_editor, branch, branchpath, r, parentctx):
     """Extract all 'branch' content at a given revision.
 
     Return a tuple (files, filectxfn) where 'files' is the list of all files
@@ -549,7 +434,7 @@
 
     return files, filectxfn
 
-def stupid_svn_server_pull_rev(ui, svn, hg_editor, r):
+def svn_server_pull_rev(ui, svn, hg_editor, r):
     # this server fails at replay
     branches = hg_editor.branches_in_paths(r.paths, r.revnum, svn.checkpath, svn.list_files)
     deleted_branches = {}
@@ -601,15 +486,15 @@
             continue
         else:
             try:
-                files_touched, filectxfn2 = stupid_diff_branchrev(
+                files_touched, filectxfn2 = diff_branchrev(
                     ui, svn, hg_editor, b, r, parentctx)
             except BadPatchApply, e:
                 # Either this revision or the previous one does not exist.
                 ui.status("Fetching entire revision: %s.\n" % e.args[0])
-                files_touched, filectxfn2 = stupid_fetch_branchrev(
+                files_touched, filectxfn2 = fetch_branchrev(
                     svn, hg_editor, b, branches[b], r, parentctx)
 
-            externals = stupid_fetch_externals(svn, branches[b], r, parentctx)
+            externals = fetch_externals(svn, branches[b], r, parentctx)
             if externals is not None:
                 files_touched.append('.hgsvnexternals')
 
@@ -687,6 +572,3 @@
         ha = hg_editor.repo.commitctx(current_ctx)
         ui.status('Marked branch %s as closed.\n' % (b or 'default'))
         hg_editor._save_metadata()
-
-class BadPatchApply(Exception):
-    pass

changeset:   240:1aa1d2d406d9
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 16:20:14 2009 +0200
summary:     Lose old mode constants, not used anymore.

diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -18,10 +18,6 @@
 # shut up, pyflakes, we must import those
 __x = [print_wc_url, fetch_revisions, commit_from_rev, diff_command, rebuildmeta]
 
-mode755 = (stat.S_IXUSR | stat.S_IXGRP| stat.S_IXOTH | stat.S_IRUSR |
-           stat.S_IRGRP| stat.S_IROTH | stat.S_IWUSR)
-mode644 = (stat.S_IRUSR | stat.S_IRGRP| stat.S_IROTH | stat.S_IWUSR)
-
 
 def svncmd(ui, repo, subcommand, *args, **opts):
     if subcommand not in svn_subcommands:

changeset:   241:4950b18cf949
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 17:49:30 2009 +0200
summary:     Move fetch_command.fetch_revisions() to svncommands.pull().

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -21,7 +21,7 @@
 from svn import core
 
 import svncommand
-import fetch_command
+import svncommands
 import tag_repo
 import util
 
@@ -58,7 +58,7 @@
     should_update = not os.path.exists(hg_repo_path)
     svn_url = util.normalize_url(svn_url)
     try:
-        res = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path, **opts)
+        res = svncommands.pull(ui, svn_url, hg_repo_path, **opts)
     except core.SubversionException, e:
         if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
             raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
diff --git a/cmdutil.py b/cmdutil.py
new file mode 100644
--- /dev/null
+++ b/cmdutil.py
@@ -0,0 +1,32 @@
+
+def replay_convert_rev(hg_editor, svn, r):
+    hg_editor.set_current_rev(r)
+    svn.get_replay(r.revnum, hg_editor)
+    i = 1
+    if hg_editor.missing_plaintexts:
+        hg_editor.ui.debug('Fetching %s files that could not use replay.\n' %
+                           len(hg_editor.missing_plaintexts))
+        files_to_grab = set()
+        rootpath = svn.subdir and svn.subdir[1:] or ''
+        for p in hg_editor.missing_plaintexts:
+            hg_editor.ui.note('.')
+            hg_editor.ui.flush()
+            if p[-1] == '/':
+                dirpath = p[len(rootpath):]
+                files_to_grab.update([dirpath + f for f,k in
+                                      svn.list_files(dirpath, r.revnum)
+                                      if k == 'f'])
+            else:
+                files_to_grab.add(p[len(rootpath):])
+        hg_editor.ui.note('\nFetching files...\n')
+        for p in files_to_grab:
+            hg_editor.ui.note('.')
+            hg_editor.ui.flush()
+            if i % 50 == 0:
+                svn.init_ra_and_client()
+            i += 1
+            data, mode = svn.get_file(p, r.revnum)
+            hg_editor.set_file(p, data, 'x' in mode, 'l' in mode)
+        hg_editor.missing_plaintexts = set()
+        hg_editor.ui.note('\n')
+    hg_editor.commit_current_delta()
diff --git a/fetch_command.py b/fetch_command.py
deleted file mode 100644
--- a/fetch_command.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import os
-
-from mercurial import util as merc_util
-from svn import core
-from svn import delta
-
-import hg_delta_editor
-import svnwrap
-import stupid as stupidmod
-import util
-
-
-def fetch_revisions(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
-                    tag_locations='tags',
-                    authors=None,
-                    filemap=None,
-                    **opts):
-    """pull new revisions from Subversion
-    """
-    svn_url = util.normalize_url(svn_url)
-    old_encoding = util.swap_out_encoding()
-    skipto_rev=int(skipto_rev)
-    have_replay = not stupid
-    if have_replay and not callable(
-        delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
-        ui.status('You are using old Subversion SWIG bindings. Replay will not'
-                  ' work until you upgrade to 1.5.0 or newer. Falling back to'
-                  ' a slower method that may be buggier. Please upgrade, or'
-                  ' contribute a patch to use the ctypes bindings instead'
-                  ' of SWIG.\n')
-        have_replay = False
-    initializing_repo = False
-    user = opts.get('username', merc_util.getuser())
-    passwd = opts.get('password', '')
-    svn = svnwrap.SubversionRepo(svn_url, user, passwd)
-    author_host = "@%s" % svn.uuid
-    tag_locations = tag_locations.split(',')
-    hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                                 ui_=ui,
-                                                 subdir=svn.subdir,
-                                                 author_host=author_host,
-                                                 tag_locations=tag_locations,
-                                                 authors=authors,
-                                                 filemap=filemap)
-    if os.path.exists(hg_editor.uuid_file):
-        uuid = open(hg_editor.uuid_file).read()
-        assert uuid == svn.uuid
-        start = hg_editor.last_known_revision()
-    else:
-        open(hg_editor.uuid_file, 'w').write(svn.uuid)
-        open(hg_editor.svn_url_file, 'w').write(svn_url)
-        initializing_repo = True
-        start = skipto_rev
-
-    if initializing_repo and start > 0:
-        raise merc_util.Abort('Revision skipping at repository initialization '
-                              'remains unimplemented.')
-
-    # start converting revisions
-    for r in svn.revisions(start=start):
-        valid = True
-        hg_editor.update_branch_tag_map_for_rev(r)
-        for p in r.paths:
-            if hg_editor._is_path_valid(p):
-                valid = True
-                break
-        if valid:
-            # got a 502? Try more than once!
-            tries = 0
-            converted = False
-            while not converted:
-                try:
-                    util.describe_revision(ui, r)
-                    if have_replay:
-                        try:
-                            replay_convert_rev(hg_editor, svn, r)
-                        except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
-                            ui.status('%s\n' % e.message)
-                            stupidmod.print_your_svn_is_old_message(ui)
-                            have_replay = False
-                            stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
-                    else:
-                        stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
-                    converted = True
-                except core.SubversionException, e: #pragma: no cover
-                    if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
-                        and '502' in str(e)
-                        and tries < 3):
-                        tries += 1
-                        ui.status('Got a 502, retrying (%s)\n' % tries)
-                    else:
-                        raise merc_util.Abort(*e.args)
-    util.swap_out_encoding(old_encoding)
-
-fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
-
-
-def cleanup_file_handles(svn, count):
-    if count % 50 == 0:
-        svn.init_ra_and_client()
-
-
-def replay_convert_rev(hg_editor, svn, r):
-    hg_editor.set_current_rev(r)
-    svn.get_replay(r.revnum, hg_editor)
-    i = 1
-    if hg_editor.missing_plaintexts:
-        hg_editor.ui.debug('Fetching %s files that could not use replay.\n' %
-                           len(hg_editor.missing_plaintexts))
-        files_to_grab = set()
-        rootpath = svn.subdir and svn.subdir[1:] or ''
-        for p in hg_editor.missing_plaintexts:
-            hg_editor.ui.note('.')
-            hg_editor.ui.flush()
-            if p[-1] == '/':
-                dirpath = p[len(rootpath):]
-                files_to_grab.update([dirpath + f for f,k in
-                                      svn.list_files(dirpath, r.revnum)
-                                      if k == 'f'])
-            else:
-                files_to_grab.add(p[len(rootpath):])
-        hg_editor.ui.note('\nFetching files...\n')
-        for p in files_to_grab:
-            hg_editor.ui.note('.')
-            hg_editor.ui.flush()
-            cleanup_file_handles(svn, i)
-            i += 1
-            data, mode = svn.get_file(p, r.revnum)
-            hg_editor.set_file(p, data, 'x' in mode, 'l' in mode)
-        hg_editor.missing_plaintexts = set()
-        hg_editor.ui.note('\n')
-    hg_editor.commit_current_delta()
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -7,7 +7,7 @@
 import hg_delta_editor
 import svnexternals
 import svnwrap
-import fetch_command
+import svncommands
 import utility_commands
 
 
@@ -68,9 +68,8 @@
                      old_ctx)
             return 1
         # 3. Fetch revisions from svn
-        r = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path,
-                                          stupid=stupid, username=user,
-                                          password=passwd)
+        r = svncommands.pull(ui, svn_url, hg_repo_path, stupid=stupid,
+                             username=user, password=passwd)
         assert not r or r == 0
         # 4. Find the new head of the target branch
         repo = hg.repository(ui, hge.path)
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -10,13 +10,13 @@
 import util
 from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
 # dirty trick to force demandimport to run my decorator anyway.
+from svncommands import pull
 from utility_commands import print_wc_url
-from fetch_command import fetch_revisions
 from push_cmd import commit_from_rev
 from diff_cmd import diff_command
 from rebuildmeta import rebuildmeta
 # shut up, pyflakes, we must import those
-__x = [print_wc_url, fetch_revisions, commit_from_rev, diff_command, rebuildmeta]
+__x = [print_wc_url, pull, commit_from_rev, diff_command, rebuildmeta]
 
 
 def svncmd(ui, repo, subcommand, *args, **opts):
diff --git a/svncommands.py b/svncommands.py
new file mode 100644
--- /dev/null
+++ b/svncommands.py
@@ -0,0 +1,93 @@
+import os
+
+from mercurial import util as hgutil
+from svn import core
+from svn import delta
+
+import hg_delta_editor
+import svnwrap
+import stupid as stupidmod
+import cmdutil
+import util
+
+
+def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
+         tag_locations='tags', authors=None, filemap=None, **opts):
+    """pull new revisions from Subversion
+    """
+    svn_url = util.normalize_url(svn_url)
+    old_encoding = util.swap_out_encoding()
+    skipto_rev=int(skipto_rev)
+    have_replay = not stupid
+    if have_replay and not callable(
+        delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
+        ui.status('You are using old Subversion SWIG bindings. Replay will not'
+                  ' work until you upgrade to 1.5.0 or newer. Falling back to'
+                  ' a slower method that may be buggier. Please upgrade, or'
+                  ' contribute a patch to use the ctypes bindings instead'
+                  ' of SWIG.\n')
+        have_replay = False
+    initializing_repo = False
+    user = opts.get('username', hgutil.getuser())
+    passwd = opts.get('password', '')
+    svn = svnwrap.SubversionRepo(svn_url, user, passwd)
+    author_host = "@%s" % svn.uuid
+    tag_locations = tag_locations.split(',')
+    hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+                                                 ui_=ui,
+                                                 subdir=svn.subdir,
+                                                 author_host=author_host,
+                                                 tag_locations=tag_locations,
+                                                 authors=authors,
+                                                 filemap=filemap)
+    if os.path.exists(hg_editor.uuid_file):
+        uuid = open(hg_editor.uuid_file).read()
+        assert uuid == svn.uuid
+        start = hg_editor.last_known_revision()
+    else:
+        open(hg_editor.uuid_file, 'w').write(svn.uuid)
+        open(hg_editor.svn_url_file, 'w').write(svn_url)
+        initializing_repo = True
+        start = skipto_rev
+
+    if initializing_repo and start > 0:
+        raise hgutil.Abort('Revision skipping at repository initialization '
+                           'remains unimplemented.')
+
+    # start converting revisions
+    for r in svn.revisions(start=start):
+        valid = True
+        hg_editor.update_branch_tag_map_for_rev(r)
+        for p in r.paths:
+            if hg_editor._is_path_valid(p):
+                valid = True
+                break
+        if valid:
+            # got a 502? Try more than once!
+            tries = 0
+            converted = False
+            while not converted:
+                try:
+                    util.describe_revision(ui, r)
+                    if have_replay:
+                        try:
+                            cmdutil.replay_convert_rev(hg_editor, svn, r)
+                        except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
+                            ui.status('%s\n' % e.message)
+                            stupidmod.print_your_svn_is_old_message(ui)
+                            have_replay = False
+                            stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+                    else:
+                        stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+                    converted = True
+                except core.SubversionException, e: #pragma: no cover
+                    if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
+                        and '502' in str(e)
+                        and tries < 3):
+                        tries += 1
+                        ui.status('Got a 502, retrying (%s)\n' % tries)
+                    else:
+                        raise hgutil.Abort(*e.args)
+    util.swap_out_encoding(old_encoding)
+
+pull = util.register_subcommand('pull')(pull)

changeset:   242:06130689a2c8
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 17:53:48 2009 +0200
summary:     Move push into svncommands.

diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,3 +1,20 @@
+
+from mercurial import util as hgutil
+
+from svn import core
+
+import svnwrap
+import svnexternals
+
+
+class BaseException(Exception):
+    pass
+
+
+class NoFilesException(BaseException):
+    """Exception raised when you try and commit without files.
+    """
+
 
 def replay_convert_rev(hg_editor, svn, r):
     hg_editor.set_current_rev(r)
@@ -30,3 +47,196 @@
         hg_editor.missing_plaintexts = set()
         hg_editor.ui.note('\n')
     hg_editor.commit_current_delta()
+
+
+def _isdir(svn, branchpath, svndir):
+    try:
+        svn.list_dir('%s/%s' % (branchpath, svndir))
+        return True
+    except core.SubversionException:
+        return False
+
+
+def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges):
+    """Compute directories to add or delete when moving from parentctx
+    to ctx, assuming only 'changedfiles' files changed, and 'extchanges'
+    external references changed (as returned by svnexternals.diff()).
+
+    Return (added, deleted) where 'added' is the list of all added
+    directories and 'deleted' the list of deleted directories.
+    Intermediate directories are included: if a/b/c is new and requires
+    the addition of a/b and a, those will be listed too. Intermediate
+    deleted directories are also listed, but item order of undefined
+    in either list.
+    """
+    def finddirs(path, includeself=False):
+        if includeself:
+            yield path
+        pos = path.rfind('/')
+        while pos != -1:
+            yield path[:pos]
+            pos = path.rfind('/', 0, pos)
+
+    def getctxdirs(ctx, keptdirs, extdirs):
+        dirs = {}
+        for f in ctx.manifest():
+            for d in finddirs(f):
+                if d in dirs:
+                    break
+                if d in keptdirs:
+                    dirs[d] = 1
+        for extdir in extdirs:
+            for d in finddirs(extdir, True):
+                dirs[d] = 1
+        return dirs
+
+    deleted, added = [], []
+    changeddirs = {}
+    for f in changedfiles:
+        if f in parentctx and f in ctx:
+            # Updated files cannot cause directories to be created
+            # or removed.
+            continue
+        for d in finddirs(f):
+            changeddirs[d] = 1
+    for e in extchanges:
+        if not e[1] or not e[2]:
+            for d in finddirs(e[0], True):
+                changeddirs[d] = 1
+    if not changeddirs:
+        return added, deleted
+    olddirs = getctxdirs(parentctx, changeddirs,
+                         [e[0] for e in extchanges if e[1]])
+    newdirs = getctxdirs(ctx, changeddirs,
+                         [e[0] for e in extchanges if e[2]])
+
+    for d in newdirs:
+        if d not in olddirs and not _isdir(svn, branchpath, d):
+            added.append(d)
+
+    for d in olddirs:
+        if d not in newdirs and _isdir(svn, branchpath, d):
+            deleted.append(d)
+
+    return added, deleted
+
+
+def _externals(ctx):
+    ext = svnexternals.externalsfile()
+    if '.hgsvnexternals' in ctx:
+        ext.read(ctx['.hgsvnexternals'].data())
+    return ext
+
+
+def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision
+                    username, password):
+    """Build and send a commit from Mercurial to Subversion.
+    """
+    file_data = {}
+    svn = svnwrap.SubversionRepo(svn_url, username, password)
+    parent = rev_ctx.parents()[0]
+    parent_branch = rev_ctx.parents()[0].branch()
+    branch_path = 'trunk'
+
+    if parent_branch and parent_branch != 'default':
+        branch_path = 'branches/%s' % parent_branch
+
+    extchanges = list(svnexternals.diff(_externals(parent),
+                                        _externals(rev_ctx)))
+    addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
+                                            rev_ctx.files(), extchanges)
+    deleteddirs = set(deleteddirs)
+
+    props = {}
+    copies = {}
+    for file in rev_ctx.files():
+        if file == '.hgsvnexternals':
+            continue
+        new_data = base_data = ''
+        action = ''
+        if file in rev_ctx:
+            fctx = rev_ctx.filectx(file)
+            new_data = fctx.data()
+
+            if 'x' in fctx.flags():
+                props.setdefault(file, {})['svn:executable'] = '*'
+            if 'l' in fctx.flags():
+                props.setdefault(file, {})['svn:special'] = '*'
+
+            if file not in parent:
+                renamed = fctx.renamed()
+                if renamed:
+                    # TODO current model (and perhaps svn model) does not support
+                    # this kind of renames: a -> b, b -> c
+                    copies[file] = renamed[0]
+                    base_data = parent[renamed[0]].data()
+
+                action = 'add'
+                dirname = '/'.join(file.split('/')[:-1] + [''])
+            else:
+                base_data = parent.filectx(file).data()
+                if ('x' in parent.filectx(file).flags()
+                    and 'x' not in rev_ctx.filectx(file).flags()):
+                    props.setdefault(file, {})['svn:executable'] = None
+                if ('l' in parent.filectx(file).flags()
+                    and 'l' not in rev_ctx.filectx(file).flags()):
+                    props.setdefault(file, {})['svn:special'] = None
+                action = 'modify'
+        else:
+            pos = file.rfind('/')
+            if pos >= 0:
+                if file[:pos] in deleteddirs:
+                    # This file will be removed when its directory is removed
+                    continue
+            action = 'delete'
+        file_data[file] = base_data, new_data, action
+
+    def svnpath(p):
+        return '%s/%s' % (branch_path, p)
+
+    changeddirs = []
+    for d, v1, v2 in extchanges:
+        props.setdefault(svnpath(d), {})['svn:externals'] = v2
+        if d not in deleteddirs and d not in addeddirs:
+            changeddirs.append(svnpath(d))
+
+    # Now we are done with files, we can prune deleted directories
+    # against themselves: ignore a/b if a/ is already removed
+    deleteddirs2 = list(deleteddirs)
+    deleteddirs2.sort(reverse=True)
+    for d in deleteddirs2:
+        pos = d.rfind('/')
+        if pos >= 0 and d[:pos] in deleteddirs:
+            deleteddirs.remove(d[:pos])
+
+    newcopies = {}
+    for source, dest in copies.iteritems():
+        newcopies[svnpath(source)] = (svnpath(dest), base_revision)
+
+    new_target_files = [svnpath(f) for f in file_data]
+    for tf, ntf in zip(file_data, new_target_files):
+        if tf in file_data:
+            file_data[ntf] = file_data[tf]
+            if tf in props:
+                props[ntf] = props[tf]
+                del props[tf]
+            if hgutil.binary(file_data[ntf][1]):
+                props.setdefault(ntf, {}).update(props.get(ntf, {}))
+                props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream'
+            del file_data[tf]
+
+    addeddirs = [svnpath(d) for d in addeddirs]
+    deleteddirs = [svnpath(d) for d in deleteddirs]
+    new_target_files += addeddirs + deleteddirs + changeddirs
+    if not new_target_files:
+        raise NoFilesException()
+    try:
+        svn.commit(new_target_files, rev_ctx.description(), file_data,
+                   base_revision, set(addeddirs), set(deleteddirs),
+                   props, newcopies)
+    except core.SubversionException, e:
+        if hasattr(e, 'apr_err') and (e.apr_err == core.SVN_ERR_FS_TXN_OUT_OF_DATE
+                                      or e.apr_err == core.SVN_ERR_FS_CONFLICT):
+            raise hgutil.Abort('Base text was out of date, maybe rebase?')
+        else:
+            raise
diff --git a/push_cmd.py b/push_cmd.py
deleted file mode 100644
--- a/push_cmd.py
+++ /dev/null
@@ -1,299 +0,0 @@
-from mercurial import util as merc_util
-from mercurial import hg
-from mercurial import node
-from svn import core
-
-import util
-import hg_delta_editor
-import svnexternals
-import svnwrap
-import svncommands
-import utility_commands
-
-
-class BaseException(Exception):
-    pass
-
-
-class NoFilesException(BaseException):
-    """Exception raised when you try and commit without files.
-    """
-
-
-def push_revisions_to_subversion(ui, repo, hg_repo_path, svn_url,
-                                 stupid=False, **opts):
-    """push revisions starting at a specified head back to Subversion.
-    """
-    old_encoding = util.swap_out_encoding()
-    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                           ui_=ui)
-    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
-                                 hge.revmap.iterkeys()))
-    user = opts.get('username', merc_util.getuser())
-    passwd = opts.get('password', '')
-    # Strategy:
-    # 1. Find all outgoing commits from this head
-    if len(repo.parents()) != 1:
-        ui.status('Cowardly refusing to push branch merge')
-        return 1
-    workingrev = repo.parents()[0]
-    outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
-    if not (outgoing and len(outgoing)):
-        ui.status('No revisions to push.')
-        return 0
-    while outgoing:
-        oldest = outgoing.pop(-1)
-        old_ctx = repo[oldest]
-        if len(old_ctx.parents()) != 1:
-            ui.status('Found a branch merge, this needs discussion and '
-                      'implementation.')
-            return 1
-        base_n = old_ctx.parents()[0].node()
-        old_children = repo[base_n].children()
-        svnbranch = repo[base_n].branch()
-        oldtip = base_n
-        samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
-                              and c.node() in svn_commit_hashes]
-        while samebranchchildren:
-            oldtip = samebranchchildren[0].node()
-            samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
-                                  and c.node() in svn_commit_hashes]
-        # 2. Commit oldest revision that needs to be pushed
-        base_revision = svn_commit_hashes[base_n][0]
-        try:
-            commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision,
-                            user, passwd)
-        except NoFilesException:
-            ui.warn("Could not push revision %s because it had no changes in svn.\n" %
-                     old_ctx)
-            return 1
-        # 3. Fetch revisions from svn
-        r = svncommands.pull(ui, svn_url, hg_repo_path, stupid=stupid,
-                             username=user, password=passwd)
-        assert not r or r == 0
-        # 4. Find the new head of the target branch
-        repo = hg.repository(ui, hge.path)
-        oldtipctx = repo[oldtip]
-        replacement = [c for c in oldtipctx.children() if c not in old_children
-                       and c.branch() == oldtipctx.branch()]
-        assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
-        replacement = replacement[0]
-        # 5. Rebase all children of the currently-pushing rev to the new branch
-        heads = repo.heads(old_ctx.node())
-        for needs_transplant in heads:
-            def extrafn(ctx, extra):
-                if ctx.node() == oldest:
-                    return
-                extra['branch'] = ctx.branch()
-            utility_commands.rebase_commits(ui, repo,
-                                            extrafn=extrafn,
-                                            sourcerev=needs_transplant,
-                                            **opts)
-            repo = hg.repository(ui, hge.path)
-            for child in repo[replacement.node()].children():
-                rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
-                if rebasesrc in outgoing:
-                    while rebasesrc in outgoing:
-                        rebsrcindex = outgoing.index(rebasesrc)
-                        outgoing = (outgoing[0:rebsrcindex] +
-                                    [child.node(), ] + outgoing[rebsrcindex+1:])
-                        children = [c for c in child.children() if c.branch() == child.branch()]
-                        if children:
-                            child = children[0]
-                        rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
-        hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
-        svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
-    util.swap_out_encoding(old_encoding)
-    return 0
-push_revisions_to_subversion = util.register_subcommand('push')(push_revisions_to_subversion)
-# for git expats
-push_revisions_to_subversion = util.register_subcommand('dcommit')(push_revisions_to_subversion)
-
-def _isdir(svn, branchpath, svndir):
-    try:
-        svn.list_dir('%s/%s' % (branchpath, svndir))
-        return True
-    except core.SubversionException:
-        return False
-
-def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges):
-    """Compute directories to add or delete when moving from parentctx
-    to ctx, assuming only 'changedfiles' files changed, and 'extchanges'
-    external references changed (as returned by svnexternals.diff()).
-
-    Return (added, deleted) where 'added' is the list of all added
-    directories and 'deleted' the list of deleted directories.
-    Intermediate directories are included: if a/b/c is new and requires
-    the addition of a/b and a, those will be listed too. Intermediate
-    deleted directories are also listed, but item order of undefined
-    in either list.
-    """
-    def finddirs(path, includeself=False):
-        if includeself:
-            yield path
-        pos = path.rfind('/')
-        while pos != -1:
-            yield path[:pos]
-            pos = path.rfind('/', 0, pos)
-
-    def getctxdirs(ctx, keptdirs, extdirs):
-        dirs = {}
-        for f in ctx.manifest():
-            for d in finddirs(f):
-                if d in dirs:
-                    break
-                if d in keptdirs:
-                    dirs[d] = 1
-        for extdir in extdirs:
-            for d in finddirs(extdir, True):
-                dirs[d] = 1
-        return dirs
-
-    deleted, added = [], []
-    changeddirs = {}
-    for f in changedfiles:
-        if f in parentctx and f in ctx:
-            # Updated files cannot cause directories to be created
-            # or removed.
-            continue
-        for d in finddirs(f):
-            changeddirs[d] = 1
-    for e in extchanges:
-        if not e[1] or not e[2]:
-            for d in finddirs(e[0], True):
-                changeddirs[d] = 1
-    if not changeddirs:
-        return added, deleted
-    olddirs = getctxdirs(parentctx, changeddirs,
-                         [e[0] for e in extchanges if e[1]])
-    newdirs = getctxdirs(ctx, changeddirs,
-                         [e[0] for e in extchanges if e[2]])
-
-    for d in newdirs:
-        if d not in olddirs and not _isdir(svn, branchpath, d):
-            added.append(d)
-
-    for d in olddirs:
-        if d not in newdirs and _isdir(svn, branchpath, d):
-            deleted.append(d)
-
-    return added, deleted
-
-def _externals(ctx):
-    ext = svnexternals.externalsfile()
-    if '.hgsvnexternals' in ctx:
-        ext.read(ctx['.hgsvnexternals'].data())
-    return ext
-
-def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision,
-                    username, password):
-    """Build and send a commit from Mercurial to Subversion.
-    """
-    file_data = {}
-    svn = svnwrap.SubversionRepo(svn_url, username, password)
-    parent = rev_ctx.parents()[0]
-    parent_branch = rev_ctx.parents()[0].branch()
-    branch_path = 'trunk'
-
-    if parent_branch and parent_branch != 'default':
-        branch_path = 'branches/%s' % parent_branch
-
-    extchanges = list(svnexternals.diff(_externals(parent),
-                                        _externals(rev_ctx)))
-    addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
-                                            rev_ctx.files(), extchanges)
-    deleteddirs = set(deleteddirs)
-
-    props = {}
-    copies = {}
-    for file in rev_ctx.files():
-        if file == '.hgsvnexternals':
-            continue
-        new_data = base_data = ''
-        action = ''
-        if file in rev_ctx:
-            fctx = rev_ctx.filectx(file)
-            new_data = fctx.data()
-
-            if 'x' in fctx.flags():
-                props.setdefault(file, {})['svn:executable'] = '*'
-            if 'l' in fctx.flags():
-                props.setdefault(file, {})['svn:special'] = '*'
-
-            if file not in parent:
-                renamed = fctx.renamed()
-                if renamed:
-                    # TODO current model (and perhaps svn model) does not support
-                    # this kind of renames: a -> b, b -> c
-                    copies[file] = renamed[0]
-                    base_data = parent[renamed[0]].data()
-
-                action = 'add'
-                dirname = '/'.join(file.split('/')[:-1] + [''])
-            else:
-                base_data = parent.filectx(file).data()
-                if ('x' in parent.filectx(file).flags()
-                    and 'x' not in rev_ctx.filectx(file).flags()):
-                    props.setdefault(file, {})['svn:executable'] = None
-                if ('l' in parent.filectx(file).flags()
-                    and 'l' not in rev_ctx.filectx(file).flags()):
-                    props.setdefault(file, {})['svn:special'] = None
-                action = 'modify'
-        else:
-            pos = file.rfind('/')
-            if pos >= 0:
-                if file[:pos] in deleteddirs:
-                    # This file will be removed when its directory is removed
-                    continue
-            action = 'delete'
-        file_data[file] = base_data, new_data, action
-
-    def svnpath(p):
-        return '%s/%s' % (branch_path, p)
-
-    changeddirs = []
-    for d, v1, v2 in extchanges:
-        props.setdefault(svnpath(d), {})['svn:externals'] = v2
-        if d not in deleteddirs and d not in addeddirs:
-            changeddirs.append(svnpath(d))
-
-    # Now we are done with files, we can prune deleted directories
-    # against themselves: ignore a/b if a/ is already removed
-    deleteddirs2 = list(deleteddirs)
-    deleteddirs2.sort(reverse=True)
-    for d in deleteddirs2:
-        pos = d.rfind('/')
-        if pos >= 0 and d[:pos] in deleteddirs:
-            deleteddirs.remove(d[:pos])
-
-    newcopies = {}
-    for source, dest in copies.iteritems():
-        newcopies[svnpath(source)] = (svnpath(dest), base_revision)
-
-    new_target_files = [svnpath(f) for f in file_data]
-    for tf, ntf in zip(file_data, new_target_files):
-        if tf in file_data:
-            file_data[ntf] = file_data[tf]
-            if tf in props:
-                props[ntf] = props[tf]
-                del props[tf]
-            if merc_util.binary(file_data[ntf][1]):
-                props.setdefault(ntf, {}).update(props.get(ntf, {}))
-                props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream'
-            del file_data[tf]
-
-    addeddirs = [svnpath(d) for d in addeddirs]
-    deleteddirs = [svnpath(d) for d in deleteddirs]
-    new_target_files += addeddirs + deleteddirs + changeddirs
-    if not new_target_files:
-        raise NoFilesException()
-    try:
-        svn.commit(new_target_files, rev_ctx.description(), file_data,
-                   base_revision, set(addeddirs), set(deleteddirs),
-                   props, newcopies)
-    except core.SubversionException, e:
-        if hasattr(e, 'apr_err') and (e.apr_err == core.SVN_ERR_FS_TXN_OUT_OF_DATE
-                                      or e.apr_err == core.SVN_ERR_FS_CONFLICT):
-            raise merc_util.Abort('Base text was out of date, maybe rebase?')
-        else:
-            raise
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -12,11 +12,10 @@
 # dirty trick to force demandimport to run my decorator anyway.
 from svncommands import pull
 from utility_commands import print_wc_url
-from push_cmd import commit_from_rev
 from diff_cmd import diff_command
 from rebuildmeta import rebuildmeta
 # shut up, pyflakes, we must import those
-__x = [print_wc_url, pull, commit_from_rev, diff_command, rebuildmeta]
+__x = [print_wc_url, pull, diff_command, rebuildmeta]
 
 
 def svncmd(ui, repo, subcommand, *args, **opts):
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,6 +1,9 @@
 import os
 
+from mercurial import hg
+from mercurial import node
 from mercurial import util as hgutil
+
 from svn import core
 from svn import delta
 
@@ -9,6 +12,7 @@
 import stupid as stupidmod
 import cmdutil
 import util
+import utility_commands
 
 
 def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
@@ -91,3 +95,93 @@
     util.swap_out_encoding(old_encoding)
 
 pull = util.register_subcommand('pull')(pull)
+
+
+def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
+    """push revisions starting at a specified head back to Subversion.
+    """
+    old_encoding = util.swap_out_encoding()
+    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+                                           ui_=ui)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    user = opts.get('username', hgutil.getuser())
+    passwd = opts.get('password', '')
+
+    # Strategy:
+    # 1. Find all outgoing commits from this head
+    if len(repo.parents()) != 1:
+        ui.status('Cowardly refusing to push branch merge')
+        return 1
+    workingrev = repo.parents()[0]
+    outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
+    if not (outgoing and len(outgoing)):
+        ui.status('No revisions to push.')
+        return 0
+    while outgoing:
+        oldest = outgoing.pop(-1)
+        old_ctx = repo[oldest]
+        if len(old_ctx.parents()) != 1:
+            ui.status('Found a branch merge, this needs discussion and '
+                      'implementation.')
+            return 1
+        base_n = old_ctx.parents()[0].node()
+        old_children = repo[base_n].children()
+        svnbranch = repo[base_n].branch()
+        oldtip = base_n
+        samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+                              and c.node() in svn_commit_hashes]
+        while samebranchchildren:
+            oldtip = samebranchchildren[0].node()
+            samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+                                  and c.node() in svn_commit_hashes]
+        # 2. Commit oldest revision that needs to be pushed
+        base_revision = svn_commit_hashes[base_n][0]
+        try:
+            cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svn_url,
+                                    base_revision, user, passwd)
+        except cmdutil.NoFilesException:
+            ui.warn("Could not push revision %s because it had no changes in svn.\n" %
+                     old_ctx)
+            return 1
+        # 3. Fetch revisions from svn
+        r = pull(ui, svn_url, hg_repo_path, stupid=stupid,
+                 username=user, password=passwd)
+        assert not r or r == 0
+        # 4. Find the new head of the target branch
+        repo = hg.repository(ui, hge.path)
+        oldtipctx = repo[oldtip]
+        replacement = [c for c in oldtipctx.children() if c not in old_children
+                       and c.branch() == oldtipctx.branch()]
+        assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
+        replacement = replacement[0]
+        # 5. Rebase all children of the currently-pushing rev to the new branch
+        heads = repo.heads(old_ctx.node())
+        for needs_transplant in heads:
+            def extrafn(ctx, extra):
+                if ctx.node() == oldest:
+                    return
+                extra['branch'] = ctx.branch()
+            utility_commands.rebase_commits(ui, repo,
+                                            extrafn=extrafn,
+                                            sourcerev=needs_transplant,
+                                            **opts)
+            repo = hg.repository(ui, hge.path)
+            for child in repo[replacement.node()].children():
+                rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+                if rebasesrc in outgoing:
+                    while rebasesrc in outgoing:
+                        rebsrcindex = outgoing.index(rebasesrc)
+                        outgoing = (outgoing[0:rebsrcindex] +
+                                    [child.node(), ] + outgoing[rebsrcindex+1:])
+                        children = [c for c in child.children() if c.branch() == child.branch()]
+                        if children:
+                            child = children[0]
+                        rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+        hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
+        svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
+    util.swap_out_encoding(old_encoding)
+    return 0
+push = util.register_subcommand('push')(push)
+# for git expats
+dcommit = util.register_subcommand('dcommit')(push)

changeset:   243:2027f851d60c
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 17:54:30 2009 +0200
summary:     Small cleanups: remove unneeded imports, useless superclass.

diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -7,11 +7,7 @@
 import svnexternals
 
 
-class BaseException(Exception):
-    pass
-
-
-class NoFilesException(BaseException):
+class NoFilesException(Exception):
     """Exception raised when you try and commit without files.
     """
 
@@ -128,7 +124,7 @@
     return ext
 
 
-def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision
+def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision,
                     username, password):
     """Build and send a commit from Mercurial to Subversion.
     """
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -1,12 +1,10 @@
 import os
-import stat
 import sys
 import traceback
 
 from mercurial import hg
 from mercurial import node
 
-import svnwrap
 import util
 from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
 # dirty trick to force demandimport to run my decorator anyway.

changeset:   244:28d0ee605308
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 18:21:47 2009 +0200
summary:     Move diff to svncommands.

diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,3 +1,5 @@
+#!/usr/bin/python
+import re
 
 from mercurial import util as hgutil
 
@@ -7,11 +9,31 @@
 import svnexternals
 
 
+b_re = re.compile(r'^\+\+\+ b\/([^\n]*)', re.MULTILINE)
+a_re = re.compile(r'^--- a\/([^\n]*)', re.MULTILINE)
+devnull_re = re.compile(r'^([-+]{3}) /dev/null', re.MULTILINE)
+header_re = re.compile(r'^diff --git .* b\/(.*)', re.MULTILINE)
+newfile_devnull_re = re.compile(r'^--- /dev/null\n\+\+\+ b/([^\n]*)',
+                                re.MULTILINE)
+
+
 class NoFilesException(Exception):
     """Exception raised when you try and commit without files.
     """
 
 
+def filterdiff(diff, base_revision):
+    diff = newfile_devnull_re.sub(r'--- \1\t(revision 0)' '\n'
+                                  r'+++ \1\t(working copy)',
+                                  diff)
+    diff = a_re.sub(r'--- \1'+ ('\t(revision %d)' % base_revision), diff)
+    diff = b_re.sub(r'+++ \1' + '\t(working copy)', diff)
+    diff = devnull_re.sub(r'\1 /dev/null' '\t(working copy)', diff)
+
+    diff = header_re.sub(r'Index: \1' + '\n' + ('=' * 67), diff)
+    return diff
+
+
 def replay_convert_rev(hg_editor, svn, r):
     hg_editor.set_current_rev(r)
     svn.get_replay(r.revnum, hg_editor)
diff --git a/diff_cmd.py b/diff_cmd.py
deleted file mode 100644
--- a/diff_cmd.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-import re
-
-from mercurial import patch
-
-import util
-import hg_delta_editor
-
-b_re = re.compile(r'^\+\+\+ b\/([^\n]*)', re.MULTILINE)
-a_re = re.compile(r'^--- a\/([^\n]*)', re.MULTILINE)
-devnull_re = re.compile(r'^([-+]{3}) /dev/null', re.MULTILINE)
-header_re = re.compile(r'^diff --git .* b\/(.*)', re.MULTILINE)
-newfile_devnull_re = re.compile(r'^--- /dev/null\n\+\+\+ b/([^\n]*)',
-                                re.MULTILINE)
-def filterdiff(diff, base_revision):
-    diff = newfile_devnull_re.sub(r'--- \1\t(revision 0)' '\n'
-                                  r'+++ \1\t(working copy)',
-                                  diff)
-    diff = a_re.sub(r'--- \1'+ ('\t(revision %d)' % base_revision), diff)
-    diff = b_re.sub(r'+++ \1' + '\t(working copy)', diff)
-    diff = devnull_re.sub(r'\1 /dev/null' '\t(working copy)', diff)
-
-    diff = header_re.sub(r'Index: \1' + '\n' + ('=' * 67), diff)
-    return diff
-
-
-def diff_command(ui, repo, hg_repo_path, **opts):
-    """show a diff of the most recent revision against its parent from svn
-    """
-    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                           ui_=ui)
-    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
-                                 hge.revmap.iterkeys()))
-    parent = repo.parents()[0]
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, parent.node())
-    if o_r:
-        parent = repo[o_r[-1]].parents()[0]
-    base_rev, _junk = svn_commit_hashes[parent.node()]
-    it = patch.diff(repo, parent.node(), None,
-                    opts=patch.diffopts(ui, opts={'git': True,
-                                                  'show_function': False,
-                                                  'ignore_all_space': False,
-                                                  'ignore_space_change': False,
-                                                  'ignore_blank_lines': False,
-                                                  'unified': True,
-                                                  'text': False,
-                                                  }))
-    ui.write(filterdiff(''.join(it), base_rev))
-diff_command = util.register_subcommand('diff')(diff_command)
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -8,12 +8,11 @@
 import util
 from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
 # dirty trick to force demandimport to run my decorator anyway.
-from svncommands import pull
+from svncommands import pull, diff
 from utility_commands import print_wc_url
-from diff_cmd import diff_command
 from rebuildmeta import rebuildmeta
 # shut up, pyflakes, we must import those
-__x = [print_wc_url, pull, diff_command, rebuildmeta]
+__x = [print_wc_url, pull, diff, rebuildmeta]
 
 
 def svncmd(ui, repo, subcommand, *args, **opts):
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -2,6 +2,7 @@
 
 from mercurial import hg
 from mercurial import node
+from mercurial import patch
 from mercurial import util as hgutil
 
 from svn import core
@@ -185,3 +186,28 @@
 push = util.register_subcommand('push')(push)
 # for git expats
 dcommit = util.register_subcommand('dcommit')(push)
+
+
+def diff(ui, repo, hg_repo_path, **opts):
+    """show a diff of the most recent revision against its parent from svn
+    """
+    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+                                           ui_=ui)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    parent = repo.parents()[0]
+    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, parent.node())
+    if o_r:
+        parent = repo[o_r[-1]].parents()[0]
+    base_rev, _junk = svn_commit_hashes[parent.node()]
+    it = patch.diff(repo, parent.node(), None,
+                    opts=patch.diffopts(ui, opts={'git': True,
+                                                  'show_function': False,
+                                                  'ignore_all_space': False,
+                                                  'ignore_space_change': False,
+                                                  'ignore_blank_lines': False,
+                                                  'unified': True,
+                                                  'text': False,
+                                                  }))
+    ui.write(cmdutil.filterdiff(''.join(it), base_rev))
+diff = util.register_subcommand('diff')(diff)

changeset:   246:074f27c68818
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 18:49:44 2009 +0200
summary:     Move rebuildmeta into svncommands.

diff --git a/rebuildmeta.py b/rebuildmeta.py
deleted file mode 100644
--- a/rebuildmeta.py
+++ /dev/null
@@ -1,126 +0,0 @@
-import os
-import pickle
-
-from mercurial import node
-from mercurial import util as mutil
-
-import svnwrap
-import util
-
-def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
-    """rebuild hgsubversion metadata using values stored in revisions
-    """
-    if len(args) != 1:
-        raise mutil.Abort('You must pass the svn URI used to create this repo.')
-    uuid = None
-    url = args[0].rstrip('/')
-    user = opts.get('username', mutil.getuser())
-    passwd = opts.get('password', '')
-    svn = svnwrap.SubversionRepo(url, user, passwd)
-    subdir = svn.subdir
-    svnmetadir = os.path.join(repo.path, 'svn')
-    if not os.path.exists(svnmetadir):
-        os.makedirs(svnmetadir)
-
-    revmap = open(os.path.join(svnmetadir, 'rev_map'), 'w')
-    revmap.write('1\n')
-    last_rev = -1
-    branchinfo = {}
-    noderevnums = {}
-    for rev in repo:
-        ctx = repo[rev]
-        convinfo = ctx.extra().get('convert_revision', None)
-        if convinfo:
-            assert convinfo.startswith('svn:')
-            revpath, revision = convinfo[40:].split('@')
-            if subdir and subdir[0] != '/':
-                subdir = '/' + subdir
-            if subdir and subdir[-1] == '/':
-                subdir = subdir[:-1]
-            assert revpath.startswith(subdir), ('That does not look like the '
-                                                'right location in the repo.')
-            if uuid is None:
-                uuid = convinfo[4:40]
-                assert uuid == svn.uuid, 'UUIDs did not match!'
-                urlfile = open(os.path.join(svnmetadir, 'url'), 'w')
-                urlfile.write(url)
-                urlfile.close()
-                uuidfile = open(os.path.join(svnmetadir, 'uuid'), 'w')
-                uuidfile.write(uuid)
-                uuidfile.close()
-            commitpath = revpath[len(subdir)+1:]
-            if commitpath.startswith('branches'):
-                commitpath = commitpath[len('branches/'):]
-            elif commitpath == 'trunk':
-                commitpath = ''
-            else:
-                assert False, 'Unhandled case in rebuildmeta'
-            revmap.write('%s %s %s\n' % (revision,
-                                         node.hex(ctx.node()),
-                                         commitpath))
-            revision = int(revision)
-            noderevnums[ctx.node()] = revision
-            if revision > last_rev:
-                last_rev = revision
-            branch = ctx.branch()
-            if branch == 'default':
-                branch = None
-            if branch not in branchinfo:
-                parent = ctx.parents()[0]
-                if (parent.node() in noderevnums
-                    and parent.branch() != ctx.branch()):
-                    parentbranch = parent.branch()
-                    if parentbranch == 'default':
-                        parentbranch = None
-                else:
-                    parentbranch = None
-                branchinfo[branch] = (parentbranch,
-                                      noderevnums.get(parent.node(), 0),
-                                      revision)
-            for c in ctx.children():
-                if c.branch() == 'closed-branches':
-                    if branch in branchinfo:
-                        del branchinfo[branch]
-    branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
-    pickle.dump(branchinfo, branchinfofile)
-    branchinfofile.close()
-
-    # now handle tags
-    tagsinfo = {}
-    realtags = svn.tags
-    tagsleft = realtags.items()
-    while tagsleft:
-        tag, tagparent = tagsleft.pop(0)
-        source, rev = tagparent
-        if source.startswith('tags/'):
-            src = source[len('tags/'):]
-            if src in tagsinfo:
-                tagsinfo[tag] = tagsinfo[src]
-            elif src in realtags:
-                if (realtags[src][1] <= last_rev
-                    or realtags[src][0].startswith('tags/')):
-                    tagsleft.append(src)
-            else:
-                older_tags = svn.tags_at_rev(rev)
-                newsrc, newrev = older_tags[src]
-                tagsleft.append((tag, (newsrc, newrev)))
-            continue
-        else:
-            source = determinebranch(source)
-        if rev <= last_rev and (source or 'default') in repo.branchtags():
-            tagsinfo[tag] = source, rev
-    tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
-    pickle.dump(tagsinfo, tagsinfofile)
-    tagsinfofile.close()
-rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
-rebuildmeta = util.command_needs_no_url(rebuildmeta)
-
-def determinebranch(branch):
-    assert not branch.startswith('tags/'), "Tags can't be tags of other tags."
-    if branch.startswith('branches/'):
-        branch = branch[len('branches/'):]
-    elif branch == 'trunk':
-        branch = None
-    else:
-        branch = '../' + branch
-    return branch
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -8,9 +8,8 @@
 import util
 from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
 # dirty trick to force demandimport to run my decorator anyway.
-from svncommands import pull, diff
+from svncommands import pull, diff, rebuildmeta
 from utility_commands import print_wc_url
-from rebuildmeta import rebuildmeta
 # shut up, pyflakes, we must import those
 __x = [print_wc_url, pull, diff, rebuildmeta]
 
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,4 +1,5 @@
 import os
+import cPickle as pickle
 
 from mercurial import hg
 from mercurial import node
@@ -211,3 +212,120 @@
                                                   }))
     ui.write(cmdutil.filterdiff(''.join(it), base_rev))
 diff = util.register_subcommand('diff')(diff)
+
+
+def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
+    """rebuild hgsubversion metadata using values stored in revisions
+    """
+    if len(args) != 1:
+        raise hgutil.Abort('You must pass the svn URI used to create this repo.')
+    uuid = None
+    url = args[0].rstrip('/')
+    user = opts.get('username', hgutil.getuser())
+    passwd = opts.get('password', '')
+    svn = svnwrap.SubversionRepo(url, user, passwd)
+    subdir = svn.subdir
+    svnmetadir = os.path.join(repo.path, 'svn')
+    if not os.path.exists(svnmetadir):
+        os.makedirs(svnmetadir)
+
+    revmap = open(os.path.join(svnmetadir, 'rev_map'), 'w')
+    revmap.write('1\n')
+    last_rev = -1
+    branchinfo = {}
+    noderevnums = {}
+    for rev in repo:
+        ctx = repo[rev]
+        convinfo = ctx.extra().get('convert_revision', None)
+        if convinfo:
+            assert convinfo.startswith('svn:')
+            revpath, revision = convinfo[40:].split('@')
+            if subdir and subdir[0] != '/':
+                subdir = '/' + subdir
+            if subdir and subdir[-1] == '/':
+                subdir = subdir[:-1]
+            assert revpath.startswith(subdir), ('That does not look like the '
+                                                'right location in the repo.')
+            if uuid is None:
+                uuid = convinfo[4:40]
+                assert uuid == svn.uuid, 'UUIDs did not match!'
+                urlfile = open(os.path.join(svnmetadir, 'url'), 'w')
+                urlfile.write(url)
+                urlfile.close()
+                uuidfile = open(os.path.join(svnmetadir, 'uuid'), 'w')
+                uuidfile.write(uuid)
+                uuidfile.close()
+            commitpath = revpath[len(subdir)+1:]
+            if commitpath.startswith('branches'):
+                commitpath = commitpath[len('branches/'):]
+            elif commitpath == 'trunk':
+                commitpath = ''
+            else:
+                assert False, 'Unhandled case in rebuildmeta'
+            revmap.write('%s %s %s\n' % (revision,
+                                         node.hex(ctx.node()),
+                                         commitpath))
+            revision = int(revision)
+            noderevnums[ctx.node()] = revision
+            if revision > last_rev:
+                last_rev = revision
+            branch = ctx.branch()
+            if branch == 'default':
+                branch = None
+            if branch not in branchinfo:
+                parent = ctx.parents()[0]
+                if (parent.node() in noderevnums
+                    and parent.branch() != ctx.branch()):
+                    parentbranch = parent.branch()
+                    if parentbranch == 'default':
+                        parentbranch = None
+                else:
+                    parentbranch = None
+                branchinfo[branch] = (parentbranch,
+                                      noderevnums.get(parent.node(), 0),
+                                      revision)
+            for c in ctx.children():
+                if c.branch() == 'closed-branches':
+                    if branch in branchinfo:
+                        del branchinfo[branch]
+    branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
+    pickle.dump(branchinfo, branchinfofile)
+    branchinfofile.close()
+
+    # now handle tags
+    tagsinfo = {}
+    realtags = svn.tags
+    tagsleft = realtags.items()
+    while tagsleft:
+        tag, tagparent = tagsleft.pop(0)
+        source, rev = tagparent
+        if source.startswith('tags/'):
+            src = source[len('tags/'):]
+            if src in tagsinfo:
+                tagsinfo[tag] = tagsinfo[src]
+            elif src in realtags:
+                if (realtags[src][1] <= last_rev
+                    or realtags[src][0].startswith('tags/')):
+                    tagsleft.append(src)
+            else:
+                older_tags = svn.tags_at_rev(rev)
+                newsrc, newrev = older_tags[src]
+                tagsleft.append((tag, (newsrc, newrev)))
+            continue
+        else:
+            # determine the branch
+            assert not source.startswith('tags/'), "Tags can't be tags of other tags."
+            if source.startswith('branches/'):
+                source = source[len('branches/'):]
+            elif source == 'trunk':
+                source = None
+            else:
+                source = '../' + source
+        if rev <= last_rev and (source or 'default') in repo.branchtags():
+            tagsinfo[tag] = source, rev
+
+    tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
+    pickle.dump(tagsinfo, tagsinfofile)
+    tagsinfofile.close()
+rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
+rebuildmeta = util.command_needs_no_url(rebuildmeta)

changeset:   247:1272e87546ed
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 18:59:50 2009 +0200
summary:     Move help, update into svncommands.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -82,7 +82,7 @@
           ('', 'username', '', 'username for authentication'),
           ('', 'password', '', 'password for authentication'),
           ],
-         svncommand.generate_help(),
+         svncommands.generate_help(),
          ),
     "svnclone":
         (svn_fetch,
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -2,11 +2,7 @@
 import sys
 import traceback
 
-from mercurial import hg
-from mercurial import node
-
-import util
-from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
+from util import svn_subcommands, svn_commands_nourl
 # dirty trick to force demandimport to run my decorator anyway.
 from svncommands import pull, diff, rebuildmeta
 from utility_commands import print_wc_url
@@ -43,52 +39,3 @@
             ui.status('Unknown subcommand %s\n' % subcommand)
         else:
             raise
-
-
-def help_command(ui, args=None, **opts):
-    """show help for a given subcommands or a help overview
-    """
-    if args:
-        subcommand = args[0]
-        if subcommand not in svn_subcommands:
-            candidates = []
-            for c in svn_subcommands:
-                if c.startswith(subcommand):
-                    candidates.append(c)
-            if len(candidates) == 1:
-                subcommand = candidates[0]
-            elif len(candidates) > 1:
-                ui.status('Ambiguous command. Could have been:\n%s\n' %
-                          ' '.join(candidates))
-                return
-        doc = svn_subcommands[subcommand].__doc__
-        if doc is None:
-            doc = "No documentation available for %s." % subcommand
-        ui.status(doc.strip(), '\n')
-        return
-    ui.status(generate_help())
-help_command = register_subcommand('help')(help_command)
-
-def update(ui, args, repo, clean=False, **opts):
-    """update to a specified Subversion revision number
-    """
-    assert len(args) == 1
-    rev = int(args[0])
-    path = os.path.join(repo.path, 'svn', 'rev_map')
-    answers = []
-    for k,v in util.parse_revmap(path).iteritems():
-        if k[0] == rev:
-            answers.append((v, k[1]))
-    if len(answers) == 1:
-        if clean:
-            return hg.clean(repo, answers[0][0])
-        return hg.update(repo, answers[0][0])
-    elif len(answers) == 0:
-        ui.status('Revision %s did not produce an hg revision.\n' % rev)
-        return 1
-    else:
-        ui.status('Ambiguous revision!\n')
-        ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in
-                             answers]+['']))
-    return 1
-update = register_subcommand('up')(update)
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -16,6 +16,8 @@
 import util
 import utility_commands
 
+from util import generate_help, svn_subcommands, register_subcommand
+
 
 def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
          tag_locations='tags', authors=None, filemap=None, **opts):
@@ -329,3 +331,53 @@
     tagsinfofile.close()
 rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
 rebuildmeta = util.command_needs_no_url(rebuildmeta)
+
+
+def help(ui, args=None, **opts):
+    """show help for a given subcommands or a help overview
+    """
+    if args:
+        subcommand = args[0]
+        if subcommand not in svn_subcommands:
+            candidates = []
+            for c in svn_subcommands:
+                if c.startswith(subcommand):
+                    candidates.append(c)
+            if len(candidates) == 1:
+                subcommand = candidates[0]
+            elif len(candidates) > 1:
+                ui.status('Ambiguous command. Could have been:\n%s\n' %
+                          ' '.join(candidates))
+                return
+        doc = svn_subcommands[subcommand].__doc__
+        if doc is None:
+            doc = "No documentation available for %s." % subcommand
+        ui.status(doc.strip(), '\n')
+        return
+    ui.status(generate_help())
+help = register_subcommand('help')(help)
+
+
+def update(ui, args, repo, clean=False, **opts):
+    """update to a specified Subversion revision number
+    """
+    assert len(args) == 1
+    rev = int(args[0])
+    path = os.path.join(repo.path, 'svn', 'rev_map')
+    answers = []
+    for k,v in util.parse_revmap(path).iteritems():
+        if k[0] == rev:
+            answers.append((v, k[1]))
+    if len(answers) == 1:
+        if clean:
+            return hg.clean(repo, answers[0][0])
+        return hg.update(repo, answers[0][0])
+    elif len(answers) == 0:
+        ui.status('Revision %s did not produce an hg revision.\n' % rev)
+        return 1
+    else:
+        ui.status('Ambiguous revision!\n')
+        ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in
+                             answers]+['']))
+    return 1
+update = register_subcommand('up')(update)

changeset:   248:a9134fa28d15
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Wed Apr 08 19:12:23 2009 +0200
summary:     Move svncommand code into __init__.py.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -16,7 +16,7 @@
 
 from mercurial import commands
 from mercurial import hg
-from mercurial import util as mutil
+from mercurial import util as hgutil
 
 from svn import core
 
@@ -25,6 +25,8 @@
 import tag_repo
 import util
 
+from util import svn_subcommands, svn_commands_nourl
+
 def reposetup(ui, repo):
     if not util.is_svn_repo(repo):
         return
@@ -34,13 +36,39 @@
 
 def svn(ui, repo, subcommand, *args, **opts):
     '''see detailed help for list of subcommands'''
+
+    # guess command if prefix
+    if subcommand not in svn_subcommands:
+        candidates = []
+        for c in svn_subcommands:
+            if c.startswith(subcommand):
+                candidates.append(c)
+        if len(candidates) == 1:
+            subcommand = candidates[0]
+
+    path = os.path.dirname(repo.path)
     try:
-        return svncommand.svncmd(ui, repo, subcommand, *args, **opts)
+        commandfunc = svn_subcommands[subcommand]
+        if commandfunc not in svn_commands_nourl:
+            opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
+        return commandfunc(ui, args=args, hg_repo_path=path, repo=repo, **opts)
     except core.SubversionException, e:
         if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
-            raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+            raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
                      'Please try running svn ls on that url first.')
         raise
+    except TypeError:
+        tb = traceback.extract_tb(sys.exc_info()[2])
+        if len(tb) == 1:
+            ui.status('Bad arguments for subcommand %s\n' % subcommand)
+        else:
+            raise
+    except KeyError, e:
+        tb = traceback.extract_tb(sys.exc_info()[2])
+        if len(tb) == 1:
+            ui.status('Unknown subcommand %s\n' % subcommand)
+        else:
+            raise
 
 
 def svn_fetch(ui, svn_url, hg_repo_path=None, **opts):
@@ -61,7 +89,7 @@
         res = svncommands.pull(ui, svn_url, hg_repo_path, **opts)
     except core.SubversionException, e:
         if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
-            raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+            raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
                      'Please try running svn ls on that url first.')
         raise
     if (res is None or res == 0) and should_update:
diff --git a/svncommand.py b/svncommand.py
deleted file mode 100644
--- a/svncommand.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import os
-import sys
-import traceback
-
-from util import svn_subcommands, svn_commands_nourl
-# dirty trick to force demandimport to run my decorator anyway.
-from svncommands import pull, diff, rebuildmeta
-from utility_commands import print_wc_url
-# shut up, pyflakes, we must import those
-__x = [print_wc_url, pull, diff, rebuildmeta]
-
-
-def svncmd(ui, repo, subcommand, *args, **opts):
-    if subcommand not in svn_subcommands:
-        candidates = []
-        for c in svn_subcommands:
-            if c.startswith(subcommand):
-                candidates.append(c)
-        if len(candidates) == 1:
-            subcommand = candidates[0]
-    path = os.path.dirname(repo.path)
-    try:
-        commandfunc = svn_subcommands[subcommand]
-        if commandfunc not in svn_commands_nourl:
-            opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
-        return commandfunc(ui, args=args,
-                           hg_repo_path=path,
-                           repo=repo,
-                           **opts)
-    except TypeError:
-        tb = traceback.extract_tb(sys.exc_info()[2])
-        if len(tb) == 1:
-            ui.status('Bad arguments for subcommand %s\n' % subcommand)
-        else:
-            raise
-    except KeyError, e:
-        tb = traceback.extract_tb(sys.exc_info()[2])
-        if len(tb) == 1:
-            ui.status('Unknown subcommand %s\n' % subcommand)
-        else:
-            raise

changeset:   249:a065bf61caa1
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Fri Apr 10 15:43:47 2009 -0500
summary:     encoding: fix encoding swap to work with demandimport enabled (#67)

diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -4,10 +4,6 @@
 from mercurial import hg
 from mercurial import node
 from mercurial import util
-try:
-    from mercurial import encoding
-except ImportError:
-    encoding = None
 
 svn_subcommands = { }
 def register_subcommand(name):
@@ -143,10 +139,11 @@
 
 def swap_out_encoding(new_encoding="UTF-8"):
     """ Utility for mercurial incompatibility changes, can be removed after 1.3"""
-    if encoding is None:
+    try:
+        from mercurial import encoding
+        old = encoding.encoding
+        encoding.encoding = new_encoding
+    except ImportError:
         old = util._encoding
         util._encoding = new_encoding
-    else:
-        old = encoding.encoding
-        encoding.encoding = new_encoding
     return old

changeset:   250:79349fd04836
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Fri Apr 10 13:43:44 2009 -0400
summary:     utils: standardizing imported name to hgutil, our_util to util

diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -8,14 +8,14 @@
 from mercurial import context
 from mercurial import hg
 from mercurial import ui
-from mercurial import util
+from mercurial import util as hgutil
 from mercurial import revlog
 from mercurial import node
 from svn import delta
 from svn import core
 
 import svnexternals
-import util as our_util
+import util
 
 def pickle_atomic(data, file_path, dir=None):
     """pickle some data to a path atomically.
@@ -31,7 +31,7 @@
     except: #pragma: no cover
         raise
     else:
-        util.rename(path, file_path)
+        hgutil.rename(path, file_path)
 
 def stash_exception_on_self(fn):
     """Stash any exception raised in the method on self.
@@ -100,7 +100,7 @@
             self.subdir = self.subdir[1:]
         self.revmap = {}
         if os.path.exists(self.revmap_file):
-            self.revmap = our_util.parse_revmap(self.revmap_file)
+            self.revmap = util.parse_revmap(self.revmap_file)
         self.branches = {}
         if os.path.exists(self.branch_info_file):
             f = open(self.branch_info_file)
@@ -160,7 +160,7 @@
             self.repo = hg.repository(self.ui, repo_path, create=True)
             os.makedirs(os.path.dirname(self.uuid_file))
             f = open(self.revmap_file, 'w')
-            f.write('%s\n' % our_util.REVMAP_FILE_VERSION)
+            f.write('%s\n' % util.REVMAP_FILE_VERSION)
             f.flush()
             f.close()
 
@@ -634,9 +634,9 @@
             if parents[0] in closed_revs and branch in self.branches_to_delete:
                 continue
             # TODO this needs to be fixed with the new revmap
-            extra = our_util.build_extra(rev.revnum, branch,
-                                         open(self.uuid_file).read(),
-                                         self.subdir)
+            extra = util.build_extra(rev.revnum, branch,
+                                     open(self.uuid_file).read(),
+                                     self.subdir)
             if branch is not None:
                 if (branch not in self.branches
                     and branch not in self.repo.branchtags()):
@@ -676,7 +676,7 @@
                                          date,
                                          extra)
             new_hash = self.repo.commitctx(current_ctx)
-            our_util.describe_commit(self.ui, new_hash, branch)
+            util.describe_commit(self.ui, new_hash, branch)
             if (rev.revnum, branch) not in self.revmap:
                 self.add_to_revmap(rev.revnum, branch, new_hash)
         # now we handle branches that need to be committed without any files
@@ -689,9 +689,9 @@
                 raise IOError
            # True here meant nuke all files, shouldn't happen with branch closing
             if self.commit_branches_empty[branch]: #pragma: no cover
-               raise util.Abort('Empty commit to an open branch attempted. '
-                                'Please report this issue.')
-            extra = our_util.build_extra(rev.revnum, branch,
+               raise hgutil.Abort('Empty commit to an open branch attempted. '
+                                  'Please report this issue.')
+            extra = util.build_extra(rev.revnum, branch,
                                      open(self.uuid_file).read(),
                                      self.subdir)
             current_ctx = context.memctx(self.repo,
@@ -703,7 +703,7 @@
                                          date,
                                          extra)
             new_hash = self.repo.commitctx(current_ctx)
-            our_util.describe_commit(self.ui, new_hash, branch)
+            util.describe_commit(self.ui, new_hash, branch)
             if (rev.revnum, branch) not in self.revmap:
                 self.add_to_revmap(rev.revnum, branch, new_hash)
         self._save_metadata()
@@ -856,7 +856,7 @@
                 self.externals[path] = None
                 map(self.delete_file, [pat for pat in self.current_files.iterkeys()
                                        if pat.startswith(path+'/')])
-                for f in ctx.walk(our_util.PrefixMatch(br_path2)):
+                for f in ctx.walk(util.PrefixMatch(br_path2)):
                     f_p = '%s/%s' % (path, f[len(br_path2):])
                     if f_p not in self.current_files:
                         self.delete_file(f_p)
@@ -1055,8 +1055,8 @@
 
         handler, baton = delta.svn_txdelta_apply(source, target, None)
         if not callable(handler): #pragma: no cover
-            raise util.Abort('Error in Subversion bindings: '
-                             'cannot call handler!')
+            raise hgutil.Abort('Error in Subversion bindings: '
+                               'cannot call handler!')
         def txdelt_window(window):
             try:
                 if not self._is_path_valid(self.current_file):
@@ -1069,7 +1069,7 @@
                 if e.apr_err == core.SVN_ERR_INCOMPLETE_DATA:
                     self.missing_plaintexts.add(self.current_file)
                 else: #pragma: no cover
-                    raise util.Abort(*e.args)
+                    raise hgutil.Abort(*e.args)
             except: #pragma: no cover
                 print len(base), self.current_file
                 self._exception_info = sys.exc_info()
diff --git a/svnexternals.py b/svnexternals.py
--- a/svnexternals.py
+++ b/svnexternals.py
@@ -1,6 +1,6 @@
 import cStringIO
 
-from mercurial import util as merc_util
+from mercurial import util as hgutil
 
 class externalsfile(dict):
     """Map svn directories to lists of externals entries.
@@ -24,7 +24,7 @@
 
     def write(self):
         fp = cStringIO.StringIO()
-        for target in merc_util.sort(self):
+        for target in hgutil.sort(self):
             lines = self[target]
             if not lines:
                 continue
@@ -47,7 +47,7 @@
             if line.startswith('['):
                 line = line.strip()
                 if line[-1] != ']':
-                    raise merc_util.Abort('invalid externals section name: %s' % line)
+                    raise hgutil.Abort('invalid externals section name: %s' % line)
                 target = line[1:-1]
                 if target == '.':
                     target = ''
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -3,7 +3,7 @@
 
 from mercurial import hg
 from mercurial import node
-from mercurial import util
+from mercurial import util as hgutil
 
 svn_subcommands = { }
 def register_subcommand(name):
@@ -96,7 +96,7 @@
         outgoing_rev_hashes.append(sourcerev.node())
         sourcerev = sourcerev.parents()
         if len(sourcerev) != 1:
-            raise util.Abort("Sorry, can't find svn parent of a merge revision.")
+            raise hgutil.Abort("Sorry, can't find svn parent of a merge revision.")
         sourcerev = sourcerev[0]
     if sourcerev.node() != node.nullid:
         return outgoing_rev_hashes
@@ -144,6 +144,6 @@
         old = encoding.encoding
         encoding.encoding = new_encoding
     except ImportError:
-        old = util._encoding
-        util._encoding = new_encoding
+        old = hgutil._encoding
+        hgutil._encoding = new_encoding
     return old
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -3,7 +3,7 @@
 import mercurial
 from mercurial import cmdutil
 from mercurial import node
-from mercurial import util as mutil
+from mercurial import util as hgutil
 from hgext import rebase
 
 import svnwrap
@@ -34,7 +34,7 @@
     """
     ignpath = os.path.join(hg_repo_path, '.hgignore')
     if not force and os.path.exists(ignpath):
-        raise mutil.Abort('not overwriting existing .hgignore, try --force?')
+        raise hgutil.Abort('not overwriting existing .hgignore, try --force?')
     ignorefile = open(ignpath, 'w')
     ignorefile.write('.hgignore\nsyntax:glob\n')
     hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -50,7 +50,7 @@
     url = hge.url
     if url[-1] == '/':
         url = url[:-1]
-    user = opts.get('username', mutil.getuser())
+    user = opts.get('username', hgutil.getuser())
     passwd = opts.get('passwd', '')
     svn = svnwrap.SubversionRepo(url, user, passwd)
     dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd']
@@ -108,8 +108,8 @@
                'author': author,
                'revision': r,
                # TODO I'd like to format this to the user's local TZ if possible
-               'date': mutil.datestr(parent.date(),
-                                     '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
+               'date': hgutil.datestr(parent.date(),
+                                      '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
               })
 run_svn_info = util.register_subcommand('info')(run_svn_info)
 
@@ -221,7 +221,7 @@
 def version(ui, **opts):
     """Show current version of hg and hgsubversion.
     """
-    ui.status('hg: %s\n' % mutil.version())
+    ui.status('hg: %s\n' % hgutil.version())
     ui.status('svn bindings: %s\n' % svnwrap.version())
     ui.status('hgsubversion: %s\n' % util.version(ui))
 version = util.register_subcommand('version')(version)

changeset:   251:23b02f892d9b
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Fri Apr 10 17:12:04 2009 +0200
summary:     Fix up imports in __init__.py.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -13,6 +13,8 @@
 '''
 
 import os
+import sys
+import traceback
 
 from mercurial import commands
 from mercurial import hg
@@ -20,7 +22,6 @@
 
 from svn import core
 
-import svncommand
 import svncommands
 import tag_repo
 import util

changeset:   252:4d3bcd2f26ed
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Fri Apr 10 17:13:07 2009 +0200
summary:     Remove unused imports from stupid.py.

diff --git a/stupid.py b/stupid.py
--- a/stupid.py
+++ b/stupid.py
@@ -1,16 +1,12 @@
 import cStringIO
 import re
-import os
 
 from mercurial import patch
 from mercurial import node
 from mercurial import context
 from mercurial import revlog
-from mercurial import util as merc_util
 from svn import core
-from svn import delta
 
-import hg_delta_editor
 import svnwrap
 import svnexternals
 import util

changeset:   253:c3d5c4ae9c7c
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Fri Apr 10 23:09:59 2009 +0200
summary:     Work with simple command table instead of decorators.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -26,8 +26,6 @@
 import tag_repo
 import util
 
-from util import svn_subcommands, svn_commands_nourl
-
 def reposetup(ui, repo):
     if not util.is_svn_repo(repo):
         return
@@ -39,9 +37,9 @@
     '''see detailed help for list of subcommands'''
 
     # guess command if prefix
-    if subcommand not in svn_subcommands:
+    if subcommand not in svncommands.table:
         candidates = []
-        for c in svn_subcommands:
+        for c in svncommands.table:
             if c.startswith(subcommand):
                 candidates.append(c)
         if len(candidates) == 1:
@@ -49,8 +47,8 @@
 
     path = os.path.dirname(repo.path)
     try:
-        commandfunc = svn_subcommands[subcommand]
-        if commandfunc not in svn_commands_nourl:
+        commandfunc = svncommands.table[subcommand]
+        if commandfunc not in svncommands.nourl:
             opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
         return commandfunc(ui, args=args, hg_repo_path=path, repo=repo, **opts)
     except core.SubversionException, e:
@@ -111,7 +109,7 @@
           ('', 'username', '', 'username for authentication'),
           ('', 'password', '', 'password for authentication'),
           ],
-         svncommands.generate_help(),
+         svncommands._helpgen(),
          ),
     "svnclone":
         (svn_fetch,
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -16,8 +16,6 @@
 import util
 import utility_commands
 
-from util import generate_help, svn_subcommands, register_subcommand
-
 
 def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
          tag_locations='tags', authors=None, filemap=None, **opts):
@@ -98,8 +96,6 @@
                         raise hgutil.Abort(*e.args)
     util.swap_out_encoding(old_encoding)
 
-pull = util.register_subcommand('pull')(pull)
-
 
 def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
     """push revisions starting at a specified head back to Subversion.
@@ -166,10 +162,8 @@
                 if ctx.node() == oldest:
                     return
                 extra['branch'] = ctx.branch()
-            utility_commands.rebase_commits(ui, repo,
-                                            extrafn=extrafn,
-                                            sourcerev=needs_transplant,
-                                            **opts)
+            utility_commands.rebase(ui, repo, extrafn=extrafn,
+                                    sourcerev=needs_transplant, **opts)
             repo = hg.repository(ui, hge.path)
             for child in repo[replacement.node()].children():
                 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
@@ -186,9 +180,6 @@
         svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
     util.swap_out_encoding(old_encoding)
     return 0
-push = util.register_subcommand('push')(push)
-# for git expats
-dcommit = util.register_subcommand('dcommit')(push)
 
 
 def diff(ui, repo, hg_repo_path, **opts):
@@ -213,7 +204,6 @@
                                                   'text': False,
                                                   }))
     ui.write(cmdutil.filterdiff(''.join(it), base_rev))
-diff = util.register_subcommand('diff')(diff)
 
 
 def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
@@ -329,8 +319,6 @@
     tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
     pickle.dump(tagsinfo, tagsinfofile)
     tagsinfofile.close()
-rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
-rebuildmeta = util.command_needs_no_url(rebuildmeta)
 
 
 def help(ui, args=None, **opts):
@@ -338,9 +326,9 @@
     """
     if args:
         subcommand = args[0]
-        if subcommand not in svn_subcommands:
+        if subcommand not in table:
             candidates = []
-            for c in svn_subcommands:
+            for c in table:
                 if c.startswith(subcommand):
                     candidates.append(c)
             if len(candidates) == 1:
@@ -349,13 +337,12 @@
                 ui.status('Ambiguous command. Could have been:\n%s\n' %
                           ' '.join(candidates))
                 return
-        doc = svn_subcommands[subcommand].__doc__
+        doc = table[subcommand].__doc__
         if doc is None:
             doc = "No documentation available for %s." % subcommand
         ui.status(doc.strip(), '\n')
         return
-    ui.status(generate_help())
-help = register_subcommand('help')(help)
+    ui.status(_helpgen())
 
 
 def update(ui, args, repo, clean=False, **opts):
@@ -380,4 +367,27 @@
         ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in
                              answers]+['']))
     return 1
-update = register_subcommand('up')(update)
+
+
+nourl = ['rebuildmeta'] + utility_commands.nourl
+table = {
+    'pull': pull,
+    'push': push,
+    'dcommit': push,
+    'update': update,
+    'help': help,
+    'rebuildmeta': rebuildmeta,
+    'diff': diff,
+}
+
+table.update(utility_commands.table)
+
+
+def _helpgen():
+    ret = ['hg svn ...', '',
+           'subcommands for Subversion integration', '',
+           'list of subcommands:', '']
+    for name, func in sorted(table.items()):
+        short_description = (func.__doc__ or '').splitlines()[0]
+        ret.append(" %-10s  %s" % (name, short_description))
+    return '\n'.join(ret) + '\n'
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -5,18 +5,6 @@
 from mercurial import node
 from mercurial import util as hgutil
 
-svn_subcommands = { }
-def register_subcommand(name):
-    def inner(fn):
-        svn_subcommands[name] = fn
-        return fn
-    return inner
-
-svn_commands_nourl = set()
-def command_needs_no_url(fn):
-    svn_commands_nourl.add(fn)
-    return fn
-
 
 def version(ui):
     """Guess the version of hgsubversion.
@@ -27,18 +15,6 @@
     return node.hex(ver)[:12]
 
 
-def generate_help():
-    ret = ['hg svn ...', '',
-           'subcommands for Subversion integration', '',
-           'list of subcommands:', '']
-
-    for name, func in sorted(svn_subcommands.items()):
-        short_description = (func.__doc__ or '').splitlines()[0]
-        ret.append(" %-10s  %s" % (name, short_description))
-
-    return "\n".join(ret) + '\n'
-
-
 def normalize_url(svn_url):
     return svn_url.rstrip('/')
 
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -4,19 +4,18 @@
 from mercurial import cmdutil
 from mercurial import node
 from mercurial import util as hgutil
-from hgext import rebase
+from hgext import rebase as hgrebase
 
 import svnwrap
 import util
 import hg_delta_editor
 
-def print_wc_url(ui, repo, hg_repo_path, **opts):
+def url(ui, repo, hg_repo_path, **opts):
     """show the location (URL) of the Subversion repository
     """
     hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
                                            ui_=ui)
     ui.status(hge.url, '\n')
-print_wc_url = util.register_subcommand('url')(print_wc_url)
 
 
 def find_wc_parent_rev(ui, repo, hge, svn_commit_hashes):
@@ -29,7 +28,7 @@
     return workingctx
 
 
-def generate_ignore(ui, repo, hg_repo_path, force=False, **opts):
+def genignore(ui, repo, hg_repo_path, force=False, **opts):
     """generate .hgignore from svn:ignore properties.
     """
     ignpath = os.path.join(hg_repo_path, '.hgignore')
@@ -63,10 +62,9 @@
                     ignorefile.write('%s/%s\n' % (dir, prop))
                 else:
                     ignorefile.write('%s\n' % prop)
-generate_ignore = util.register_subcommand('genignore')(generate_ignore)
 
 
-def run_svn_info(ui, repo, hg_repo_path, **opts):
+def info(ui, repo, hg_repo_path, **opts):
     """show Subversion details similar to `svn info'
     """
     hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -111,10 +109,9 @@
                'date': hgutil.datestr(parent.date(),
                                       '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
               })
-run_svn_info = util.register_subcommand('info')(run_svn_info)
 
 
-def print_parent_revision(ui, repo, hg_repo_path, **opts):
+def parent(ui, repo, hg_repo_path, **opts):
     """show Mercurial & Subversion parents of the working dir or revision
     """
     hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -129,10 +126,9 @@
     else:
         ui.status('Working copy seems to have no parent svn revision.\n')
     return 0
-print_parent_revision = util.register_subcommand('parent')(print_parent_revision)
 
 
-def rebase_commits(ui, repo, extrafn=None, sourcerev=None, **opts):
+def rebase(ui, repo, extrafn=None, sourcerev=None, **opts):
     """rebase current unpushed revisions onto the Subversion head
 
     This moves a line of development from making its own head to the top of
@@ -175,13 +171,12 @@
         ui.status('Already up to date!\n')
         return 0
     # TODO this is really hacky, there must be a more direct way
-    return rebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
+    return hgrebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
                          base=node.hex(sourcerev),
                          extrafn=extrafn)
-rebase_commits = util.register_subcommand('rebase')(rebase_commits)
 
 
-def show_outgoing_to_svn(ui, repo, hg_repo_path, **opts):
+def outgoing(ui, repo, hg_repo_path, **opts):
     """show changesets not found in the Subversion repository
     """
     hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -195,10 +190,9 @@
     displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False)
     for node in reversed(o_r):
         displayer.show(repo[node])
-show_outgoing_to_svn = util.register_subcommand('outgoing')(show_outgoing_to_svn)
 
 
-def list_authors(ui, args, authors=None, **opts):
+def listauthors(ui, args, authors=None, **opts):
     """list all authors in a Subversion repository
     """
     if not len(args):
@@ -214,8 +208,6 @@
         authorfile.close()
     else:
         ui.status('%s\n' % '\n'.join(sorted(author_set)))
-list_authors = util.register_subcommand('listauthors')(list_authors)
-list_authors = util.command_needs_no_url(list_authors)
 
 
 def version(ui, **opts):
@@ -224,5 +216,16 @@
     ui.status('hg: %s\n' % hgutil.version())
     ui.status('svn bindings: %s\n' % svnwrap.version())
     ui.status('hgsubversion: %s\n' % util.version(ui))
-version = util.register_subcommand('version')(version)
-version = util.command_needs_no_url(version)
+
+
+nourl = ['version', 'listauthors']
+table = {
+    'url': url,
+    'genignore': genignore,
+    'info': info,
+    'parent': parent,
+    'outgoing': outgoing,
+    'listauthors': listauthors,
+    'version': version,
+    'rebase': rebase,
+}

changeset:   254:9ba31af57e4b
user:        Dirkjan Ochtman <dirkjan@ochtman.nl>
date:        Fri Apr 10 17:29:45 2009 +0200
summary:     Move utility_commands.find_wc_parent_rev() to cmdutil.parentrev().

diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -5,6 +5,7 @@
 
 from svn import core
 
+import util
 import svnwrap
 import svnexternals
 
@@ -34,6 +35,17 @@
     return diff
 
 
+def parentrev(ui, repo, hge, svn_commit_hashes):
+    """Find the svn parent revision of the repo's dirstate.
+    """
+    workingctx = repo.parents()[0]
+    outrev = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+                                     workingctx.node())
+    if outrev:
+        workingctx = repo[outrev[-1]].parents()[0]
+    return workingctx
+
+
 def replay_convert_rev(hg_editor, svn, r):
     hg_editor.set_current_rev(r)
     svn.get_replay(r.revnum, hg_editor)
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -1,12 +1,13 @@
 import os
 
 import mercurial
-from mercurial import cmdutil
+from mercurial import cmdutil as hgcmdutil
 from mercurial import node
 from mercurial import util as hgutil
 from hgext import rebase as hgrebase
 
 import svnwrap
+import cmdutil
 import util
 import hg_delta_editor
 
@@ -18,16 +19,6 @@
     ui.status(hge.url, '\n')
 
 
-def find_wc_parent_rev(ui, repo, hge, svn_commit_hashes):
-    """Find the svn parent revision of the repo's dirstate.
-    """
-    workingctx = repo.parents()[0]
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingctx.node())
-    if o_r:
-        workingctx = repo[o_r[-1]].parents()[0]
-    return workingctx
-
-
 def genignore(ui, repo, hg_repo_path, force=False, **opts):
     """generate .hgignore from svn:ignore properties.
     """
@@ -40,7 +31,7 @@
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+    parent = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
     r, br = svn_commit_hashes[parent.node()]
     if br == None:
         branchpath = 'trunk'
@@ -71,7 +62,7 @@
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+    parent = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
     pn = parent.node()
     if pn not in svn_commit_hashes:
         ui.status('Not a child of an svn revision.\n')
@@ -118,7 +109,7 @@
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    ha = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+    ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
     if ha.node() != node.nullid:
         r, br = svn_commit_hashes[ha.node()]
         ui.status('Working copy parent revision is %s: r%s on %s\n' %
@@ -187,7 +178,7 @@
     if not (o_r and len(o_r)):
         ui.status('No outgoing changes found.\n')
         return 0
-    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False)
+    displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
     for node in reversed(o_r):
         displayer.show(repo[node])
 

changeset:   255:246aaefb1cc0
user:        Nat Williams <nat.williams@gmail.com>
date:        Thu Apr 09 22:08:06 2009 -0500
summary:     Add a basic incoming command.

diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -97,6 +97,55 @@
     util.swap_out_encoding(old_encoding)
 
 
+def incoming(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
+             tag_locations='tags', authors=None, filemap=None, **opts):
+    """show incoming revisions from Subversion
+    """
+    svn_url = util.normalize_url(svn_url)
+
+    initializing_repo = False
+    user = opts.get('username', hgutil.getuser())
+    passwd = opts.get('password', '')
+    svn = svnwrap.SubversionRepo(svn_url, user, passwd)
+    author_host = "@%s" % svn.uuid
+    tag_locations = tag_locations.split(',')
+    hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+                                                 ui_=ui,
+                                                 subdir=svn.subdir,
+                                                 author_host=author_host,
+                                                 tag_locations=tag_locations,
+                                                 authors=authors,
+                                                 filemap=filemap)
+    if os.path.exists(hg_editor.uuid_file):
+        uuid = open(hg_editor.uuid_file).read()
+        assert uuid == svn.uuid
+        start = hg_editor.last_known_revision()
+    else:
+        open(hg_editor.uuid_file, 'w').write(svn.uuid)
+        open(hg_editor.svn_url_file, 'w').write(svn_url)
+        initializing_repo = True
+        start = skipto_rev
+
+    if initializing_repo and start > 0:
+        raise hgutil.Abort('Revision skipping at repository initialization '
+                           'remains unimplemented.')
+
+    rev_stuff = (('revision', 'revnum'),
+                 ('user', 'author'),
+                 ('date', 'date'),
+                 ('message', 'message')
+                )
+
+    ui.status('incoming changes from %s\n' % svn_url)
+
+    for r in svn.revisions(start=start):
+        ui.status('\n')
+        for label, attr in rev_stuff:
+            l1 = label+':'
+            ui.status('%s%s\n' % (l1.ljust(13),
+                                  str(r.__getattribute__(attr)).strip(), ))
+
+
 def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
     """push revisions starting at a specified head back to Subversion.
     """
@@ -378,6 +427,7 @@
     'help': help,
     'rebuildmeta': rebuildmeta,
     'diff': diff,
+    'incoming': incoming,
 }
 
 table.update(utility_commands.table)

changeset:   256:7932d098cb5f
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 10 18:47:18 2009 -0500
summary:     Refactor commands to wrap their hg equivalent adding a --svn flag where sane.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -17,6 +17,7 @@
 import traceback
 
 from mercurial import commands
+from mercurial import extensions
 from mercurial import hg
 from mercurial import util as hgutil
 
@@ -32,6 +33,45 @@
 
     repo.__class__ = tag_repo.generate_repo_class(ui, repo)
 
+def uisetup(ui):
+    """Do our UI setup.
+
+    Does the following wrappings:
+     * parent -> utility_commands.parent
+     * outgoing -> utility_commands.outgoing
+     """
+    entry = extensions.wrapcommand(commands.table, 'parents',
+                                   utility_commands.parent)
+    entry[1].append(('', 'svn', None, "show parent svn revision instead"))
+    entry = extensions.wrapcommand(commands.table, 'outgoing',
+                                   utility_commands.outgoing)
+    entry[1].append(('', 'svn', None, "show revisions outgoing to subversion"))
+    entry = extensions.wrapcommand(commands.table, 'diff',
+                                   svncommands.diff)
+    entry[1].append(('', 'svn', None,
+                     "show svn-style diffs, default against svn parent"))
+    entry = extensions.wrapcommand(commands.table, 'push',
+                                   svncommands.push)
+    entry[1].append(('', 'svn', None, "push to subversion"))
+    entry[1].append(('', 'svn-stupid', None, "use stupid replay during push to svn"))
+    entry = extensions.wrapcommand(commands.table, 'pull',
+                                   svncommands.pull)
+    entry[1].append(('', 'svn', None, "pull from subversion"))
+    entry[1].append(('', 'svn-stupid', None, "use stupid replay during pull from svn"))
+
+    entry = extensions.wrapcommand(commands.table, 'clone',
+                                   svncommands.clone)
+    entry[1].extend([#('', 'skipto-rev', '0', 'skip commits before this revision.'),
+                     ('', 'svn-stupid', False, 'be stupid and use diffy replay.'),
+                     ('', 'svn-tag-locations', 'tags', 'Relative path to Subversion tags.'),
+                     ('', 'svn-authors', '', 'username mapping filename'),
+                     ('', 'svn-filemap', '',
+                      'remap file to exclude paths or include only certain paths'),
+                     ])
+#         (svn_fetch,
+#          ,
+#          'hg svnclone source [dest]'),
+
 
 def svn(ui, repo, subcommand, *args, **opts):
     '''see detailed help for list of subcommands'''
@@ -70,33 +110,7 @@
             raise
 
 
-def svn_fetch(ui, svn_url, hg_repo_path=None, **opts):
-    '''clone Subversion repository to a local Mercurial repository.
 
-    If no destination directory name is specified, it defaults to the
-    basename of the source plus "-hg".
-
-    You can specify multiple paths for the location of tags using comma
-    separated values.
-    '''
-    if not hg_repo_path:
-        hg_repo_path = hg.defaultdest(svn_url) + "-hg"
-        ui.status("Assuming destination %s\n" % hg_repo_path)
-    should_update = not os.path.exists(hg_repo_path)
-    svn_url = util.normalize_url(svn_url)
-    try:
-        res = svncommands.pull(ui, svn_url, hg_repo_path, **opts)
-    except core.SubversionException, e:
-        if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
-            raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
-                     'Please try running svn ls on that url first.')
-        raise
-    if (res is None or res == 0) and should_update:
-        repo = hg.repository(ui, hg_repo_path)
-        commands.update(ui, repo, repo['tip'].node())
-    return res
-
-commands.norepo += " svnclone"
 cmdtable = {
     "svn":
         (svn,
@@ -111,16 +125,4 @@
           ],
          svncommands._helpgen(),
          ),
-    "svnclone":
-        (svn_fetch,
-         [('S', 'skipto-rev', '0', 'skip commits before this revision.'),
-          ('', 'stupid', False, 'be stupid and use diffy replay.'),
-          ('T', 'tag-locations', 'tags', 'Relative path to Subversion tags.'),
-          ('A', 'authors', '', 'username mapping filename'),
-          ('', 'filemap', '',
-           'remap file to exclude paths or include only certain paths'),
-          ('', 'username', '', 'username for authentication'),
-          ('', 'password', '', 'password for authentication'),
-         ],
-         'hg svnclone source [dest]'),
 }
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,5 +1,6 @@
 #!/usr/bin/python
 import re
+import os
 
 from mercurial import util as hgutil
 
@@ -22,15 +23,21 @@
     """Exception raised when you try and commit without files.
     """
 
+def formatrev(rev):
+    if rev == -1:
+        return '\t(working copy)'
+    return '\t(revision %d)' % rev
 
-def filterdiff(diff, base_revision):
+
+def filterdiff(diff, oldrev, newrev):
     diff = newfile_devnull_re.sub(r'--- \1\t(revision 0)' '\n'
                                   r'+++ \1\t(working copy)',
                                   diff)
-    diff = a_re.sub(r'--- \1'+ ('\t(revision %d)' % base_revision), diff)
-    diff = b_re.sub(r'+++ \1' + '\t(working copy)', diff)
-    diff = devnull_re.sub(r'\1 /dev/null' '\t(working copy)', diff)
-
+    oldrev = formatrev(oldrev)
+    newrev = formatrev(newrev)
+    diff = a_re.sub(r'--- \1'+ oldrev, diff)
+    diff = b_re.sub(r'+++ \1' + newrev, diff)
+    diff = devnull_re.sub(r'\1 /dev/null\t(working copy)', diff)
     diff = header_re.sub(r'Index: \1' + '\n' + ('=' * 67), diff)
     return diff
 
@@ -270,3 +277,17 @@
             raise hgutil.Abort('Base text was out of date, maybe rebase?')
         else:
             raise
+
+def filecheck(path):
+    for x in ('locks', 'hooks', 'format', 'db', ):
+        if not os.path.exists(os.path.join(path, x)):
+            return False
+    return True
+
+
+def issvnurl(url):
+    return url.startswith('svn+') or (
+        url.startswith('file://') and
+        reduce(lambda x,y: x and y,
+               map(lambda p: os.path.exists(os.path.join(url[7:], p)),
+                   ('locks', 'hooks', 'format', 'db', ))))
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,6 +1,8 @@
 import os
 import cPickle as pickle
 
+from mercurial import cmdutil as hgcmdutil
+from mercurial import commands
 from mercurial import hg
 from mercurial import node
 from mercurial import patch
@@ -17,14 +19,63 @@
 import utility_commands
 
 
-def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
-         tag_locations='tags', authors=None, filemap=None, **opts):
+def clone(orig, ui, source, dest=None, **opts):
+    '''clone Subversion repository to a local Mercurial repository.
+
+    If no destination directory name is specified, it defaults to the
+    basename of the source plus "-hg".
+
+    You can specify multiple paths for the location of tags using comma
+    separated values.
+    '''
+    svnurl = ui.expandpath(source)
+    if not cmdutil.issvnurl(svnurl):
+        orig(ui, repo, source=source, dest=dest, *args, **opts)
+
+    if not dest:
+        dest = hg.defaultdest(source) + '-hg'
+        ui.status("Assuming destination %s\n" % dest)
+
+    if os.path.exists(dest):
+        raise hgutil.Abort("destination '%s' already exists" % dest)
+    url = util.normalize_url(svnurl)
+    res = -1
+    try:
+        try:
+            res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
+                       source=url, create_new_dest=dest, **opts)
+        except core.SubversionException, e:
+            if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
+                raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+                         'Please try running svn ls on that url first.')
+            raise
+    finally:
+        if os.path.exists(dest):
+            repo = hg.repository(ui, dest)
+            fp = repo.opener("hgrc", "w", text=True)
+            fp.write("[paths]\n")
+            # percent needs to be escaped for ConfigParser
+            fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
+            fp.close()
+            if res is None or res == 0:
+                commands.update(ui, repo, repo['tip'].node())
+
+    return res
+
+
+def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
+         *args, **opts):
     """pull new revisions from Subversion
     """
+    url = ui.expandpath(source)
+    if not (cmdutil.issvnurl(url) or svn or create_new_dest):
+        orig(ui, repo, source=source, *args, **opts)
+    svn_url = url
     svn_url = util.normalize_url(svn_url)
     old_encoding = util.swap_out_encoding()
-    skipto_rev=int(skipto_rev)
-    have_replay = not stupid
+    # TODO implement skipto support
+    skipto_rev = 0
+    have_replay = not svn_stupid
     if have_replay and not callable(
         delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
         ui.status('You are using old Subversion SWIG bindings. Replay will not'
@@ -38,14 +89,21 @@
     passwd = opts.get('password', '')
     svn = svnwrap.SubversionRepo(svn_url, user, passwd)
     author_host = "@%s" % svn.uuid
-    tag_locations = tag_locations.split(',')
-    hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                                 ui_=ui,
-                                                 subdir=svn.subdir,
-                                                 author_host=author_host,
-                                                 tag_locations=tag_locations,
-                                                 authors=authors,
-                                                 filemap=filemap)
+    # TODO these should be configurable again, but I'm torn on how.
+    # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
+    tag_locations = ['tags', ]
+    authors = None
+    filemap = None
+    if repo:
+        hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo)
+    else:
+        hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui,
+                                                     path=create_new_dest,
+                                                     subdir=svn.subdir,
+                                                     author_host=author_host,
+                                                     tag_locations=tag_locations,
+                                                     authors=authors,
+                                                     filemap=filemap)
     if os.path.exists(hg_editor.uuid_file):
         uuid = open(hg_editor.uuid_file).read()
         assert uuid == svn.uuid
@@ -146,12 +204,15 @@
                                   str(r.__getattribute__(attr)).strip(), ))
 
 
-def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
+def push(orig, ui, repo, dest=None, **opts):
     """push revisions starting at a specified head back to Subversion.
     """
+    svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
+    if not cmdutil.issvnurl(svnurl):
+        orig(ui, repo, dest=dest, *args, **opts)
     old_encoding = util.swap_out_encoding()
-    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                           ui_=ui)
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+    assert svnurl == hge.url
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
     user = opts.get('username', hgutil.getuser())
@@ -187,14 +248,14 @@
         # 2. Commit oldest revision that needs to be pushed
         base_revision = svn_commit_hashes[base_n][0]
         try:
-            cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svn_url,
+            cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svnurl,
                                     base_revision, user, passwd)
         except cmdutil.NoFilesException:
             ui.warn("Could not push revision %s because it had no changes in svn.\n" %
                      old_ctx)
             return 1
         # 3. Fetch revisions from svn
-        r = pull(ui, svn_url, hg_repo_path, stupid=stupid,
+        r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
                  username=user, password=passwd)
         assert not r or r == 0
         # 4. Find the new head of the target branch
@@ -225,25 +286,32 @@
                         if children:
                             child = children[0]
                         rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
-        hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
+        hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui)
         svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
     util.swap_out_encoding(old_encoding)
     return 0
 
 
-def diff(ui, repo, hg_repo_path, **opts):
+def diff(orig, ui, repo, *args, **opts):
     """show a diff of the most recent revision against its parent from svn
     """
-    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                           ui_=ui)
+    if not opts.get('svn', False) or opts.get('change', None):
+        return orig(ui, repo, *args, **opts)
+    svn_commit_hashes = {}
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    parent = repo.parents()[0]
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, parent.node())
-    if o_r:
-        parent = repo[o_r[-1]].parents()[0]
-    base_rev, _junk = svn_commit_hashes[parent.node()]
-    it = patch.diff(repo, parent.node(), None,
+    if not opts.get('rev', None):
+        parent = repo.parents()[0]
+        o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+                                      parent.node())
+        if o_r:
+            parent = repo[o_r[-1]].parents()[0]
+        opts['rev'] = ['%s:.' % node.hex(parent.node()), ]
+    node1, node2 = hgcmdutil.revpair(repo, opts['rev'])
+    baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', ))
+    newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', ))
+    it = patch.diff(repo, node1, node2,
                     opts=patch.diffopts(ui, opts={'git': True,
                                                   'show_function': False,
                                                   'ignore_all_space': False,
@@ -252,7 +320,7 @@
                                                   'unified': True,
                                                   'text': False,
                                                   }))
-    ui.write(cmdutil.filterdiff(''.join(it), base_rev))
+    ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
 
 
 def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
@@ -420,13 +488,9 @@
 
 nourl = ['rebuildmeta'] + utility_commands.nourl
 table = {
-    'pull': pull,
-    'push': push,
-    'dcommit': push,
     'update': update,
     'help': help,
     'rebuildmeta': rebuildmeta,
-    'diff': diff,
     'incoming': incoming,
 }
 
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -15,8 +15,10 @@
     return node.hex(ver)[:12]
 
 
-def normalize_url(svn_url):
-    return svn_url.rstrip('/')
+def normalize_url(svnurl):
+    if svnurl.startswith('svn+http'):
+        svnurl = svnurl[4:]
+    return svnurl.rstrip('/')
 
 
 def wipe_all_files(hg_wc_path):
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -102,20 +102,19 @@
               })
 
 
-def parent(ui, repo, hg_repo_path, **opts):
+def parent(orig, ui, repo, *args, **opts):
     """show Mercurial & Subversion parents of the working dir or revision
     """
-    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                           ui_=ui)
+    if not opts.get('svn', False):
+        return orig(ui, repo, *args, **opts)
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
     ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
-    if ha.node() != node.nullid:
-        r, br = svn_commit_hashes[ha.node()]
-        ui.status('Working copy parent revision is %s: r%s on %s\n' %
-                  (ha, r, br or 'trunk'))
-    else:
-        ui.status('Working copy seems to have no parent svn revision.\n')
+    if ha.node() == node.nullid:
+        raise mutil.Abort('No parent svn revision!')
+    displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
+    displayer.show(ha)
     return 0
 
 
@@ -167,16 +166,20 @@
                          extrafn=extrafn)
 
 
-def outgoing(ui, repo, hg_repo_path, **opts):
+def outgoing(orig, ui, repo, dest=None, *args, **opts):
     """show changesets not found in the Subversion repository
     """
-    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
-                                           ui_=ui)
+    svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
+    if not (cmdutil.issvnurl(svnurl) or opts.get('svn', False)):
+        return orig(ui, repo, dest, *args, **opts)
+
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, repo.parents()[0].node())
+    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+                                  repo.parents()[0].node())
     if not (o_r and len(o_r)):
-        ui.status('No outgoing changes found.\n')
+        ui.status('no changes found\n')
         return 0
     displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
     for node in reversed(o_r):
@@ -214,8 +217,6 @@
     'url': url,
     'genignore': genignore,
     'info': info,
-    'parent': parent,
-    'outgoing': outgoing,
     'listauthors': listauthors,
     'version': version,
     'rebase': rebase,

changeset:   257:ffccf0080e54
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 10 22:38:29 2009 -0500
summary:     Move wrappers for hg commands to their own module.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -18,7 +18,6 @@
 
 from mercurial import commands
 from mercurial import extensions
-from mercurial import hg
 from mercurial import util as hgutil
 
 from svn import core
@@ -26,6 +25,7 @@
 import svncommands
 import tag_repo
 import util
+import wrappers
 
 def reposetup(ui, repo):
     if not util.is_svn_repo(repo):
@@ -41,26 +41,26 @@
      * outgoing -> utility_commands.outgoing
      """
     entry = extensions.wrapcommand(commands.table, 'parents',
-                                   utility_commands.parent)
+                                   wrappers.parent)
     entry[1].append(('', 'svn', None, "show parent svn revision instead"))
     entry = extensions.wrapcommand(commands.table, 'outgoing',
-                                   utility_commands.outgoing)
+                                   wrappers.outgoing)
     entry[1].append(('', 'svn', None, "show revisions outgoing to subversion"))
     entry = extensions.wrapcommand(commands.table, 'diff',
-                                   svncommands.diff)
+                                   wrappers.diff)
     entry[1].append(('', 'svn', None,
                      "show svn-style diffs, default against svn parent"))
     entry = extensions.wrapcommand(commands.table, 'push',
-                                   svncommands.push)
+                                   wrappers.push)
     entry[1].append(('', 'svn', None, "push to subversion"))
     entry[1].append(('', 'svn-stupid', None, "use stupid replay during push to svn"))
     entry = extensions.wrapcommand(commands.table, 'pull',
-                                   svncommands.pull)
+                                   wrappers.pull)
     entry[1].append(('', 'svn', None, "pull from subversion"))
     entry[1].append(('', 'svn-stupid', None, "use stupid replay during pull from svn"))
 
     entry = extensions.wrapcommand(commands.table, 'clone',
-                                   svncommands.clone)
+                                   wrappers.clone)
     entry[1].extend([#('', 'skipto-rev', '0', 'skip commits before this revision.'),
                      ('', 'svn-stupid', False, 'be stupid and use diffy replay.'),
                      ('', 'svn-tag-locations', 'tags', 'Relative path to Subversion tags.'),
@@ -68,9 +68,6 @@
                      ('', 'svn-filemap', '',
                       'remap file to exclude paths or include only certain paths'),
                      ])
-#         (svn_fetch,
-#          ,
-#          'hg svnclone source [dest]'),
 
 
 def svn(ui, repo, subcommand, *args, **opts):
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 import re
 import os
+import urllib
 
 from mercurial import util as hgutil
 
@@ -278,16 +279,19 @@
         else:
             raise
 
-def filecheck(path):
-    for x in ('locks', 'hooks', 'format', 'db', ):
-        if not os.path.exists(os.path.join(path, x)):
-            return False
     return True
 
+def islocalrepo(url):
+    if not url.startswith('file:///'):
+        return False
+    path = urllib.unquote(url[len('file://'):])
+    while '/' in path:
+        if reduce(lambda x,y: x and y,
+                  map(lambda p: os.path.exists(os.path.join(path, p)),
+                      ('hooks', 'format', 'db', ))):
+            return True
+        path = path.rsplit('/', 1)[0]
+    return False
 
 def issvnurl(url):
-    return url.startswith('svn+') or (
-        url.startswith('file://') and
-        reduce(lambda x,y: x and y,
-               map(lambda p: os.path.exists(os.path.join(url[7:], p)),
-                   ('locks', 'hooks', 'format', 'db', ))))
+    return url.startswith('svn') or islocalrepo(url)
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -710,9 +710,9 @@
         self.clear_current_info()
 
     def authorforsvnauthor(self, author):
-        if(author in self.authors):
+        if author in self.authors:
             return self.authors[author]
-        return '%s%s' %(author, self.author_host)
+        return '%s%s' % (author, self.author_host)
 
     def svnauthorforauthor(self, author):
         for svnauthor, hgauthor in self.authors.iteritems():
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,160 +1,17 @@
 import os
 import cPickle as pickle
 
-from mercurial import cmdutil as hgcmdutil
-from mercurial import commands
 from mercurial import hg
 from mercurial import node
-from mercurial import patch
 from mercurial import util as hgutil
 
-from svn import core
-from svn import delta
 
 import hg_delta_editor
 import svnwrap
-import stupid as stupidmod
-import cmdutil
 import util
 import utility_commands
 
 
-def clone(orig, ui, source, dest=None, **opts):
-    '''clone Subversion repository to a local Mercurial repository.
-
-    If no destination directory name is specified, it defaults to the
-    basename of the source plus "-hg".
-
-    You can specify multiple paths for the location of tags using comma
-    separated values.
-    '''
-    svnurl = ui.expandpath(source)
-    if not cmdutil.issvnurl(svnurl):
-        orig(ui, repo, source=source, dest=dest, *args, **opts)
-
-    if not dest:
-        dest = hg.defaultdest(source) + '-hg'
-        ui.status("Assuming destination %s\n" % dest)
-
-    if os.path.exists(dest):
-        raise hgutil.Abort("destination '%s' already exists" % dest)
-    url = util.normalize_url(svnurl)
-    res = -1
-    try:
-        try:
-            res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
-                       source=url, create_new_dest=dest, **opts)
-        except core.SubversionException, e:
-            if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
-                raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
-                         'Please try running svn ls on that url first.')
-            raise
-    finally:
-        if os.path.exists(dest):
-            repo = hg.repository(ui, dest)
-            fp = repo.opener("hgrc", "w", text=True)
-            fp.write("[paths]\n")
-            # percent needs to be escaped for ConfigParser
-            fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
-            fp.close()
-            if res is None or res == 0:
-                commands.update(ui, repo, repo['tip'].node())
-
-    return res
-
-
-def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
-         *args, **opts):
-    """pull new revisions from Subversion
-    """
-    url = ui.expandpath(source)
-    if not (cmdutil.issvnurl(url) or svn or create_new_dest):
-        orig(ui, repo, source=source, *args, **opts)
-    svn_url = url
-    svn_url = util.normalize_url(svn_url)
-    old_encoding = util.swap_out_encoding()
-    # TODO implement skipto support
-    skipto_rev = 0
-    have_replay = not svn_stupid
-    if have_replay and not callable(
-        delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
-        ui.status('You are using old Subversion SWIG bindings. Replay will not'
-                  ' work until you upgrade to 1.5.0 or newer. Falling back to'
-                  ' a slower method that may be buggier. Please upgrade, or'
-                  ' contribute a patch to use the ctypes bindings instead'
-                  ' of SWIG.\n')
-        have_replay = False
-    initializing_repo = False
-    user = opts.get('username', hgutil.getuser())
-    passwd = opts.get('password', '')
-    svn = svnwrap.SubversionRepo(svn_url, user, passwd)
-    author_host = "@%s" % svn.uuid
-    # TODO these should be configurable again, but I'm torn on how.
-    # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
-    tag_locations = ['tags', ]
-    authors = None
-    filemap = None
-    if repo:
-        hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo)
-    else:
-        hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui,
-                                                     path=create_new_dest,
-                                                     subdir=svn.subdir,
-                                                     author_host=author_host,
-                                                     tag_locations=tag_locations,
-                                                     authors=authors,
-                                                     filemap=filemap)
-    if os.path.exists(hg_editor.uuid_file):
-        uuid = open(hg_editor.uuid_file).read()
-        assert uuid == svn.uuid
-        start = hg_editor.last_known_revision()
-    else:
-        open(hg_editor.uuid_file, 'w').write(svn.uuid)
-        open(hg_editor.svn_url_file, 'w').write(svn_url)
-        initializing_repo = True
-        start = skipto_rev
-
-    if initializing_repo and start > 0:
-        raise hgutil.Abort('Revision skipping at repository initialization '
-                           'remains unimplemented.')
-
-    # start converting revisions
-    for r in svn.revisions(start=start):
-        valid = True
-        hg_editor.update_branch_tag_map_for_rev(r)
-        for p in r.paths:
-            if hg_editor._is_path_valid(p):
-                valid = True
-                break
-        if valid:
-            # got a 502? Try more than once!
-            tries = 0
-            converted = False
-            while not converted:
-                try:
-                    util.describe_revision(ui, r)
-                    if have_replay:
-                        try:
-                            cmdutil.replay_convert_rev(hg_editor, svn, r)
-                        except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
-                            ui.status('%s\n' % e.message)
-                            stupidmod.print_your_svn_is_old_message(ui)
-                            have_replay = False
-                            stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
-                    else:
-                        stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
-                    converted = True
-                except core.SubversionException, e: #pragma: no cover
-                    if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
-                        and '502' in str(e)
-                        and tries < 3):
-                        tries += 1
-                        ui.status('Got a 502, retrying (%s)\n' % tries)
-                    else:
-                        raise hgutil.Abort(*e.args)
-    util.swap_out_encoding(old_encoding)
-
-
 def incoming(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
              tag_locations='tags', authors=None, filemap=None, **opts):
     """show incoming revisions from Subversion
@@ -204,125 +61,6 @@
                                   str(r.__getattribute__(attr)).strip(), ))
 
 
-def push(orig, ui, repo, dest=None, **opts):
-    """push revisions starting at a specified head back to Subversion.
-    """
-    svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
-    if not cmdutil.issvnurl(svnurl):
-        orig(ui, repo, dest=dest, *args, **opts)
-    old_encoding = util.swap_out_encoding()
-    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
-    assert svnurl == hge.url
-    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
-                                 hge.revmap.iterkeys()))
-    user = opts.get('username', hgutil.getuser())
-    passwd = opts.get('password', '')
-
-    # Strategy:
-    # 1. Find all outgoing commits from this head
-    if len(repo.parents()) != 1:
-        ui.status('Cowardly refusing to push branch merge')
-        return 1
-    workingrev = repo.parents()[0]
-    outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
-    if not (outgoing and len(outgoing)):
-        ui.status('No revisions to push.')
-        return 0
-    while outgoing:
-        oldest = outgoing.pop(-1)
-        old_ctx = repo[oldest]
-        if len(old_ctx.parents()) != 1:
-            ui.status('Found a branch merge, this needs discussion and '
-                      'implementation.')
-            return 1
-        base_n = old_ctx.parents()[0].node()
-        old_children = repo[base_n].children()
-        svnbranch = repo[base_n].branch()
-        oldtip = base_n
-        samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
-                              and c.node() in svn_commit_hashes]
-        while samebranchchildren:
-            oldtip = samebranchchildren[0].node()
-            samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
-                                  and c.node() in svn_commit_hashes]
-        # 2. Commit oldest revision that needs to be pushed
-        base_revision = svn_commit_hashes[base_n][0]
-        try:
-            cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svnurl,
-                                    base_revision, user, passwd)
-        except cmdutil.NoFilesException:
-            ui.warn("Could not push revision %s because it had no changes in svn.\n" %
-                     old_ctx)
-            return 1
-        # 3. Fetch revisions from svn
-        r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
-                 username=user, password=passwd)
-        assert not r or r == 0
-        # 4. Find the new head of the target branch
-        repo = hg.repository(ui, hge.path)
-        oldtipctx = repo[oldtip]
-        replacement = [c for c in oldtipctx.children() if c not in old_children
-                       and c.branch() == oldtipctx.branch()]
-        assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
-        replacement = replacement[0]
-        # 5. Rebase all children of the currently-pushing rev to the new branch
-        heads = repo.heads(old_ctx.node())
-        for needs_transplant in heads:
-            def extrafn(ctx, extra):
-                if ctx.node() == oldest:
-                    return
-                extra['branch'] = ctx.branch()
-            utility_commands.rebase(ui, repo, extrafn=extrafn,
-                                    sourcerev=needs_transplant, **opts)
-            repo = hg.repository(ui, hge.path)
-            for child in repo[replacement.node()].children():
-                rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
-                if rebasesrc in outgoing:
-                    while rebasesrc in outgoing:
-                        rebsrcindex = outgoing.index(rebasesrc)
-                        outgoing = (outgoing[0:rebsrcindex] +
-                                    [child.node(), ] + outgoing[rebsrcindex+1:])
-                        children = [c for c in child.children() if c.branch() == child.branch()]
-                        if children:
-                            child = children[0]
-                        rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
-        hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui)
-        svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
-    util.swap_out_encoding(old_encoding)
-    return 0
-
-
-def diff(orig, ui, repo, *args, **opts):
-    """show a diff of the most recent revision against its parent from svn
-    """
-    if not opts.get('svn', False) or opts.get('change', None):
-        return orig(ui, repo, *args, **opts)
-    svn_commit_hashes = {}
-    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
-    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
-                                 hge.revmap.iterkeys()))
-    if not opts.get('rev', None):
-        parent = repo.parents()[0]
-        o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
-                                      parent.node())
-        if o_r:
-            parent = repo[o_r[-1]].parents()[0]
-        opts['rev'] = ['%s:.' % node.hex(parent.node()), ]
-    node1, node2 = hgcmdutil.revpair(repo, opts['rev'])
-    baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', ))
-    newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', ))
-    it = patch.diff(repo, node1, node2,
-                    opts=patch.diffopts(ui, opts={'git': True,
-                                                  'show_function': False,
-                                                  'ignore_all_space': False,
-                                                  'ignore_space_change': False,
-                                                  'ignore_blank_lines': False,
-                                                  'unified': True,
-                                                  'text': False,
-                                                  }))
-    ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
-
-
 def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
     """rebuild hgsubversion metadata using values stored in revisions
     """
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -1,7 +1,5 @@
 import os
 
-import mercurial
-from mercurial import cmdutil as hgcmdutil
 from mercurial import node
 from mercurial import util as hgutil
 from hgext import rebase as hgrebase
@@ -102,22 +100,6 @@
               })
 
 
-def parent(orig, ui, repo, *args, **opts):
-    """show Mercurial & Subversion parents of the working dir or revision
-    """
-    if not opts.get('svn', False):
-        return orig(ui, repo, *args, **opts)
-    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
-    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
-                                 hge.revmap.iterkeys()))
-    ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
-    if ha.node() == node.nullid:
-        raise mutil.Abort('No parent svn revision!')
-    displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
-    displayer.show(ha)
-    return 0
-
-
 def rebase(ui, repo, extrafn=None, sourcerev=None, **opts):
     """rebase current unpushed revisions onto the Subversion head
 
@@ -166,26 +148,6 @@
                          extrafn=extrafn)
 
 
-def outgoing(orig, ui, repo, dest=None, *args, **opts):
-    """show changesets not found in the Subversion repository
-    """
-    svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
-    if not (cmdutil.issvnurl(svnurl) or opts.get('svn', False)):
-        return orig(ui, repo, dest, *args, **opts)
-
-    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
-    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
-                                 hge.revmap.iterkeys()))
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
-                                  repo.parents()[0].node())
-    if not (o_r and len(o_r)):
-        ui.status('no changes found\n')
-        return 0
-    displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
-    for node in reversed(o_r):
-        displayer.show(repo[node])
-
-
 def listauthors(ui, args, authors=None, **opts):
     """list all authors in a Subversion repository
     """
diff --git a/wrappers.py b/wrappers.py
new file mode 100644
--- /dev/null
+++ b/wrappers.py
@@ -0,0 +1,320 @@
+import os
+
+from mercurial import cmdutil as hgcmdutil
+from mercurial import commands
+from mercurial import patch
+from mercurial import hg
+from mercurial import util as hgutil
+from mercurial import node
+
+from svn import core
+from svn import delta
+
+import cmdutil
+import hg_delta_editor
+import stupid as stupidmod
+import svnwrap
+import util
+import utility_commands
+
+def parent(orig, ui, repo, *args, **opts):
+    """show Mercurial & Subversion parents of the working dir or revision
+    """
+    if not opts.get('svn', False):
+        return orig(ui, repo, *args, **opts)
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
+    if ha.node() == node.nullid:
+        raise hgutil.Abort('No parent svn revision!')
+    displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
+    displayer.show(ha)
+    return 0
+
+
+def outgoing(orig, ui, repo, dest=None, *args, **opts):
+    """show changesets not found in the Subversion repository
+    """
+    svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
+    if not (cmdutil.issvnurl(svnurl) or opts.get('svn', False)):
+        return orig(ui, repo, dest, *args, **opts)
+
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+                                  repo.parents()[0].node())
+    if not (o_r and len(o_r)):
+        ui.status('no changes found\n')
+        return 0
+    displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
+    for node in reversed(o_r):
+        displayer.show(repo[node])
+
+
+
+
+def diff(orig, ui, repo, *args, **opts):
+    """show a diff of the most recent revision against its parent from svn
+    """
+    if not opts.get('svn', False) or opts.get('change', None):
+        return orig(ui, repo, *args, **opts)
+    svn_commit_hashes = {}
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    if not opts.get('rev', None):
+        parent = repo.parents()[0]
+        o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+                                      parent.node())
+        if o_r:
+            parent = repo[o_r[-1]].parents()[0]
+        opts['rev'] = ['%s:.' % node.hex(parent.node()), ]
+    node1, node2 = hgcmdutil.revpair(repo, opts['rev'])
+    baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', ))
+    newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', ))
+    it = patch.diff(repo, node1, node2,
+                    opts=patch.diffopts(ui, opts={'git': True,
+                                                  'show_function': False,
+                                                  'ignore_all_space': False,
+                                                  'ignore_space_change': False,
+                                                  'ignore_blank_lines': False,
+                                                  'unified': True,
+                                                  'text': False,
+                                                  }))
+    ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
+
+
+
+
+def push(orig, ui, repo, dest=None, *args, **opts):
+    """push revisions starting at a specified head back to Subversion.
+    """
+    svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
+    if not cmdutil.issvnurl(svnurl):
+        orig(ui, repo, dest=dest, *args, **opts)
+    old_encoding = util.swap_out_encoding()
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+    svnurl = util.normalize_url(svnurl)
+    if svnurl != hge.url:
+        raise hgutil.Abort('wrong subversion url!')
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    user = opts.get('username', hgutil.getuser())
+    passwd = opts.get('password', '')
+
+    # Strategy:
+    # 1. Find all outgoing commits from this head
+    if len(repo.parents()) != 1:
+        ui.status('Cowardly refusing to push branch merge')
+        return 1
+    workingrev = repo.parents()[0]
+    outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
+    if not (outgoing and len(outgoing)):
+        ui.status('No revisions to push.')
+        return 0
+    while outgoing:
+        oldest = outgoing.pop(-1)
+        old_ctx = repo[oldest]
+        if len(old_ctx.parents()) != 1:
+            ui.status('Found a branch merge, this needs discussion and '
+                      'implementation.')
+            return 1
+        base_n = old_ctx.parents()[0].node()
+        old_children = repo[base_n].children()
+        svnbranch = repo[base_n].branch()
+        oldtip = base_n
+        samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+                              and c.node() in svn_commit_hashes]
+        while samebranchchildren:
+            oldtip = samebranchchildren[0].node()
+            samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+                                  and c.node() in svn_commit_hashes]
+        # 2. Commit oldest revision that needs to be pushed
+        base_revision = svn_commit_hashes[base_n][0]
+        try:
+            cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svnurl,
+                                    base_revision, user, passwd)
+        except cmdutil.NoFilesException:
+            ui.warn("Could not push revision %s because it had no changes in svn.\n" %
+                     old_ctx)
+            return 1
+        # 3. Fetch revisions from svn
+        r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
+                 username=user, password=passwd)
+        assert not r or r == 0
+        # 4. Find the new head of the target branch
+        repo = hg.repository(ui, hge.path)
+        oldtipctx = repo[oldtip]
+        replacement = [c for c in oldtipctx.children() if c not in old_children
+                       and c.branch() == oldtipctx.branch()]
+        assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
+        replacement = replacement[0]
+        # 5. Rebase all children of the currently-pushing rev to the new branch
+        heads = repo.heads(old_ctx.node())
+        for needs_transplant in heads:
+            def extrafn(ctx, extra):
+                if ctx.node() == oldest:
+                    return
+                extra['branch'] = ctx.branch()
+            utility_commands.rebase(ui, repo, extrafn=extrafn,
+                                    sourcerev=needs_transplant, **opts)
+            repo = hg.repository(ui, hge.path)
+            for child in repo[replacement.node()].children():
+                rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+                if rebasesrc in outgoing:
+                    while rebasesrc in outgoing:
+                        rebsrcindex = outgoing.index(rebasesrc)
+                        outgoing = (outgoing[0:rebsrcindex] +
+                                    [child.node(), ] + outgoing[rebsrcindex+1:])
+                        children = [c for c in child.children() if c.branch() == child.branch()]
+                        if children:
+                            child = children[0]
+                        rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+        hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui)
+        svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
+    util.swap_out_encoding(old_encoding)
+    return 0
+
+
+
+def clone(orig, ui, source, dest=None, *args, **opts):
+    '''clone Subversion repository to a local Mercurial repository.
+
+    If no destination directory name is specified, it defaults to the
+    basename of the source plus "-hg".
+
+    You can specify multiple paths for the location of tags using comma
+    separated values.
+    '''
+    svnurl = ui.expandpath(source)
+    if not cmdutil.issvnurl(svnurl):
+        orig(ui, source=source, dest=dest, *args, **opts)
+
+    if not dest:
+        dest = hg.defaultdest(source) + '-hg'
+        ui.status("Assuming destination %s\n" % dest)
+
+    if os.path.exists(dest):
+        raise hgutil.Abort("destination '%s' already exists" % dest)
+    url = util.normalize_url(svnurl)
+    res = -1
+    try:
+        try:
+            res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
+                       source=url, create_new_dest=dest, **opts)
+        except core.SubversionException, e:
+            if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
+                raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+                         'Please try running svn ls on that url first.')
+            raise
+    finally:
+        if os.path.exists(dest):
+            repo = hg.repository(ui, dest)
+            fp = repo.opener("hgrc", "w", text=True)
+            fp.write("[paths]\n")
+            # percent needs to be escaped for ConfigParser
+            fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
+            fp.close()
+            if (res is None or res == 0) and not opts.get('noupdate', False):
+                commands.update(ui, repo, repo['tip'].node())
+
+    return res
+
+
+def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
+         *args, **opts):
+    """pull new revisions from Subversion
+    """
+    url = ((repo and repo.ui) or ui).expandpath(source)
+    if not (cmdutil.issvnurl(url) or svn or create_new_dest):
+        orig(ui, repo, source=source, *args, **opts)
+    svn_url = url
+    svn_url = util.normalize_url(svn_url)
+    old_encoding = util.swap_out_encoding()
+    # TODO implement skipto support
+    skipto_rev = 0
+    have_replay = not svn_stupid
+    if have_replay and not callable(
+        delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
+        ui.status('You are using old Subversion SWIG bindings. Replay will not'
+                  ' work until you upgrade to 1.5.0 or newer. Falling back to'
+                  ' a slower method that may be buggier. Please upgrade, or'
+                  ' contribute a patch to use the ctypes bindings instead'
+                  ' of SWIG.\n')
+        have_replay = False
+    initializing_repo = False
+    user = opts.get('username', hgutil.getuser())
+    passwd = opts.get('password', '')
+    svn = svnwrap.SubversionRepo(svn_url, user, passwd)
+    author_host = "@%s" % svn.uuid
+    # TODO these should be configurable again, but I'm torn on how.
+    # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
+    tag_locations = ['tags', ]
+    authors = opts.pop('svn_authors', None)
+    filemap = opts.pop('svn_filemap', None)
+    if repo:
+        hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo,
+                                                     subdir=svn.subdir,
+                                                     author_host=author_host,
+                                                     tag_locations=tag_locations,
+                                                     authors=authors,
+                                                     filemap=filemap)
+    else:
+        hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui,
+                                                     path=create_new_dest,
+                                                     subdir=svn.subdir,
+                                                     author_host=author_host,
+                                                     tag_locations=tag_locations,
+                                                     authors=authors,
+                                                     filemap=filemap)
+    if os.path.exists(hg_editor.uuid_file):
+        uuid = open(hg_editor.uuid_file).read()
+        assert uuid == svn.uuid
+        start = hg_editor.last_known_revision()
+    else:
+        open(hg_editor.uuid_file, 'w').write(svn.uuid)
+        open(hg_editor.svn_url_file, 'w').write(svn_url)
+        initializing_repo = True
+        start = skipto_rev
+
+    if initializing_repo and start > 0:
+        raise hgutil.Abort('Revision skipping at repository initialization '
+                           'remains unimplemented.')
+
+    # start converting revisions
+    for r in svn.revisions(start=start):
+        valid = True
+        hg_editor.update_branch_tag_map_for_rev(r)
+        for p in r.paths:
+            if hg_editor._is_path_valid(p):
+                valid = True
+                break
+        if valid:
+            # got a 502? Try more than once!
+            tries = 0
+            converted = False
+            while not converted:
+                try:
+                    util.describe_revision(ui, r)
+                    if have_replay:
+                        try:
+                            cmdutil.replay_convert_rev(hg_editor, svn, r)
+                        except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
+                            ui.status('%s\n' % e.message)
+                            stupidmod.print_your_svn_is_old_message(ui)
+                            have_replay = False
+                            stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+                    else:
+                        stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+                    converted = True
+                except core.SubversionException, e: #pragma: no cover
+                    if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
+                        and '502' in str(e)
+                        and tries < 3):
+                        tries += 1
+                        ui.status('Got a 502, retrying (%s)\n' % tries)
+                    else:
+                        raise hgutil.Abort(*e.args)
+    util.swap_out_encoding(old_encoding)

changeset:   258:4ab27ddbae51
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 10 22:41:33 2009 -0500
summary:     Updated readme a little bit for the new UI.

diff --git a/README b/README
--- a/README
+++ b/README
@@ -42,18 +42,20 @@
 -----------
 Get a new clone of an svn server::
 
- $ hg svnclone <svn URI> [destination]
+ $ hg clone <svn URI> [destination]
 
 Real example::
 
- $ hg svnclone http://python-nose.googlecode.com/svn nose-hg
+ $ hg clone svn+http://python-nose.googlecode.com/svn nose-hg
 
 Note, you should pull from the root subversion directory, not specific
-folders (such as trunk).
+folders (such as trunk). Also, you only need to modify http:// urls as shown.
+This is a side effect of Mercurial and Subversion both claiming the http
+protocol, so svn+http is used to work around that.
 
 Pull new revisions into an already-converted repo::
 
- $ hg svn pull
+ $ hg pull
 
 For more information, see ``hg help svn`` while in a converted repo.
 

changeset:   259:52cc0982dfcd
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 10 22:42:50 2009 -0500
summary:     Fix the verify script which broke because of the change in parent.

diff --git a/tools/common.sh b/tools/common.sh
--- a/tools/common.sh
+++ b/tools/common.sh
@@ -1,7 +1,7 @@
 function verify_current_revision()
 {
     /bin/rm -rf *
-    exportcmd="svn export `hg svn info 2> /dev/null | grep '^URL: ' | sed 's/URL: //'` -`hg svn parent | sed 's/.*: //;s/ .*//'` . --force"
+    exportcmd="svn export `hg svn info 2> /dev/null | grep '^URL: ' | sed 's/URL: //'` -r`hg svn info | grep ^Revision | sed 's/.*: //;s/ .*//'` . --force"
     `echo $exportcmd` > /dev/null
     x=$?
     if [[ "$x" != "0" ]] ; then

changeset:   260:87dc4d0dd048
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 10 22:45:50 2009 -0500
summary:     Forgot some return statements.

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -93,7 +93,7 @@
     """
     svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
     if not cmdutil.issvnurl(svnurl):
-        orig(ui, repo, dest=dest, *args, **opts)
+        return orig(ui, repo, dest=dest, *args, **opts)
     old_encoding = util.swap_out_encoding()
     hge = hg_delta_editor.HgChangeReceiver(repo=repo)
     svnurl = util.normalize_url(svnurl)
@@ -190,7 +190,7 @@
     '''
     svnurl = ui.expandpath(source)
     if not cmdutil.issvnurl(svnurl):
-        orig(ui, source=source, dest=dest, *args, **opts)
+        return orig(ui, source=source, dest=dest, *args, **opts)
 
     if not dest:
         dest = hg.defaultdest(source) + '-hg'
@@ -229,7 +229,7 @@
     """
     url = ((repo and repo.ui) or ui).expandpath(source)
     if not (cmdutil.issvnurl(url) or svn or create_new_dest):
-        orig(ui, repo, source=source, *args, **opts)
+        return orig(ui, repo, source=source, *args, **opts)
     svn_url = url
     svn_url = util.normalize_url(svn_url)
     old_encoding = util.swap_out_encoding()

changeset:   261:141513b5173b
user:        Augie Fackler <durin42@gmail.com>
date:        Sun Apr 12 12:18:04 2009 -0500
summary:     Stop breaking hg pull <path>.

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -202,8 +202,9 @@
     res = -1
     try:
         try:
-            res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
-                       source=url, create_new_dest=dest, **opts)
+            res = pull(None, ui, None, source=url, svn=None,
+                       svn_stupid=opts.pop('svn_stupid', False),
+                       create_new_dest=dest, **opts)
         except core.SubversionException, e:
             if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
                 raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
@@ -223,10 +224,14 @@
     return res
 
 
-def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
-         *args, **opts):
+def pull(orig, ui, repo, source="default", *args, **opts):
     """pull new revisions from Subversion
+
+    Also takes svn, svn_stupid, and create_new_dest kwargs.
     """
+    svn = opts.pop('svn', None)
+    svn_stupid = opts.pop('svn_stupid', False)
+    create_new_dest = opts.pop('create_new_dest', False)
     url = ((repo and repo.ui) or ui).expandpath(source)
     if not (cmdutil.issvnurl(url) or svn or create_new_dest):
         return orig(ui, repo, source=source, *args, **opts)

changeset:   262:3b3627611468
user:        Augie Fackler <durin42@gmail.com>
date:        Sun Apr 12 12:25:20 2009 -0500
summary:     Fix pushing after 141513b.

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -141,7 +141,8 @@
                      old_ctx)
             return 1
         # 3. Fetch revisions from svn
-        r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
+        # TODO this probably should pass in the source explicitly
+        r = pull(None, ui, repo, svn=True, stupid=opts.get('svn_stupid', False),
                  username=user, password=passwd)
         assert not r or r == 0
         # 4. Find the new head of the target branch

changeset:   263:d978192f0d63
user:        Augie Fackler <durin42@gmail.com>
date:        Mon Apr 13 21:19:13 2009 -0500
summary:     Clean up TODOs that are no longer relevant.

diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -216,8 +216,6 @@
             while paths_need_discovery:
                 p = paths_need_discovery.pop(0)
                 path_could_be_file = True
-                # TODO(augie) Figure out if you can use break here in a for loop, quick
-                # testing of that failed earlier.
                 ind = 0
                 while ind < len(paths_need_discovery) and not paths_need_discovery:
                     if op.startswith(p):
@@ -633,7 +631,6 @@
                        revlog.nullid)
             if parents[0] in closed_revs and branch in self.branches_to_delete:
                 continue
-            # TODO this needs to be fixed with the new revmap
             extra = util.build_extra(rev.revnum, branch,
                                      open(self.uuid_file).read(),
                                      self.subdir)
diff --git a/stupid.py b/stupid.py
--- a/stupid.py
+++ b/stupid.py
@@ -513,9 +513,10 @@
         for f in excluded:
             files_touched.remove(f)
         if parentctx.node() != node.nullid or files_touched:
-            # TODO(augie) remove this debug code? Or maybe it's sane to have it.
             for f in files_touched:
                 if f:
+                    # this is a case that really shouldn't ever happen, it means something
+                    # is very wrong
                     assert f[0] != '/'
             current_ctx = context.memctx(hg_editor.repo,
                                          [parentctx.node(), revlog.nullid],
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -112,7 +112,6 @@
     """
     def __init__(self, revnum, author, message, date, paths, strip_path=''):
         self.revnum, self.author, self.message = revnum, author, message
-        # TODO parse this into a datetime
         self.date = date
         self.paths = {}
         if paths:
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -80,7 +80,6 @@
         return outgoing_rev_hashes
 
 def build_extra(revnum, branch, uuid, subdir):
-    # TODO this needs to be fixed with the new revmap
     extra = {}
     branchpath = 'trunk'
     if branch:
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -142,7 +142,6 @@
     if parent_rev == target_rev:
         ui.status('Already up to date!\n')
         return 0
-    # TODO this is really hacky, there must be a more direct way
     return hgrebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
                          base=node.hex(sourcerev),
                          extrafn=extrafn)
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -255,8 +255,6 @@
     passwd = opts.get('password', '')
     svn = svnwrap.SubversionRepo(svn_url, user, passwd)
     author_host = "@%s" % svn.uuid
-    # TODO these should be configurable again, but I'm torn on how.
-    # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
     tag_locations = ['tags', ]
     authors = opts.pop('svn_authors', None)
     filemap = opts.pop('svn_filemap', None)

changeset:   264:112d57bb736e
user:        Augie Fackler <durin42@gmail.com>
date:        Mon Apr 13 21:51:12 2009 -0500
summary:     rebase: moved to wrappers, now a wrapper around rebase triggered with --svn.

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -69,6 +69,14 @@
                       'remap file to exclude paths or include only certain paths'),
                      ])
 
+    try:
+        rebase = extensions.find('rebase')
+        if rebase:
+            entry = extensions.wrapcommand(rebase.cmdtable, 'rebase', wrappers.rebase)
+            entry[1].append(('', 'svn', None, 'automatic svn rebase', ))
+    except:
+        pass
+
 
 def svn(ui, repo, subcommand, *args, **opts):
     '''see detailed help for list of subcommands'''
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -1,8 +1,6 @@
 import os
 
-from mercurial import node
 from mercurial import util as hgutil
-from hgext import rebase as hgrebase
 
 import svnwrap
 import cmdutil
@@ -100,53 +98,6 @@
               })
 
 
-def rebase(ui, repo, extrafn=None, sourcerev=None, **opts):
-    """rebase current unpushed revisions onto the Subversion head
-
-    This moves a line of development from making its own head to the top of
-    Subversion development, linearizing the changes. In order to make sure you
-    rebase on top of the current top of Subversion work, you should probably run
-    'hg svn pull' before running this.
-    """
-    if extrafn is None:
-        def extrafn2(ctx, extra):
-            """defined here so we can add things easily.
-            """
-            extra['branch'] = ctx.branch()
-        extrafn = extrafn2
-    if sourcerev is None:
-        sourcerev = repo.parents()[0].node()
-    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
-    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
-                                 hge.revmap.iterkeys()))
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, sourcerev=sourcerev)
-    if not o_r:
-        ui.status('Nothing to rebase!\n')
-        return 0
-    if len(repo[sourcerev].children()):
-        ui.status('Refusing to rebase non-head commit like a coward\n')
-        return 0
-    parent_rev = repo[o_r[-1]].parents()[0]
-    target_rev = parent_rev
-    p_n = parent_rev.node()
-    exhausted_choices = False
-    while target_rev.children() and not exhausted_choices:
-        for c in target_rev.children():
-            exhausted_choices = True
-            n = c.node()
-            if (n in svn_commit_hashes and
-                svn_commit_hashes[n][1] == svn_commit_hashes[p_n][1]):
-                target_rev = c
-                exhausted_choices = False
-                break
-    if parent_rev == target_rev:
-        ui.status('Already up to date!\n')
-        return 0
-    return hgrebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
-                         base=node.hex(sourcerev),
-                         extrafn=extrafn)
-
-
 def listauthors(ui, args, authors=None, **opts):
     """list all authors in a Subversion repository
     """
@@ -180,5 +131,4 @@
     'info': info,
     'listauthors': listauthors,
     'version': version,
-    'rebase': rebase,
 }
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -1,5 +1,7 @@
 import os
 
+from hgext import rebase as hgrebase
+
 from mercurial import cmdutil as hgcmdutil
 from mercurial import commands
 from mercurial import patch
@@ -15,7 +17,6 @@
 import stupid as stupidmod
 import svnwrap
 import util
-import utility_commands
 
 def parent(orig, ui, repo, *args, **opts):
     """show Mercurial & Subversion parents of the working dir or revision
@@ -53,8 +54,6 @@
         displayer.show(repo[node])
 
 
-
-
 def diff(orig, ui, repo, *args, **opts):
     """show a diff of the most recent revision against its parent from svn
     """
@@ -86,8 +85,6 @@
     ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
 
 
-
-
 def push(orig, ui, repo, dest=None, *args, **opts):
     """push revisions starting at a specified head back to Subversion.
     """
@@ -115,6 +112,7 @@
         ui.status('No revisions to push.')
         return 0
     while outgoing:
+        print [node.hex(x) for x in outgoing]
         oldest = outgoing.pop(-1)
         old_ctx = repo[oldest]
         if len(old_ctx.parents()) != 1:
@@ -159,12 +157,14 @@
                 if ctx.node() == oldest:
                     return
                 extra['branch'] = ctx.branch()
-            utility_commands.rebase(ui, repo, extrafn=extrafn,
-                                    sourcerev=needs_transplant, **opts)
+            rebase(hgrebase.rebase, ui, repo, svn=True, svnextrafn=extrafn,
+                   svnsourcerev=needs_transplant, **opts)
             repo = hg.repository(ui, hge.path)
             for child in repo[replacement.node()].children():
                 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+                print node.hex(rebasesrc)
                 if rebasesrc in outgoing:
+                    print 'swap outgoin'
                     while rebasesrc in outgoing:
                         rebsrcindex = outgoing.index(rebasesrc)
                         outgoing = (outgoing[0:rebsrcindex] +
@@ -179,7 +179,6 @@
     return 0
 
 
-
 def clone(orig, ui, source, dest=None, *args, **opts):
     '''clone Subversion repository to a local Mercurial repository.
 
@@ -322,3 +321,52 @@
                     else:
                         raise hgutil.Abort(*e.args)
     util.swap_out_encoding(old_encoding)
+
+
+def rebase(orig, ui, repo, **opts):
+    """rebase current unpushed revisions onto the Subversion head
+
+    This moves a line of development from making its own head to the top of
+    Subversion development, linearizing the changes. In order to make sure you
+    rebase on top of the current top of Subversion work, you should probably run
+    'hg svn pull' before running this.
+
+    Also looks for svnextrafn and svnsourcerev in **opts.
+    """
+    if not opts.get('svn', False):
+        return orig(ui, repo, **opts)
+    def extrafn2(ctx, extra):
+        """defined here so we can add things easily.
+        """
+        extra['branch'] = ctx.branch()
+    extrafn = opts.get('svnextrafn', extrafn2)
+    sourcerev = opts.get('svnsourcerev', repo.parents()[0].node())
+    hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, sourcerev=sourcerev)
+    if not o_r:
+        ui.status('Nothing to rebase!\n')
+        return 0
+    if len(repo[sourcerev].children()):
+        ui.status('Refusing to rebase non-head commit like a coward\n')
+        return 0
+    parent_rev = repo[o_r[-1]].parents()[0]
+    target_rev = parent_rev
+    p_n = parent_rev.node()
+    exhausted_choices = False
+    while target_rev.children() and not exhausted_choices:
+        for c in target_rev.children():
+            exhausted_choices = True
+            n = c.node()
+            if (n in svn_commit_hashes and
+                svn_commit_hashes[n][1] == svn_commit_hashes[p_n][1]):
+                target_rev = c
+                exhausted_choices = False
+                break
+    if parent_rev == target_rev:
+        ui.status('Already up to date!\n')
+        return 0
+    return orig(ui, repo, dest=node.hex(target_rev.node()),
+                base=node.hex(sourcerev),
+                extrafn=extrafn)

changeset:   265:9f0738587f94
user:        Augie Fackler <durin42@gmail.com>
date:        Mon Apr 13 21:12:33 2009 -0500
summary:     Re-re-refix username support, add a comment so maybe I remember this time.

diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -19,8 +19,7 @@
     svn_url = util.normalize_url(svn_url)
 
     initializing_repo = False
-    user = opts.get('username', hgutil.getuser())
-    passwd = opts.get('password', '')
+    user, passwd = util.getuserpass(opts)
     svn = svnwrap.SubversionRepo(svn_url, user, passwd)
     author_host = "@%s" % svn.uuid
     tag_locations = tag_locations.split(',')
@@ -68,8 +67,7 @@
         raise hgutil.Abort('You must pass the svn URI used to create this repo.')
     uuid = None
     url = args[0].rstrip('/')
-    user = opts.get('username', hgutil.getuser())
-    passwd = opts.get('password', '')
+    user, passwd = util.getuserpass(opts)
     svn = svnwrap.SubversionRepo(url, user, passwd)
     subdir = svn.subdir
     svnmetadir = os.path.join(repo.path, 'svn')
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -6,6 +6,14 @@
 from mercurial import util as hgutil
 
 
+def getuserpass(opts):
+    # DO NOT default the user to hg's getuser(). If you provide
+    # *any* default username to Subversion, it won't use any remembered
+    # username for the desired realm, breaking OS X Keychain support,
+    # GNOME keyring support, and all similar tools.
+    return opts.get('username', None), opts.get('password', '')
+
+
 def version(ui):
     """Guess the version of hgsubversion.
     """
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -36,8 +36,7 @@
     url = hge.url
     if url[-1] == '/':
         url = url[:-1]
-    user = opts.get('username', hgutil.getuser())
-    passwd = opts.get('passwd', '')
+    user, passwd = util.getuserpass(opts)
     svn = svnwrap.SubversionRepo(url, user, passwd)
     dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd']
     for dir in dirs:
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -98,9 +98,7 @@
         raise hgutil.Abort('wrong subversion url!')
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    user = opts.get('username', hgutil.getuser())
-    passwd = opts.get('password', '')
-
+    user, passwd = util.getuserpass(opts)
     # Strategy:
     # 1. Find all outgoing commits from this head
     if len(repo.parents()) != 1:
@@ -250,8 +248,7 @@
                   ' of SWIG.\n')
         have_replay = False
     initializing_repo = False
-    user = opts.get('username', hgutil.getuser())
-    passwd = opts.get('password', '')
+    user, passwd = util.getuserpass(opts)
     svn = svnwrap.SubversionRepo(svn_url, user, passwd)
     author_host = "@%s" % svn.uuid
     tag_locations = ['tags', ]

changeset:   266:a5f20358f737
user:        Luke Opperman <luke@loppear.com>
date:        Tue Apr 14 09:32:09 2009 -0500
summary:     Remove stray prints

diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -351,14 +351,10 @@
             if action == 'modify':
                 baton = editor.open_file(path, parent, base_revision, pool)
             elif action == 'add':
-                try:
-                    frompath, fromrev = copies.get(path, (None, -1))
-                    if frompath:
-                        frompath = self.svn_url + '/' + frompath
-                    baton = editor.add_file(path, parent, frompath, fromrev, pool)
-                except (core.SubversionException, TypeError), e: #pragma: no cover
-                    print e.message
-                    raise
+                frompath, fromrev = copies.get(path, (None, -1))
+                if frompath:
+                    frompath = self.svn_url + '/' + frompath
+                baton = editor.add_file(path, parent, frompath, fromrev, pool)
             elif action == 'delete':
                 baton = editor.delete_entry(path, base_revision, parent, pool)
                 compute_delta = False
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -110,7 +110,6 @@
         ui.status('No revisions to push.')
         return 0
     while outgoing:
-        print [node.hex(x) for x in outgoing]
         oldest = outgoing.pop(-1)
         old_ctx = repo[oldest]
         if len(old_ctx.parents()) != 1:

changeset:   267:f000b2392fc2
user:        Luke Opperman <luke@loppear.com>
date:        Tue Apr 14 09:32:40 2009 -0500
summary:     Push status messages, remove svn flag from opts before passing on

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -88,6 +88,7 @@
 def push(orig, ui, repo, dest=None, *args, **opts):
     """push revisions starting at a specified head back to Subversion.
     """
+    opts.pop('svn', None) # unused in this case
     svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
     if not cmdutil.issvnurl(svnurl):
         return orig(ui, repo, dest=dest, *args, **opts)
@@ -105,9 +106,10 @@
         ui.status('Cowardly refusing to push branch merge')
         return 1
     workingrev = repo.parents()[0]
+    ui.status('searching for changes\n')
     outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
     if not (outgoing and len(outgoing)):
-        ui.status('No revisions to push.')
+        ui.status('no changes found\n')
         return 0
     while outgoing:
         oldest = outgoing.pop(-1)

changeset:   269:14914dbd8a4a
user:        Augie Fackler <durin42@gmail.com>
date:        Tue Apr 21 15:19:50 2009 -0500
summary:     More stray prints.

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -161,9 +161,7 @@
             repo = hg.repository(ui, hge.path)
             for child in repo[replacement.node()].children():
                 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
-                print node.hex(rebasesrc)
                 if rebasesrc in outgoing:
-                    print 'swap outgoin'
                     while rebasesrc in outgoing:
                         rebsrcindex = outgoing.index(rebasesrc)
                         outgoing = (outgoing[0:rebsrcindex] +

changeset:   271:5278817fe8a1
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Tue Apr 21 18:31:17 2009 -0400
summary:     Add newlines to ui.status calls where needed

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -103,7 +103,7 @@
     # Strategy:
     # 1. Find all outgoing commits from this head
     if len(repo.parents()) != 1:
-        ui.status('Cowardly refusing to push branch merge')
+        ui.status('Cowardly refusing to push branch merge\n')
         return 1
     workingrev = repo.parents()[0]
     ui.status('searching for changes\n')
@@ -116,7 +116,7 @@
         old_ctx = repo[oldest]
         if len(old_ctx.parents()) != 1:
             ui.status('Found a branch merge, this needs discussion and '
-                      'implementation.')
+                      'implementation.\n')
             return 1
         base_n = old_ctx.parents()[0].node()
         old_children = repo[base_n].children()

changeset:   272:25d843281127
user:        Patrick Mezard <pmezard@gmail.com>
date:        Wed Apr 22 23:24:58 2009 +0200
summary:     Use svn 1.6 platform specific auth providers if available

diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -62,7 +62,7 @@
     # Give the client context baton a suite of authentication
     # providers.h
     platform_specific = ['svn_auth_get_gnome_keyring_simple_provider',
-                       'svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider',
+                         'svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider',
                          'svn_auth_get_keychain_simple_provider',
                          'svn_auth_get_keychain_ssl_client_cert_pw_provider',
                          'svn_auth_get_kwallet_simple_provider',
@@ -73,13 +73,23 @@
                          ]
 
     providers = []
-
-    for p in platform_specific:
-        if hasattr(core, p):
-            try:
-                providers.append(getattr(core, p)())
-            except RuntimeError:
-                pass
+    # Platform-dependant authentication methods
+    getprovider = getattr(core, 'svn_auth_get_platform_specific_provider',
+                          None)
+    if getprovider:
+        # Available in svn >= 1.6
+        for name in ('gnome_keyring', 'keychain', 'kwallet', 'windows'):
+            for type in ('simple', 'ssl_client_cert_pw', 'ssl_server_trust'):
+                p = getprovider(name, type, pool)
+                if p:
+                    providers.append(p)
+    else:
+        for p in platform_specific:
+            if hasattr(core, p):
+                try:
+                    providers.append(getattr(core, p)())
+                except RuntimeError:
+                    pass
 
     providers += [
         client.get_simple_provider(),

changeset:   273:e0f9fa0acc03
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Thu Apr 23 01:33:38 2009 -0400
summary:     Fix the nourl check to use the command name, instead of the function

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -93,7 +93,7 @@
     path = os.path.dirname(repo.path)
     try:
         commandfunc = svncommands.table[subcommand]
-        if commandfunc not in svncommands.nourl:
+        if subcommand not in svncommands.nourl:
             opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
         return commandfunc(ui, args=args, hg_repo_path=path, repo=repo, **opts)
     except core.SubversionException, e:

changeset:   274:a119ab6135f3
user:        Daniel Tang <dytang@cs.purdue.edu>
date:        Thu Apr 23 14:51:13 2009 -0400
summary:     Add help to the list of nourl commands

diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -222,7 +222,7 @@
     return 1
 
 
-nourl = ['rebuildmeta'] + utility_commands.nourl
+nourl = ['rebuildmeta', 'help'] + utility_commands.nourl
 table = {
     'update': update,
     'help': help,

changeset:   275:68aea59116b6
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 24 20:25:14 2009 -0500
summary:     util: kill unused function

diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -29,15 +29,6 @@
     return svnurl.rstrip('/')
 
 
-def wipe_all_files(hg_wc_path):
-    files = [f for f in os.listdir(hg_wc_path) if f != '.hg']
-    for f in files:
-        f = os.path.join(hg_wc_path, f)
-        if os.path.isdir(f):
-            shutil.rmtree(f)
-        else:
-            os.remove(f)
-
 REVMAP_FILE_VERSION = 1
 def parse_revmap(revmap_filename):
     revmap = {}

changeset:   276:b45bae16be32
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 24 20:26:00 2009 -0500
summary:     clone: Fix url-escaping for new config parser

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -213,7 +213,7 @@
             fp = repo.opener("hgrc", "w", text=True)
             fp.write("[paths]\n")
             # percent needs to be escaped for ConfigParser
-            fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
+            fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl})
             fp.close()
             if (res is None or res == 0) and not opts.get('noupdate', False):
                 commands.update(ui, repo, repo['tip'].node())

changeset:   278:60acc38eac96
user:        Martijn Pieters <mj@zopatista.com>
date:        Fri Apr 24 20:36:38 2009 -0500
summary:     clone: prefer tip of default to overall tip when updating

diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -216,7 +216,13 @@
             fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl})
             fp.close()
             if (res is None or res == 0) and not opts.get('noupdate', False):
-                commands.update(ui, repo, repo['tip'].node())
+                for test in ('default', 'tip'):
+                    try:
+                        uprev = repo.lookup(test)
+                        break
+                    except:
+                        continue
+                commands.update(ui, repo, uprev)
 
     return res
 

changeset:   279:376ba9399ce6
user:        Max Bowsher <maxb@f2s.com>
date:        Fri Apr 24 21:04:25 2009 -0500
summary:     hg_delta_editor: Fix a bug in marking branches as deleted.

diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -521,7 +521,7 @@
                 # check for case 5
                 for known in self.branches:
                     if self._svnpath(known).startswith(p):
-                        self.branches_to_delete.add(br) # case 5
+                        self.branches_to_delete.add(known) # case 5
             added_branches.update(self.__determine_parent_branch(p, paths[p].copyfrom_path,
                                                                  paths[p].copyfrom_rev, revision.revnum))
         for t in tags_to_delete:

changeset:   280:4a327bfc69c6
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 24 21:06:54 2009 -0500
summary:     README: Now require 1.3.

diff --git a/README b/README
--- a/README
+++ b/README
@@ -14,7 +14,8 @@
 Installation
 ------------
 You need to have Subversion installed with the SWIG Python bindings
-from Subversion 1.5 or later. You need Mercurial 1.1.1 or later.
+from Subversion 1.5 or later. You need Mercurial 1.3 (currently in development)
+or later.
 
 .. _mercurial: http://selenic.com/repo/hg
 .. _mercurial-stable: http://selenic.com/repo/hg-stable

changeset:   281:8ff0b3261b7f
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 24 21:07:59 2009 -0500
summary:     util: drop 1.2 support in the encoding swap.

diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -114,12 +114,9 @@
 
 
 def swap_out_encoding(new_encoding="UTF-8"):
-    """ Utility for mercurial incompatibility changes, can be removed after 1.3"""
-    try:
-        from mercurial import encoding
-        old = encoding.encoding
-        encoding.encoding = new_encoding
-    except ImportError:
-        old = hgutil._encoding
-        hgutil._encoding = new_encoding
+    """ Utility for mercurial incompatibility changes, can be removed after 1.3
+    """
+    from mercurial import encoding
+    old = encoding.encoding
+    encoding.encoding = new_encoding
     return old

changeset:   282:77892f67b1cd
tag:         tip
user:        Augie Fackler <durin42@gmail.com>
date:        Fri Apr 24 22:33:54 2009 -0500
summary:     tools: bash script to convert tags as a stopgap until the tags refactor

diff --git a/tools/converttags.sh b/tools/converttags.sh
new file mode 100644
--- /dev/null
+++ b/tools/converttags.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# This shell script exists to convert hgsubversion tags to real hg tags.
+# This will go away once hgsubversion's tags handling uses .hgtags directly.
+hg tags | sed -E 's/([a-zA-Z0-9./_-]*) [^:]*:([a-f0-9]*)/\2 \1/' | grep -v ' tip$' > .hgtags
+cat .hgtags | sed "$(
+for x in `cat .hgtags| cut -f 1 -d ' '` ;do
+    echo -n "s/$x/" ; hg log --template '{node}' -r $x ; echo -n '/g; '
+done)" > .hgtags.new
+mv .hgtags.new .hgtags