You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/08/23 18:28:32 UTC

svn commit: r988188 - in /subversion/trunk/subversion: libsvn_client/cmdline.c libsvn_client/merge.c tests/cmdline/merge_tests.py

Author: stsp
Date: Mon Aug 23 16:28:31 2010
New Revision: 988188

URL: http://svn.apache.org/viewvc?rev=988188&view=rev
Log:
Allow locally added files and directories as merge targets.

See this thread for related discussion:
  Date: Wed, 4 Aug 2010 16:29:51 +0200
  From: Stefan Sperling
  To: dev@
  Subject: merging into locally added files
  Message-ID: <20...@ted.stsp.name>
  http://mail-archives.apache.org/mod_mbox/subversion-dev/201008.mbox/%3C20100804142951.GE30031@ted.stsp.name%3E

* subversion/libsvn_client/cmdline.c
  (check_root_url_of_target): Tolerate a "bad revision" error when
   attempting to query the URL of the merge target. Locally added targets
   don't have a URL yet.

* subversion/libsvn_client/merge.c
  (get_full_mergeinfo): Tolerate targets which have no location in the
   repository yet. Return empty implicit mergeinfo for such targets.

* subversion/tests/cmdline/merge_tests.py
  (merge_into_locally_added_file, merge_into_locally_added_directory,
   test_list): New tests.

Modified:
    subversion/trunk/subversion/libsvn_client/cmdline.c
    subversion/trunk/subversion/libsvn_client/merge.c
    subversion/trunk/subversion/tests/cmdline/merge_tests.py

Modified: subversion/trunk/subversion/libsvn_client/cmdline.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/cmdline.c?rev=988188&r1=988187&r2=988188&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/cmdline.c (original)
+++ subversion/trunk/subversion/libsvn_client/cmdline.c Mon Aug 23 16:28:31 2010
@@ -125,11 +125,14 @@ check_root_url_of_target(const char **ro
        * If the target itself is a URL to a repository that does not exist,
        * that's fine, too. The callers will deal with this argument in an
        * appropriate manter if it does not make any sense.
+       *
+       * Also tolerate locally added targets ("bad revision" error).
        */
       if ((err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
           || (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
           || (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
-          || (err->apr_err == SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED))
+          || (err->apr_err == SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED)
+          || (err->apr_err == SVN_ERR_CLIENT_BAD_REVISION))
         {
           svn_error_clear(err);
           return SVN_NO_ERROR;

Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=988188&r1=988187&r2=988188&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Mon Aug 23 16:28:31 2010
@@ -3367,6 +3367,7 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
       svn_revnum_t target_rev;
       svn_opt_revision_t peg_revision;
       apr_pool_t *sesspool = NULL;
+      svn_error_t *err;
 
       /* Assert that we have sane input. */
       SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start)
@@ -3374,9 +3375,26 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
                  && (start > end));
 
       peg_revision.kind = svn_opt_revision_working;
-      SVN_ERR(svn_client__derive_location(&url, &target_rev, target_abspath,
-                                          &peg_revision, ra_session,
-                                          ctx, result_pool, scratch_pool));
+      err = svn_client__derive_location(&url, &target_rev, target_abspath,
+                                        &peg_revision, ra_session,
+                                        ctx, result_pool, scratch_pool);
+
+      if (err)
+        {
+          if (err->apr_err == SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED)
+            {
+              /* We've been asked to operate on a target which has no location
+               * in the repository. Either it's unversioned (but attempts to
+               * merge into unversioned targets should not get as far as here),
+               * or it is locally added, in which case the target's implicit
+               * mergeinfo is empty. */
+              svn_error_clear(err);
+              *implicit_mergeinfo = apr_hash_make(result_pool);
+              return SVN_NO_ERROR;
+            }
+          else
+            return svn_error_return(err);
+        }
 
       if (target_rev <= end)
         {

Modified: subversion/trunk/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/merge_tests.py?rev=988188&r1=988187&r2=988188&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/merge_tests.py Mon Aug 23 16:28:31 2010
@@ -15764,6 +15764,113 @@ def copy_causes_phantom_eol_conflict(sbo
                                        expected_skip,
                                        None, None, None, None, None, 1, 1)
   
+
+#----------------------------------------------------------------------
+def merge_into_locally_added_file(sbox):
+  "merge into locally added file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Some paths we'll care about
+  pi_path = sbox.ospath("A/D/G/pi")
+  new_path = sbox.ospath("A/D/G/new")
+
+  shutil.copy(pi_path, new_path)
+  svntest.main.file_append(pi_path, "foo\n")
+  sbox.simple_commit(); # r2
+
+  sbox.simple_add(new_path)
+
+  expected_output = wc.State(wc_dir, {
+    'A/D/G/new' : Item(status='G '),
+    })
+  expected_mergeinfo_output = wc.State(wc_dir, {
+    'A/D/G/new'   : Item(status=' U'),
+    })
+  expected_elision_output = wc.State(wc_dir, {})
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({ 'A/D/G/new' : Item(status='A ', wc_rev=0)})
+  expected_status.tweak('A/D/G/pi', wc_rev=2)
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nfoo\n")
+  expected_disk.add({'A/D/G/new' : Item("This is the file 'pi'.\nfoo\n",
+                     props={SVN_PROP_MERGEINFO : '/A/D/G/pi:2'})})
+  expected_skip = wc.State(wc_dir, {})
+
+  svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
+                                       sbox.repo_url + '/A/D/G/pi', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None, None,
+                                       True, True, new_path)
+  sbox.simple_commit()
+
+#----------------------------------------------------------------------
+def merge_into_locally_added_directory(sbox):
+  "merge into locally added directory"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Some paths we'll care about
+  G_path = sbox.ospath("A/D/G")
+  pi_path = sbox.ospath("A/D/G/pi")
+  new_dir_path = sbox.ospath("A/D/new_dir")
+
+  svntest.main.file_append(pi_path, "foo\n")
+  sbox.simple_commit(); # r2
+
+  os.mkdir(new_dir_path)
+  svntest.main.file_append(os.path.join(new_dir_path, 'pi'),
+                           "This is the file 'pi'.\n")
+  svntest.main.file_append(os.path.join(new_dir_path, 'rho'),
+                           "This is the file 'rho'.\n")
+  svntest.main.file_append(os.path.join(new_dir_path, 'tau'),
+                           "This is the file 'tau'.\n")
+  sbox.simple_add(new_dir_path)
+
+  expected_output = wc.State(wc_dir, {
+    'A/D/new_dir/pi' : Item(status='G '),
+    })
+  expected_mergeinfo_output = wc.State(wc_dir, {
+    'A/D/new_dir'   : Item(status=' U'),
+    })
+  expected_elision_output = wc.State(wc_dir, {})
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({ 'A/D/new_dir' : Item(status='A ', wc_rev=0)})
+  expected_status.add({ 'A/D/new_dir/pi' : Item(status='A ', wc_rev=0)})
+  expected_status.add({ 'A/D/new_dir/rho' : Item(status='A ', wc_rev=0)})
+  expected_status.add({ 'A/D/new_dir/tau' : Item(status='A ', wc_rev=0)})
+  expected_status.tweak('A/D/G/pi', wc_rev=2)
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nfoo\n")
+  expected_disk.add({'A/D/new_dir' :
+                       Item(props={SVN_PROP_MERGEINFO : '/A/D/G:2'})})
+  expected_disk.add({'A/D/new_dir/pi' :
+                     Item(contents="This is the file 'pi'.\nfoo\n")})
+  expected_disk.add({'A/D/new_dir/rho' :
+                     Item(contents="This is the file 'rho'.\n")})
+  expected_disk.add({'A/D/new_dir/tau' :
+                     Item(contents="This is the file 'tau'.\n")})
+  expected_skip = wc.State(wc_dir, {})
+
+  svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
+                                       sbox.repo_url + '/A/D/G', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None, None,
+                                       True, True, new_dir_path)
+  sbox.simple_commit()
+
 ########################################################################
 # Run the tests
 
@@ -15951,6 +16058,8 @@ test_list = [ None,
               immediate_depth_merge_creates_minimal_subtree_mergeinfo,
               record_only_merge_creates_self_referential_mergeinfo,
               copy_causes_phantom_eol_conflict,
+              merge_into_locally_added_file,
+              merge_into_locally_added_directory,
              ]
 
 if __name__ == '__main__':