
[[[
Check repository UUID when creating an RA session.  This fixes an issue
where "svn update" would silently overwrite the UUID of a working copy
if the repository UUID has changed.

Several tests in svnadmin_tests which silently took advantage of this
are fixed too.

* subversion/include/svn_ra.h 
  (svn_ra_open2, svn_ra_open3):  New API revision.

* subversion/libsvn_ra/ra_loader.c
  (svn_ra_open2, svn_ra_open3):  New API revision.

* subversion/libsvn_client/ra.c
  (svn_client__open_ra_session_internal):
    When having a working copy, pass its uuid to svn_ra_open3().

* subversion/tests/cmdline/update_tests.py
  (update_uuid_changed): New test.
  (test_list): Run it.

* subversion/tests/cmdline/svnadmin_tests.py
  (extra_headers, extra_blockcontent, empty_date):
    Load dumpfile with '--ignore-uuid' in order to not change the repository
    uuid, since a working copy already has been checked out from it.

* subversion/tests/cmdline/switch_tests.py
  (switch_change_repos_root):  Change expected error message.

* subversion/include/svn_error_codes.h
  (SVN_ERR_RA_UUID_MISMATCH):  New error.
]]]

[[[
Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h	(revision 30027)
+++ subversion/include/svn_ra.h	(working copy)
@@ -578,12 +578,30 @@ typedef struct svn_ra_session_t svn_ra_session_t;
  * @c svn_config_t * values.  For example, the @c svn_config_t for the
  * "~/.subversion/config" file is under the key "config".
  *
+ * Return @c SVN_ERR_RA_UUID_MISMATCH if @a uuid is non-@c NULL and not equal
+ * to the UUID of the repository at @c repos_URL.
+ *
  * All RA requests require a session; they will continue to
  * use @a pool for memory allocation.
  *
  * @see svn_client_open_ra_session().
  *
+ * @since New in 1.5.
+ */
+svn_error_t *
+svn_ra_open3(svn_ra_session_t **session_p,
+             const char *repos_URL,
+             const svn_ra_callbacks2_t *callbacks,
+             void *callback_baton,
+             const char *uuid,
+             apr_hash_t *config,
+             apr_pool_t *pool);
+
+/**
+ * Similiar to svn_ra_open3(), but with @a uuid set to @c NULL.
+ *
  * @since New in 1.3.
+ * @deprecated Provided for backward compatibility with the 1.4 API.
  */
 svn_error_t *
 svn_ra_open2(svn_ra_session_t **session_p,
Index: subversion/libsvn_ra/ra_loader.c
===================================================================
--- subversion/libsvn_ra/ra_loader.c	(revision 30027)
+++ subversion/libsvn_ra/ra_loader.c	(working copy)
@@ -378,10 +378,11 @@ svn_ra_create_callbacks(svn_ra_callbacks2_t **call
   return SVN_NO_ERROR;
 }
 
-svn_error_t *svn_ra_open2(svn_ra_session_t **session_p,
+svn_error_t *svn_ra_open3(svn_ra_session_t **session_p,
                           const char *repos_URL,
                           const svn_ra_callbacks2_t *callbacks,
                           void *callback_baton,
+                          const char *uuid,
                           apr_hash_t *config,
                           apr_pool_t *pool)
 {
@@ -468,10 +469,37 @@ svn_ra_create_callbacks(svn_ra_callbacks2_t **call
   SVN_ERR(vtable->open_session(session, repos_URL, callbacks, callback_baton,
                                config, pool));
 
+  /* Check the UUID. */
+  if (uuid)
+    {
+      const char *actual_uuid;
+
+      SVN_ERR(vtable->get_uuid(session, &actual_uuid, pool));
+
+      if (strcmp(uuid, actual_uuid) != 0)
+        {
+          return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
+                                   _("Repository UUID '%s' doesn't match "
+                                     "expected UUID '%s'"),
+                                   actual_uuid, uuid);
+        }
+    }
+
   *session_p = session;
   return SVN_NO_ERROR;
 }
 
+svn_error_t *svn_ra_open2(svn_ra_session_t **session_p,
+                          const char *repos_URL,
+                          const svn_ra_callbacks2_t *callbacks,
+                          void *callback_baton,
+                          apr_hash_t *config,
+                          apr_pool_t *pool)
+{
+  return svn_ra_open3(session_p, repos_URL, callbacks, callback_baton,
+                      NULL, config, pool);
+}
+
 svn_error_t *svn_ra_open(svn_ra_session_t **session_p,
                          const char *repos_URL,
                          const svn_ra_callbacks_t *callbacks,
Index: subversion/libsvn_client/ra.c
===================================================================
--- subversion/libsvn_client/ra.c	(revision 30027)
+++ subversion/libsvn_client/ra.c	(working copy)
@@ -291,6 +291,7 @@ svn_client__open_ra_session_internal(svn_ra_sessio
 {
   svn_ra_callbacks2_t *cbtable = apr_pcalloc(pool, sizeof(*cbtable));
   svn_client__callback_baton_t *cb = apr_pcalloc(pool, sizeof(*cb));
+  const char *uuid = NULL;
 
   cbtable->open_tmp_file = use_admin ? open_admin_tmp_file : open_tmp_file;
   cbtable->get_wc_prop = use_admin ? get_wc_prop : NULL;
@@ -310,7 +311,17 @@ svn_client__open_ra_session_internal(svn_ra_sessio
   cb->commit_items = commit_items;
   cb->ctx = ctx;
 
-  SVN_ERR(svn_ra_open2(ra_session, base_url, cbtable, cb,
+  if (base_access)
+    {
+      svn_wc_entry_t *entry;
+      
+      SVN_ERR(svn_wc_entry(&entry, base_dir, base_access, FALSE, pool));
+
+      if (entry && entry->uuid)
+        uuid = entry->uuid;
+    }
+
+  SVN_ERR(svn_ra_open3(ra_session, base_url, cbtable, cb, uuid,
                        ctx->config, pool));
 
   return SVN_NO_ERROR;
Index: subversion/tests/cmdline/update_tests.py
===================================================================
--- subversion/tests/cmdline/update_tests.py	(revision 30027)
+++ subversion/tests/cmdline/update_tests.py	(working copy)
@@ -3740,7 +3740,46 @@ interactive-conflicts = true
                                         '-r1', wc_dir)
 
 
+#----------------------------------------------------------------------
 
+
+def update_uuid_changed(sbox):
+  "update fails when repos uuid changed"
+  
+  def wc_uuid(wc_dir):
+    "Return the UUID of the working copy at WC_DIR."
+
+    exit_code, output, errput = svntest.main.run_svn(None, 'info', wc_dir)
+    if errput:
+      raise svntest.verify.SVNUnexpectedStderr(errput)
+
+    for line in output:
+      if line.startswith('Repository UUID:'):
+        return line[17:].rstrip()
+
+    # No 'Repository UUID' line in 'svn info'?
+    raise svntest.verify.SVNUnexpectedStdout(output)
+
+  sbox.build(read_only = True)
+  wc_dir = sbox.wc_dir
+  repo_dir = sbox.repo_dir
+  
+  uuid_before = wc_uuid(wc_dir)
+
+  # Change repository's uuid.
+  svntest.actions.run_and_verify_svnadmin(None, None, [],
+                                          'setuuid', repo_dir)
+  
+  # 'update' detected the new uuid...
+  svntest.actions.run_and_verify_svn(None, None, '.*UUID.*',
+                                     'update', wc_dir)
+  
+  # ...and didn't overwrite the old uuid.
+  uuid_after = wc_uuid(wc_dir)
+  if uuid_before != uuid_after:
+    raise svntest.Failure
+
+
 #######################################################################
 # Run the tests
 
@@ -3791,6 +3830,7 @@ test_list = [ None,
               update_copied_from_replaced_and_changed,
               update_accept_conflicts,
               eof_in_interactive_conflict_resolver,
+              update_uuid_changed,
              ]
 
 if __name__ == '__main__':
Index: subversion/tests/cmdline/svnadmin_tests.py
===================================================================
--- subversion/tests/cmdline/svnadmin_tests.py	(revision 30027)
+++ subversion/tests/cmdline/svnadmin_tests.py	(working copy)
@@ -204,7 +204,8 @@ def extra_headers(sbox):
   dumpfile[3:3] = \
        [ "X-Comment-Header: Ignored header normally not in dump stream\n" ]
 
-  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile)
+  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile,
+                             '--ignore-uuid')
 
 #----------------------------------------------------------------------
 # Ensure loading continues after skipping a bit of unknown extra content.
@@ -222,7 +223,8 @@ def extra_blockcontent(sbox):
   # Insert the extra content after "PROPS-END\n"
   dumpfile[11] = dumpfile[11][:-2] + "extra text\n\n\n"
 
-  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile)
+  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile,
+                             '--ignore-uuid')
 
 #----------------------------------------------------------------------
 def inconsistent_headers(sbox):
@@ -254,7 +256,8 @@ def empty_date(sbox):
          "K 7\nsvn:log\nV 0\n\nK 10\nsvn:author\nV 4\nerik\nPROPS-END\n\n\n"
          ]
 
-  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile)
+  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile,
+                             '--ignore-uuid')
 
   # Verify that the revision still lacks the svn:date property.
   svntest.actions.run_and_verify_svn(None, [], [], "propget",
Index: subversion/tests/cmdline/switch_tests.py
===================================================================
--- subversion/tests/cmdline/switch_tests.py	(revision 30027)
+++ subversion/tests/cmdline/switch_tests.py	(working copy)
@@ -1066,7 +1066,7 @@ def switch_change_repos_root(sbox):
 
   svntest.main.create_repos(other_repo_dir)
   svntest.actions.run_and_verify_svn(None, None,
-                                     ".*not the same repository.*",
+                                     ".*UUID.*",
                                      'switch',
                                      other_A_url, A_wc_dir)
 
Index: subversion/include/svn_error_codes.h
===================================================================
--- subversion/include/svn_error_codes.h	(revision 30027)
+++ subversion/include/svn_error_codes.h	(working copy)
@@ -739,6 +739,11 @@ SVN_ERROR_START
              SVN_ERR_RA_CATEGORY_START + 8,
              "Server can only replay from the root of a repository")
 
+  /** @since New in 1.5. */
+  SVN_ERRDEF(SVN_ERR_RA_UUID_MISMATCH,
+             SVN_ERR_RA_CATEGORY_START + 9,
+             "Repository UUID does not match expected UUID")
+
   /* ra_dav errors */
 
   SVN_ERRDEF(SVN_ERR_RA_DAV_SOCK_INIT,
]]]
