[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: Fixing issue 644 [PATCH]

From: Philip Martin <philip_at_codematters.co.uk>
Date: 2002-04-20 12:44:35 CEST

Philip Martin <philip@codematters.co.uk> writes:

> I suppose that rather than a PROPFIND we could use a CHECKOUT to
> detect if the file already exists. That looks quite simple to me (but
> I might be talking rubbish, I only started looking at HTTP/WebDAV 2
> days ago :-)

Yes, I probably was talking rubbish. Anyway, having tried a few
things here is a patch based on my original PROPFIND idea. Unlike the
other things I tried this method works, and has the benefit of
detecting the conflict in the early stages, before we get to sending
the postfix deltas.

Anybody think of a better way to do it?

* subversion/include/svn_error_codes.h: Add SVN_ERR_RA_ALREADY_EXISTS

* subversion/libsvn_ra_dav/ra_dav.h
  (svn_ra_dav__get_starting_props): New function.

* subversion/libsvn_ra_dav/props.c
  (svn_ra_dav__get_starting_props): New function

  (svn_ra_dav__get_baseline_info): Use svn_ra_dav__get_starting_props
  instead of svn_ra_dav__get_props_resource.

* subversion/libsvn_ra_dav/commit.c
  (commit_add_file): Call svn_ra_dav__get_starting_props to check whether
  file already exists.

  (resource_baton_t): Add created flag.

  (commit_open_root, commit_add_dir, commit_open_dir, commit_add_file,
  commit_open_file): Set created flag.

* subversion/tests/client/cmdline/commit_tests.py
  (commit_add_file_twice): New test

Index: ./subversion/include/svn_error_codes.h
===================================================================
--- ./subversion/include/svn_error_codes.h
+++ ./subversion/include/svn_error_codes.h Fri Apr 19 23:53:40 2002
@@ -349,6 +349,9 @@
     SVN_ERRDEF (SVN_ERR_RA_PROPS_NOT_FOUND,
                 "RA layer failed to fetch properties")
 
+ SVN_ERRDEF (SVN_ERR_RA_ALREADY_EXISTS,
+ "RA layer detected file already exists")
+
   /* End of ra_dav errors */
 
   /* These RA errors are specific to ra_local */
Index: ./subversion/tests/clients/cmdline/commit_tests.py
===================================================================
--- ./subversion/tests/clients/cmdline/commit_tests.py
+++ ./subversion/tests/clients/cmdline/commit_tests.py Sat Apr 20 00:22:03 2002
@@ -1286,6 +1286,66 @@
 
   return 0
 
+#----------------------------------------------------------------------
+
+# Issue #644 which failed over ra_dav.
+def commit_add_file_twice(sbox):
+ "issue 644 attempt to add a file twice"
+
+ if sbox.build():
+ return 1
+
+ wc_dir = sbox.wc_dir
+
+ # Create a file
+ gloo_path = os.path.join(wc_dir, 'A', 'D', 'H', 'gloo')
+ svntest.main.file_append(gloo_path, "hello")
+ svntest.main.run_svn(None, 'add', gloo_path)
+
+ # Create expected output tree.
+ output_list = [ [gloo_path, None, {}, {'verb' : 'Adding' }] ]
+ expected_output_tree = svntest.tree.build_generic_tree(output_list)
+
+ # Created expected status tree.
+ status_list = svntest.actions.get_virginal_status_list(wc_dir, '1')
+ status_list.append([gloo_path, None, {},
+ {'status' : 'A ',
+ 'wc_rev' : '0',
+ 'repos_rev' : '1'}]) # pre-commit status
+ for item in status_list:
+ item[3]['repos_rev'] = '2' # post-commit status
+ if (item[0] == gloo_path):
+ item[3]['wc_rev'] = '2'
+ item[3]['status'] = '_ '
+ expected_status_tree = svntest.tree.build_generic_tree(status_list)
+
+ # Commit should succeed
+ if svntest.actions.run_and_verify_commit (wc_dir,
+ expected_output_tree,
+ expected_status_tree,
+ None,
+ None, None,
+ None, None,
+ wc_dir):
+ return 1
+
+ # Update to state before commit
+ svntest.main.run_svn(None, 'up', '-r1', wc_dir)
+
+ # Create the file again
+ gloo_path = os.path.join(wc_dir, 'A', 'D', 'H', 'gloo')
+ svntest.main.file_append(gloo_path, "hello")
+ svntest.main.run_svn(None, 'add', gloo_path)
+
+ # Commit and *expect* a failure:
+ return svntest.actions.run_and_verify_commit (wc_dir,
+ None,
+ None,
+ "already exists",
+ None, None,
+ None, None,
+ wc_dir)
+
 ########################################################################
 # Run the tests
 
@@ -1311,6 +1371,7 @@
               commit_deleted_edited,
               commit_in_dir_scheduled_for_addition,
               commit_rmd_and_deleted_file,
+ commit_add_file_twice,
              ]
 
 if __name__ == '__main__':
Index: ./subversion/libsvn_ra_dav/ra_dav.h
===================================================================
--- ./subversion/libsvn_ra_dav/ra_dav.h
+++ ./subversion/libsvn_ra_dav/ra_dav.h Fri Apr 19 22:13:37 2002
@@ -214,6 +214,13 @@
                                              const ne_propname *which_props,
                                              apr_pool_t *pool);
 
+/* fetch a single resource's starting props from the server. */
+svn_error_t * svn_ra_dav__get_starting_props(svn_ra_dav_resource_t **rsrc,
+ ne_session *sess,
+ const char *url,
+ const char *label,
+ apr_pool_t *pool);
+
 /* fetch a single property from a single resource */
 svn_error_t * svn_ra_dav__get_one_prop(const svn_string_t **propval,
                                        ne_session *sess,
Index: ./subversion/libsvn_ra_dav/props.c
===================================================================
--- ./subversion/libsvn_ra_dav/props.c
+++ ./subversion/libsvn_ra_dav/props.c Fri Apr 19 22:15:15 2002
@@ -446,6 +446,16 @@
   return SVN_NO_ERROR;
 }
 
+svn_error_t * svn_ra_dav__get_starting_props(svn_ra_dav_resource_t **rsrc,
+ ne_session *sess,
+ const char *url,
+ const char *label,
+ apr_pool_t *pool)
+{
+ return svn_ra_dav__get_props_resource(rsrc, sess, url, label, starting_props,
+ pool);
+}
+
 svn_error_t *svn_ra_dav__get_baseline_info(svn_boolean_t *is_dir,
                                            svn_string_t *bc_url,
                                            svn_string_t *bc_relative,
@@ -503,8 +513,8 @@
 
     while (! svn_path_is_empty (path_s))
       {
- err = svn_ra_dav__get_props_resource(&rsrc, sess, path_s->data,
- NULL, starting_props, pool);
+ err = svn_ra_dav__get_starting_props(&rsrc, sess, path_s->data,
+ NULL, pool);
         if (! err)
           break; /* found an existing parent! */
 
Index: ./subversion/libsvn_ra_dav/commit.c
===================================================================
--- ./subversion/libsvn_ra_dav/commit.c
+++ ./subversion/libsvn_ra_dav/commit.c Fri Apr 19 23:53:57 2002
@@ -105,6 +105,7 @@
   resource_t *rsrc;
   apr_table_t *prop_changes; /* name/values pairs of changed (or new) properties. */
   apr_array_header_t *prop_deletes; /* names of properties to delete. */
+ svn_boolean_t created; /* set if this is an add rather than an update */
 } resource_baton_t;
 
 typedef struct
@@ -583,6 +584,7 @@
   root = apr_pcalloc(dir_pool, sizeof(*root));
   root->cc = cc;
   root->rsrc = rsrc;
+ root->created = FALSE;
 
   *root_baton = root;
 
@@ -649,6 +651,7 @@
   /* create a child object that contains all the resource urls */
   child = apr_pcalloc(dir_pool, sizeof(*child));
   child->cc = parent->cc;
+ child->created = TRUE;
   SVN_ERR( add_child(&child->rsrc, parent->cc, parent->rsrc,
                      name, 1, SVN_INVALID_REVNUM, dir_pool) );
 
@@ -726,6 +729,7 @@
   const char *name = svn_path_basename(path, dir_pool);
 
   child->cc = parent->cc;
+ child->created = FALSE;
   SVN_ERR( add_child(&child->rsrc, parent->cc, parent->rsrc,
                      name, 0, base_revision, dir_pool) );
 
@@ -800,9 +804,40 @@
   /* Construct a file_baton that contains all the resource urls. */
   file = apr_pcalloc(file_pool, sizeof(*file));
   file->cc = parent->cc;
+ file->created = TRUE;
   SVN_ERR( add_child(&file->rsrc, parent->cc, parent->rsrc,
                      name, 1, SVN_INVALID_REVNUM, file_pool) );
 
+ /* If the parent directory existed before this commit then there may be a
+ file with this URL already. We need to ensure such a file does not
+ exist, which we do by attempting a PROPFIND */
+ if (!parent->created)
+ {
+ svn_ra_dav_resource_t *res;
+ svn_error_t *err = svn_ra_dav__get_starting_props(&res,
+ file->cc->ras->sess,
+ file->rsrc->url, NULL,
+ file_pool);
+ if (!err)
+ {
+ /* If the PROPFIND succeeds the file already exists */
+ return svn_error_createf(SVN_ERR_RA_ALREADY_EXISTS, 0, NULL,
+ file_pool,
+ "file '%s' already exists", file->rsrc->url);
+ }
+ else if (err->apr_err == SVN_ERR_RA_REQUEST_FAILED)
+ {
+ /* ### TODO: This is what we get if the file doesn't exist
+ but an explicit not-found error might be better */
+ svn_error_clear_all(err);
+ }
+ else
+ {
+ /* A real error */
+ return err;
+ }
+ }
+
   if (! copyfrom_path)
     {
       /* This a truly new file. */
@@ -873,6 +908,7 @@
 
   file = apr_pcalloc(file_pool, sizeof(*file));
   file->cc = parent->cc;
+ file->created = FALSE;
   SVN_ERR( add_child(&file->rsrc, parent->cc, parent->rsrc,
                      name, 0, base_revision, file_pool) );
 

-- 
Philip
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Apr 20 12:45:53 2002

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.