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

bug: wc out of sync with repos following commit of merge

From: Rob Hubbard <hubbard.r_at_gmail.com>
Date: Mon, 27 Sep 2010 00:31:15 +0100

  Hello,

I had some unexpected behaviour from SVN following the commit of a merge
operation. I believe I have found a bug in SVN.

I'm using version 1.6.12.

On the one hand, I don't think the bug is too bad, as it is something of
a corner case. On the other hand, the result is a working copy that is
out of synchronisation with the repository, which is a serious fault.

Below, I give a basic Perl script (which should work on either Windows
or Linux) to demonstrate the problem, together with some output. If you
don't wish to run the script, I hope that the sequence of commands to
reproduce the problem is clear by just reading through from the "###
create an empty repository" line.

Briefly, the steps to reproduce the problem are:
   * have a branch in which directories have been added
   * merge (reintegrate) a branch to the trunk, but don't commit yet
   * rename a subdirectory on the branch
   * merge to trunk again
   * commit

Now, the trunk in the repository has the subdirectory both as it was
before and after the rename, but the working copy doesn't.

The script is:
__________________________________________________________________

#!/usr/bin/perl

use strict;
use File::Path;
use Cwd;

sub WriteFile ($$)
{
     my $fn = shift;
     my $str = shift;
     my $ok = open (my $fh, '>', $fn);
     if ($ok)
     {
         print $fh $str;
         close ($fh);
     }
}

sub ls ($)
{
     my $path = shift;
     my @files = <${path}/*>;
     foreach my $myfile (@files)
     {
         $myfile =~ s/^${path}\///;
         print "$myfile\n";
     }
}

my $data = 'data/';
my $repos_rel_path = "${data}repos/";
my $wc_tree = "${data}wc/";

my $content = "Contents of the file.\n";

mkdir $data;

my $repos_path = cwd . '/' . $repos_rel_path;
(my $repos_url = 'file:///' . $repos_path) =~
s/([^-._~A-Za-z0-9\/:])/sprintf("%%%02X", ord($1))/egs;

### create an empty repository and working copy
`svnadmin create $repos_rel_path` if ! -e $repos_rel_path;
`svn co $repos_url $wc_tree` if ! -e $wc_tree;

### add some directories and files
mkdir "${wc_tree}trunk/";
WriteFile ("${wc_tree}trunk/file.txt", $content);
`svn add "${wc_tree}trunk/"`;
`svn ci "${wc_tree}" -m "initial files"`;

### branch
`svn cp "${repos_url}trunk/" "${repos_url}branch/" -m "branch"`;
`svn up "${wc_tree}"`;

### possibly change some trunk files

### add some branch files
mkdir "${wc_tree}branch/olddir/";
mkdir "${wc_tree}branch/olddir/oldsubdir/";
WriteFile ("${wc_tree}branch/olddir/subfile.txt", $content);
WriteFile ("${wc_tree}branch/olddir/oldsubdir/sub2file.txt", $content);
`svn add "${wc_tree}branch/olddir"`;
`svn ci "${wc_tree}" -m "branch adds"`;

### possibly update branch with trunk (merge)

### reintegrate (merge) branch to trunk, but don't commit yet
`svn merge --reintegrate "^/branch/" "${wc_tree}trunk/"`;

### make and commit forgotten directory rename on branch
`svn mv "${wc_tree}branch/olddir/oldsubdir/"
"${wc_tree}branch/olddir/newsubdir/"`;
`svn ci "${wc_tree}branch/" -m "branch mods"`;
`svn up "${wc_tree}"`;

### further merge branch to trunk
`svn merge "^/branch/" "${wc_tree}trunk/"`;

### resolve tree conflict (in favour of the move) and commit at last
`svn rm --force ${wc_tree}trunk/olddir/oldsubdir`;
`svn resolve --accept=working ${wc_tree}trunk/olddir/oldsubdir`;
`svn ci "${wc_tree}trunk/" -m "merge"`;
`svn up "${wc_tree}"`;

### display the result
print "\n\$ svn log\n"; print `svn log ${wc_tree}trunk/olddir/ -qvrHEAD`;
print "\n\$ ls wc\n"; ls "${wc_tree}trunk/olddir/";
print "\n\$ svn ls repos\n"; print `svn ls ${wc_tree}trunk/olddir/ -rHEAD`;

### reveal the synchronisation problem between wc and repos
### by manually updating the path that the wc doesn't know is missing
`svn up "${wc_tree}trunk/olddir/oldsubdir/"`;
print "\n\$ ls wc (2)\n"; ls "${wc_tree}trunk/olddir/";

### end of perl
__________________________________________________________________

The output from the script is:
__________________________________________________________________

$ svn log
------------------------------------------------------------------------
r5 | Rob | 2010-09-26 00:22:07 +0100 (Sun, 26 Sep 2010)
Changed paths:
    M /trunk
    A /trunk/olddir (from /branch/olddir:3)
    A /trunk/olddir/newsubdir (from /branch/olddir/newsubdir:4)
------------------------------------------------------------------------

$ ls wc
newsubdir
subfile.txt

$ svn ls repos
newsubdir/
oldsubdir/
subfile.txt

$ ls wc (2)
newsubdir
oldsubdir
subfile.txt
__________________________________________________________________

The output shows the mismatch between what's in a certain directory of
the working copy (ls wc) and of the repository (svn ls repos).

Note in the log that in the final commit (r5), path ^/trunk/olddir/ is
added from a revision prior to the merged rename. This is correct. I
believe that there should also be changes within that added directory.
So it should read something like this:

------------------------------------------------------------------------
r5 | Rob | 2010-09-26 00:22:07 +0100 (Sun, 26 Sep 2010)
Changed paths:
    M /trunk
    A /trunk/olddir (from /branch/olddir:3)
    D /trunk/olddir/oldsubdir
    A /trunk/olddir/newsubdir (from /branch/olddir/newsubdir:4)
------------------------------------------------------------------------

or better still, like this perhaps (with the later merge having moved
the copy-from revision on):

------------------------------------------------------------------------
r5 | Rob | 2010-09-26 00:22:07 +0100 (Sun, 26 Sep 2010)
Changed paths:
    M /trunk
    A /trunk/olddir (from /branch/olddir:4)
------------------------------------------------------------------------

Regards,
Rob.
Received on 2010-09-27 03:43:13 CEST

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