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