changeset 1036:e775ffbcb359

push: also suggest user rebase if we get SVN_ERR_FS_ALREADY_EXISTS Subversion differentiates adding an already-added file from transaction being out of date and edit conflicts. Fixes #400.
author Augie Fackler <raf@durin42.com>
date Tue, 16 Jul 2013 10:09:55 -0400
parents 2c64453f98a7
children 2316f2623dd4
files hgsubversion/pushmod.py hgsubversion/svnwrap/subvertpy_wrapper.py hgsubversion/svnwrap/svn_swig_wrapper.py tests/test_push_command.py
diffstat 4 files changed, 56 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/hgsubversion/pushmod.py
+++ b/hgsubversion/pushmod.py
@@ -201,7 +201,8 @@ def commit(ui, repo, rev_ctx, meta, base
                    props, newcopies)
     except svnwrap.SubversionException, e:
         if len(e.args) > 0 and e.args[1] in (svnwrap.ERR_FS_TXN_OUT_OF_DATE,
-                                             svnwrap.ERR_FS_CONFLICT):
+                                             svnwrap.ERR_FS_CONFLICT,
+                                             svnwrap.ERR_FS_ALREADY_EXISTS):
             raise hgutil.Abort('Outgoing changesets parent is not at '
                                'subversion HEAD\n'
                                '(pull again and rebase on a newer revision)')
--- a/hgsubversion/svnwrap/subvertpy_wrapper.py
+++ b/hgsubversion/svnwrap/subvertpy_wrapper.py
@@ -52,6 +52,7 @@ def version():
     return (svnvers, 'Subvertpy ' + _versionstr(subvertpy.__version__))
 
 # exported values
+ERR_FS_ALREADY_EXISTS = subvertpy.ERR_FS_ALREADY_EXISTS
 ERR_FS_CONFLICT = subvertpy.ERR_FS_CONFLICT
 ERR_FS_NOT_FOUND = subvertpy.ERR_FS_NOT_FOUND
 ERR_FS_TXN_OUT_OF_DATE = subvertpy.ERR_FS_TXN_OUT_OF_DATE
@@ -59,11 +60,11 @@ ERR_INCOMPLETE_DATA = subvertpy.ERR_INCO
 ERR_RA_DAV_PATH_NOT_FOUND = subvertpy.ERR_RA_DAV_PATH_NOT_FOUND
 ERR_RA_DAV_REQUEST_FAILED = subvertpy.ERR_RA_DAV_REQUEST_FAILED
 ERR_REPOS_HOOK_FAILURE = subvertpy.ERR_REPOS_HOOK_FAILURE
-SSL_UNKNOWNCA = subvertpy.SSL_UNKNOWNCA
 SSL_CNMISMATCH = subvertpy.SSL_CNMISMATCH
-SSL_NOTYETVALID = subvertpy.SSL_NOTYETVALID
 SSL_EXPIRED = subvertpy.SSL_EXPIRED
+SSL_NOTYETVALID = subvertpy.SSL_NOTYETVALID
 SSL_OTHER = subvertpy.SSL_OTHER
+SSL_UNKNOWNCA = subvertpy.SSL_UNKNOWNCA
 SubversionException = subvertpy.SubversionException
 apply_txdelta = delta.apply_txdelta_handler
 # superclass for editor.HgEditor
--- a/hgsubversion/svnwrap/svn_swig_wrapper.py
+++ b/hgsubversion/svnwrap/svn_swig_wrapper.py
@@ -37,17 +37,18 @@ def version():
     return '%d.%d.%d' % current_bindings, 'SWIG'
 
 # exported values
+ERR_FS_ALREADY_EXISTS = core.SVN_ERR_FS_ALREADY_EXISTS
 ERR_FS_CONFLICT = core.SVN_ERR_FS_CONFLICT
 ERR_FS_NOT_FOUND = core.SVN_ERR_FS_NOT_FOUND
 ERR_FS_TXN_OUT_OF_DATE = core.SVN_ERR_FS_TXN_OUT_OF_DATE
 ERR_INCOMPLETE_DATA = core.SVN_ERR_INCOMPLETE_DATA
 ERR_RA_DAV_REQUEST_FAILED = core.SVN_ERR_RA_DAV_REQUEST_FAILED
 ERR_REPOS_HOOK_FAILURE = core.SVN_ERR_REPOS_HOOK_FAILURE
-SSL_UNKNOWNCA = core.SVN_AUTH_SSL_UNKNOWNCA
 SSL_CNMISMATCH = core.SVN_AUTH_SSL_CNMISMATCH
-SSL_NOTYETVALID = core.SVN_AUTH_SSL_NOTYETVALID
 SSL_EXPIRED = core.SVN_AUTH_SSL_EXPIRED
+SSL_NOTYETVALID = core.SVN_AUTH_SSL_NOTYETVALID
 SSL_OTHER = core.SVN_AUTH_SSL_OTHER
+SSL_UNKNOWNCA = core.SVN_AUTH_SSL_UNKNOWNCA
 SubversionException = core.SubversionException
 Editor = delta.Editor
 
--- a/tests/test_push_command.py
+++ b/tests/test_push_command.py
@@ -50,6 +50,54 @@ class PushTests(test_util.TestBase):
         tip = self.repo['tip']
         self.assertEqual(tip.node(), old_tip)
 
+    def test_push_add_of_added_upstream_gives_sane_error(self):
+        repo = self.repo
+        def file_callback(repo, memctx, path):
+            if path == 'adding_file':
+                return context.memfilectx(path=path,
+                                          data='foo',
+                                          islink=False,
+                                          isexec=False,
+                                          copied=False)
+            raise IOError()
+        p1 = repo['default'].node()
+        ctx = context.memctx(repo,
+                             (p1, node.nullid),
+                             'automated test',
+                             ['adding_file'],
+                             file_callback,
+                             'an_author',
+                             '2008-10-07 20:59:48 -0500',
+                             {'branch': 'default', })
+        new_hash = repo.commitctx(ctx)
+        hg.update(repo, repo['tip'].node())
+        old_tip = repo['tip'].node()
+        self.pushrevisions()
+        tip = self.repo['tip']
+        self.assertNotEqual(tip.node(), old_tip)
+
+        # This node adds the same file as the first one we added, and
+        # will be refused by the server for adding a file that already
+        # exists. We should respond with an error suggesting the user
+        # rebase.
+        ctx = context.memctx(repo,
+                             (p1, node.nullid),
+                             'automated test',
+                             ['adding_file'],
+                             file_callback,
+                             'an_author',
+                             '2008-10-07 20:59:48 -0500',
+                             {'branch': 'default', })
+        new_hash = repo.commitctx(ctx)
+        hg.update(repo, repo['tip'].node())
+        old_tip = repo['tip'].node()
+        try:
+          self.pushrevisions()
+        except hgutil.Abort, e:
+          assert "pull again and rebase" in str(e)
+        tip = self.repo['tip']
+        self.assertEqual(tip.node(), old_tip)
+
     def test_cant_push_with_changes(self):
         repo = self.repo
         def file_callback(repo, memctx, path):