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

[PATCH] Speed up svn:externals updates by using an internal "svn switch" rather than a straight delete.

From: Ross Mark <rossm_at_controllingedge.com.au>
Date: 2005-01-04 04:07:33 CET

I spoke of this patch to speed up the use of svn:externals last year
when 1.1 was being readied for release. Being the slack person I am I
only just got around to porting it from 1.0 to 1.2.

The idea for the patch is very simple. The original svn code would
delete any existing working copy for an external item if its url didn't
match the entry in svn:externals. This meant that updates were very
inefficient for externals if the url changed. ie if you had the
svn:externals entry 'td file:///rep/tags/ver1' and the url was changed
to ...tags/ver2 the entire td directory would have been deleted and
checked out again.

I've also attached a test script that demonstrates the problem, plus I
used it to verify my changes. The script creates a repository and a
working copy. A file is created that is then copied like a tag. The file
is changed and a new copy is made. An svn:externals entry is made that
references the first copy. It is then changed to reference
the second copy. Without the patch the changes between the first copy
and the second copy would be seen as a new checkout. With the patch it
is seen as an update.

Here is the relevant output from the script without the patch applied.

Running an update to checkout externals/ver1 as td

Fetching external item into 'trunk/lib/td'
A trunk/lib/td/dir1
A trunk/lib/td/dir1/file1
Updated external to revision 5.

Updated to revision 5.
Changing svn:externals so td is externals/ver2
property 'svn:externals' set on 'trunk/lib'
Running an update to checkout externals/ver2 as td

Fetching external item into 'trunk/lib/td'
A trunk/lib/td/dir1
A trunk/lib/td/dir1/file1
Updated external to revision 6.

Updated to revision 6.

Here is the same script but with a patched version of svn.

Running an update to checkout externals/ver1 as td

Fetching external item into 'trunk/lib/td'
A trunk/lib/td/dir1
A trunk/lib/td/dir1/file1
Updated external to revision 5.

Updated to revision 5.
Changing svn:externals so td is externals/ver2
property 'svn:externals' set on 'trunk/lib'
Running an update to checkout externals/ver2 as td

Fetching external item into 'trunk/lib/td'
U trunk/lib/td/dir1/file1
Updated external to revision 6.

Updated to revision 6.

The difference you see is that it only required an update to get to
revision 6 rather than a new checkout.

Log Message:
If the url of an svn:externals entry changed attempt to do a switch with
the new url
and only if that fails do we try and delete the existing wc and checkout
the new.

* subversion/libsvn_client/externals.c
    (handle_external_item_change) Before calling relegate_external try
    and perform a switch using the new url. If that fails then call
relegate_external.

#!/bin/sh
REP=file://`pwd`/testrep
rm -rf testrep wc
#set -x
SVN=`which svn`
SVNADMIN=`which svnadmin`
echo using $SVN and $SVNADMIN
$SVN --version
$SVNADMIN --version
echo Create the testrep
$SVNADMIN create testrep

echo Checking out the empty repository.
$SVN co -q $REP wc

echo Creating a basic file structure with one test file.
cd wc
mkdir externals
mkdir trunk
mkdir -p trunk/src/dir1
echo file1 >trunk/src/dir1/file1
$SVN add -q externals trunk
$SVN ci -m "first version" -q
$SVN update -q

echo Copying the trunk/src as externals/ver1
$SVN cp -q trunk/src externals/ver1
$SVN ci -m "version1" -q
$SVN update -q

echo Changing the contents of trunk/src/dir1/file1
echo file2 >trunk/src/dir1/file1
$SVN ci -m "second file"
$SVN update -q

echo Copying the trunk/src as externals/ver2
$SVN cp trunk/src externals/ver2
$SVN ci -m "version2" -q
$SVN update -q

echo Creating a lib directory for the svn:externals.
mkdir trunk/lib
$SVN add trunk/lib -q
$SVN propset svn:externals "td $REP/externals/ver1" trunk/lib
$SVN ci -m "extv1" -q
echo Running an update to checkout externals/ver1 as td
$SVN update

echo Changing svn:externals so td is externals/ver2
$SVN propset svn:externals "td $REP/externals/ver2" trunk/lib
$SVN ci -m "extv2" -q
echo Running an update to checkout externals/ver2 as td
$SVN update

# Now test with the externals as a completely different repository

echo
echo Create a different repository
echo

cd ..
rm -rf testrep2 wc2
REP2=file://`pwd`/testrep2
$SVNADMIN create testrep2
$SVN co -q $REP2 wc2
cd wc2
date >now
mkdir subdir
echo some data >subdir/new_file
$SVN add -q now subdir
$SVN ci -m "other file data" -q

cd ../wc
$SVN propset svn:externals "td $REP2/" trunk/lib
$SVN ci -m "other rep" -q
echo Running an update to checkout a different repository as td
$SVN update

Index: subversion/libsvn_client/externals.c
===================================================================
--- subversion/libsvn_client/externals.c (revision 12580)
+++ subversion/libsvn_client/externals.c (working copy)
@@ -285,15 +285,7 @@
     }
   else if (! compare_external_items (new_item, old_item))
     {
- /* ### Better yet, compare_external_items should report the
- nature of the difference. That way, when it's just a change
- in the "-r REV" portion, for example, we could do an update
- here instead of a relegation followed by full checkout. */
-
- SVN_ERR (relegate_external (path,
- ib->ctx->cancel_func,
- ib->ctx->cancel_baton,
- ib->pool));
+ svn_error_t *err;
       
       /* First notify that we're about to handle an external. */
       if (ib->ctx->notify_func)
@@ -306,12 +298,41 @@
                                  svn_wc_notify_state_unknown,
                                  SVN_INVALID_REVNUM);
 
- SVN_ERR (svn_client__checkout_internal (NULL, new_item->url, path,
- &(new_item->revision),
- &(new_item->revision),
- TRUE, /* recurse */
- ib->timestamp_sleep,
- ib->ctx, ib->pool));
+ /* Try doing a switch first */
+ err = svn_client_switch (NULL,
+ path,
+ new_item->url,
+ &(new_item->revision),
+ TRUE,
+ ib->ctx,
+ ib->pool);
+ if (err)
+ {
+ /* The switch failed so do a cleanup and delete the old*/
+ svn_error_clear (err);
+ err = svn_client_cleanup(path, ib->ctx, ib->pool);
+ if (err)
+ svn_error_clear(err);
+
+ /* ### Better yet, compare_external_items should report the
+ nature of the difference. That way, when it's just a change
+ in the "-r REV" portion, for example, we could do an update
+ here instead of a relegation followed by full checkout. */
+
+ SVN_ERR (relegate_external (path,
+ ib->ctx->cancel_func,
+ ib->ctx->cancel_baton,
+ ib->pool));
+
+
+
+ SVN_ERR (svn_client__checkout_internal (NULL, new_item->url, path,
+ &(new_item->revision),
+ &(new_item->revision),
+ TRUE, /* recurse */
+ ib->timestamp_sleep,
+ ib->ctx, ib->pool));
+ }
     }
   else if (ib->update_unchanged)
     {
@@ -358,19 +379,37 @@
              the one subdir, and then checkout a new one. */
           else
             {
- /* Buh-bye, old and busted ... */
- SVN_ERR (relegate_external (path,
- ib->ctx->cancel_func,
- ib->ctx->cancel_baton,
- ib->pool));
- /* ... Hello, new hotness. */
- SVN_ERR (svn_client__checkout_internal (NULL, new_item->url,
- path,
- &(new_item->revision),
- &(new_item->revision),
- TRUE, /* recurse */
- ib->timestamp_sleep,
- ib->ctx, ib->pool));
+ svn_error_t *err;
+ /* Try doing a switch first */
+ err = svn_client_switch (NULL,
+ path,
+ new_item->url,
+ &(new_item->revision),
+ TRUE,
+ ib->ctx,
+ ib->pool);
+ if (err)
+ {
+ /* The switch failed so do a cleanup and delete the old*/
+ svn_error_clear (err);
+ err = svn_client_cleanup(path, ib->ctx, ib->pool);
+ if (err)
+ svn_error_clear(err);
+
+ /* Buh-bye, old and busted ... */
+ SVN_ERR (relegate_external (path,
+ ib->ctx->cancel_func,
+ ib->ctx->cancel_baton,
+ ib->pool));
+ /* ... Hello, new hotness. */
+ SVN_ERR (svn_client__checkout_internal (NULL, new_item->url,
+ path,
+ &(new_item->revision),
+ &(new_item->revision),
+ TRUE, /* recurse */
+ ib->timestamp_sleep,
+ ib->ctx, ib->pool));
+ }
             }
         }
       else /* Not a directory at all -- just try the checkout. */

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Jan 4 04:07:50 2005

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.