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

Differences between WC->WC copy, URL->URL copy, and $dirent->created_rev()

From: Nik Clayton <nik_at_ngo.org.uk>
Date: 2006-01-28 11:07:50 CET

[ Originally sent to users@, members of #svn-dev suggested I send it here. ]


I'm not (yet) subscribed to this list -- I'd be grateful for a cc: on any

I've run in to an anomaly in the way SVN handles copies when they're WC ->
WC copies compared to when they're URL -> URL copies. This might be an
actual problem, or it might be down to a lack of understanding of the API on
my part.

I'm looking at converting an application that currently uses SVN::Repos (and
therefore has to run on the same host that holds the repository files) to
use SVN::Ra. FWIW, the app is SVN::Web.

One of the things the app needs to do is, given a repo, a filename, and
optionally a revision number, find the most recent interesting revision of
the file.

With SVN::Repos I do this:

     my $path = '/path/in/repo';
     my $fs = $repo->fs(); # $repo is an opened repo
     my $rev = $fs->youngest_rev();
     my $root = $fs->revision_root($rev);
     my $hist = $root->node_history($path);
     my $irev; # interesting revision

     $hist = $hist->prev(0);
     $irev = ($hist->location())[1]; # 2nd item returned by location()

With SVN::Ra I was trying this approach.

     my $path = '/path/in/repo';
     my $rev = $ra->get_latest_revnum(); # $ra is an SVN::Ra object
     my $ent = $ra->stat($path, $rev);
     my $irev;

     $irev = $ent->created_rev();

Which seems to work in most cases.

However, there's a case where it doesn't work.

Suppose your repo looks like this:

     Entry Created in
     /trunk 1
     /branches 1

     /trunk/foo 2
     /trunk/bar 2

     /branches/test1 3 Copied from trunk:2, WC -> WC
     /branches/test1/foo 3
     /branches/test1/bar 3

     /branches/test1/bar 4 Modified, and committed

     /branches/test2 5 Copied from trunk:4, URL -> URL
     /branches/test2/foo 5
     /branches/test2/bar 5

     /branches/test2/bar 6 Modified, and committed

In this case, and given the code above, you would expect both approaches to
agree on what is the interesting revision for each file.

That's only the case with WC -> WC copy. Both approaches agree that the
interesting revision for /branches/test1/foo is 4.

However, with a URL -> URL copy, they differ.

Using SVN::Repos, it tells me that the youngest interesting revision for
/branches/test2/foo is 5. This seems to make sense -- it's the revision at
which this file appeared in the repository.

Using SVN::Ra, it tells me that the youngest interesting revision for
/branches/test2/foo is 2 -- i.e., the revision in which it was last modified
on /trunk.

This difference is also reflected in the 'svn log' output.

With the WC -> WC copy you see this from 'svn log'

     Changed paths:
       A /branches/test1 (from /trunk:1)
       A /branches/test1/bar (from /trunk/bar:2)
       A /branches/test1/foo (from /trunk/foo:2)

With the URL -> URL copy you see this:

     Changed paths:
       A /branches/test2 (from /trunk:4)

Notice that there's no 'A' line for /branches/test2/bar or /branches/test2/foo.

This situation persists until the file is affected by a later commit. At
which point $ent->created_rev() returns the correct value. So in the
example above, both ::Ra and ::Repos agree that the youngest interesting
revision for /branches/test2/bar is 6.

This doesn't seem right. Is this a bug, or am I using the API incorrectly?
If I am using the API incorrectly, could someone indicate what the correct
approach is? I'm currently using Subversion 1.3.0 rc4.

Attached are two files.

mkrepo.sh is a shell script that, given a path, will create a repo in that
path that mirrors the one described here.

     sh mkrepo.sh /tmp/repo

for example. test.pl is a small Perl program that uses SVN::Ra and
SVN::Repos to access the repo and see whether or not the two approaches
agree on which is the youngest interesting revision. To run it,

     perl test.pl /tmp/repo

I get this output:

ok 1 - ra/repos agree on youngest rev
ok 2 - ra/repos agree on interesting rev for /trunk/foo
ok 3 - ra/repos agree on interesting rev for /trunk/bar
ok 4 - ra/repos agree on interesting rev for /branches/test1/foo
ok 5 - ra/repos agree on interesting rev for /branches/test1/bar
not ok 6 - ra/repos agree on interesting rev for /branches/test2/foo
# Failed test 'ra/repos agree on interesting rev for /branches/test2/foo'
# in test.pl at line 30.
# got: '2'
# expected: '5'
ok 7 - ra/repos agree on interesting rev for /branches/test2/bar
# Looks like you failed 1 test of 7.

As you can see, for /branches/test2/foo ::Ra says the youngest interesting
rev is 2, ::Repos says it's 5.

Any advice gratefully received.




if [ "$1x" = "x" ]; then
        echo "usage: $0 /path/to/repo"

# Blow away repo directory, and WC
rm -rf $1/*
rm -rf repo

# Create new repo, check it out, cd(1) to it.
svnadmin create /tmp/repo
svn checkout $repo
cd repo

# Create trunk/ and branches/
mkdir trunk branches
svn add trunk branches
svn commit -m 'trunk branches' trunk branches

# Create two files in trunk/
cd trunk
touch foo bar
svn add foo bar
svn commit -m 'Add foo and bar'

# Copy trunk/ to branches/test1/, using a WC -> WC copy, and ::Ra and
# ::Repos both handle this properly
cd ..
svn copy trunk branches/test1
svn commit -m 'Create test1 with "svn copy trunk branches/test1"'
svn update

# Make a change to branches/test1/bar and commit.
cd branches/test1
echo bar > bar
svn commit -m "Add 'bar'"

# Copy trunk/ to branches/test2/, using a URL -> URL copy. ::Ra wil have
# problems with this.
svn copy $repo/trunk $repo/branches/test2 -m 'Create test2 with immediate copy'
cd ..
svn update

# Make a change to branches/test2/bar, and commit. ::Ra and ::Repos will now
# agree on the youngest rev for branches/test2/bar, but they will disagree
# on the youngest rev for branches/test2/foo (which has not been changed
# since it was copied). This is the issue.
cd test2
echo bar > bar
svn commit -m "Add 'bar'"

echo Now run:
echo " perl test.pl $1"


use warnings;
use strict;

use Test::More tests => 7;

use SVN::Core;
use SVN::Repos;
use SVN::Fs;
use SVN::Ra;

my $repo_path = shift;

die "usage: $0 /path/to/repo\n" if ! defined $repo_path;

my $ra = SVN::Ra->new("file://$repo_path");
my $repo = SVN::Repos::open($repo_path);
my $fs = $repo->fs();

my $latest_rev_ra = $ra->get_latest_revnum();
my $latest_rev_repo = $fs->youngest_rev();

is($latest_rev_ra, $latest_rev_repo, 'ra/repos agree on youngest rev');

foreach my $file (qw(/trunk/foo /trunk/bar
                     /branches/test1/foo /branches/test1/bar
                     /branches/test2/foo /branches/test2/bar)) {

  is(check_ra($file, $latest_rev_ra), check_repos($file, $latest_rev_repo),
     "ra/repos agree on interesting rev for $file");

# Both functions return the youngest interesting rev for $path that
# is <= $rev.
# check_ra uses SVN::Ra, check_repos uses SVN::Repos.
# I would expect them both to return the same result
sub check_ra {
    my $path = shift;
    my $rev = shift;

    my $ent = $ra->stat($path, $rev);
    return $ent->created_rev();

sub check_repos {
    my $path = shift;
    my $rev = shift;

    my $root = $fs->revision_root($rev);
    my $hist = $root->node_history($path);

    $hist = $hist->prev(0);
    return ($hist->location())[1];

To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sun Jan 29 15:19:43 2006

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.