very sorry, I forgot the attachments.
Liu Yubao wrote:
> Hi,
>
> I wrote two little scripts to generate and apply diff series for Subversion,
> you need Subversion command line client and patch utility program to run it.
> Windows users can download the latter from http://gnuwin32.sourceforge.net or
> http://unxutils.sourceforge.net. I hope they are useful for you.
>
> I know SVK can help decentralized development, but it hasn't pretty GUI frontend
> now(no TortoiseSVK) and seems can't synchronzie between two Subversion repositories
> easily.
>
> Another similar tool I found is svnpush, but it seems not be maintained long time and
> I guess it's a pain to install Perl modules in windows.
>
> svn-patches.pl doesn't commit automatically because it's not easy to erase
> your mistake in Subversion. It makes use of GNU patch, no 3-way merge is done,
> so maybe it's not so convenient as you hope.
>
> Below is a short scenario to show its usage.
>
> ----------------------------------------------------
> reposA --- public repository
> reposB --- private repository
> svn export reposA/branches/something@2000
> svn import reposB/branches/something@100
> svn co reposB/branches/something
> ...work...commit...now it reaches revision 120
>
> mkdir diffs;
> perl svn-diffs.pl URL_to_reposB/branches/something 101 120
>
> cd working_copy_for_reposA;
> make sure the working copy is clean and updated;
> svn lock reposA/branches/something -m "applying diff series, please wait..."
>
> perl svn-patches.pl ..\path_to\diffs
> ...check...commit...run svn-patches.pl again until all patches are applied.
>
> svn unlock reposA/branches/something
> -----------------------------------------------------
>
> regards,
>
> Liu Yubao
>
#!/usr/bin/perl -w
#
# svn-diffs.pl:
# generate a diff series for svn, they can be applied by another
# little Perl script svn-patches.pl.
#
# set environment variables "SVNUSER" and "SVNPASSWD" to provide
# user name and password if needed.
#
# note:
# svn properties are ignored.
#
# 2007-01-09 Liu Yubao <yubao.liu@gmail.com>
#
use strict;
use warnings;
my $URL = shift;
my $fromREV = shift;
my $toREV = shift;
die "Usage: perl $0 URL [fromREV] [toREV]\n" if !defined $URL;
$fromREV ||= 2;
my $LANG=$ENV{LANG};
$ENV{LANG} = "en_US.UTF-8"; # make parsing easy
my $svncmd = "svn";
if (exists $ENV{SVNUSER} && exists $ENV{SVNPASSWD}) {
$svncmd .= " --username $ENV{SVNUSER} --password $ENV{SVNPASSWD} ";
}
######################################################
# check whether svn has been installed
#
if (! qx/svn --version/) {
die "[svn-diffs] Can't execute svn: $!\n";
}
######################################################
# check whether we are in an empty directory, avoid file overwrite by mistake
#
{
my @dirs = <*>;
die "[svn-diffs] Please run me in an empty directory.\n" if @dirs > 0;
}
######################################################
# check URL
#
my ($line, @lines);
open F, "$svncmd info $URL |" || die "[svn-diffs] Can't execute 'svn info': $!\n";
@lines = <F>;
close F;
($line) = grep /^Last Changed Rev:/, @lines;
die "[svn-diffs] Error happened when executed 'svn info': $!\n" if !defined $line;
($line) = $line =~ /(\d+)/;
$toREV = $line if (!defined $toREV) || ($toREV > $line);
print "[svn-diffs] generate diffs for $URL [$fromREV, $toREV]\n";
######################################################
# get log and generate diff
#
open F, "$svncmd log -r $fromREV:$toREV $URL |" || die "[svn-diffs] Can't execute 'svn log': $!\n";
$line = <F>;
die "[svn-diffs] Error happened when executed 'svn log': $!\n" if $line !~ /-{72}/;
my ($rev, $author, $date, $n);
my ($count, $file) = (0, "");
while ($line = <F>) {
print "-----------------------------------------\n";
######################### parse log header #################################
++$count;
($rev, $author, $date, $n) = split / \| /, $line;
$rev = substr $rev, 1;
($n) = $n =~ /(\d+)/;
++$n; # include blank line after log header
$file = "$fromREV-$toREV-$count";
######################### generate log for one revision ####################
print "write log for revision $rev as '$file.log'...\n";
open LOG, ">$file.log" || die "[svn-diffs] Can't write $file.log: $!\n";
print LOG $line;
do {
$line = <F>;
print LOG $line;
} while (--$n > 0);
close LOG;
$line = <F>; # skip '----------------' log separator
######################### generate patch and binary files for one revision #
print "write diff for revision $rev as '$file.diff'...\n";
open F2, "$svncmd diff -r " . ($rev - 1) . ":$rev $URL |" || die "[svn-diffs] Can't execute 'svn diff': $!\n";
open DIFF, ">$file.diff" || die "[svn-diffs] Can't write $file.diff: $!\n";
my $index;
my @binary_files = ();
while ($line = <F2>) {
print DIFF $line;
if ($line =~ /^Index: (.*)/) {
$index = $1;
} elsif ($line =~ /^Cannot display: file marked as a binary type.$/) {
push @binary_files, $index;
print "write binary file '$index' for revision $rev as '$file-$#binary_files.bin'...\n";
system("$svncmd cat -r $rev $URL/$index > $file-$#binary_files.bin") &&
die "[svn-diff] Can't execute 'svn cat': $!\n";
}
}
close DIFF;
close F2;
######################### generate list of binary files ####################
if (@binary_files > 0) {
print "write list of binary files for revision $rev as '$file.idx'...\n";
open IDX, ">$file.idx" || die "[svn-diffs] Can't write $file.idx: $!\n";
foreach my $f (@binary_files) {
print IDX "$f\n";
}
close IDX;
}
}
close F;
#!/usr/bin/perl -w
#
# svn-patches.pl:
# apply a diff series generated by another little Perl script svn-diffs.pl.
#
# set environment variables "SVNUSER" and "SVNPASSWD" to provide
# user name and password if needed.
#
# note:
# svn properties are ignored, merge is done with GNU patch, no 3-way
# merge.
#
# 2007-01-09 Liu Yubao <yubao.liu@gmail.com>
#
use strict;
use warnings;
use File::Copy;
use File::Spec;
my $diffsDIR = shift;
die "Usage: perl $0 diffsDIR\n" if !defined $diffsDIR;
my $LANG=$ENV{LANG};
$ENV{LANG} = "en_US.UTF-8"; # make parsing easy
my $svncmd = "svn";
if (exists $ENV{SVNUSER} && exists $ENV{SVNPASSWD}) {
$svncmd .= " --username $ENV{SVNUSER} --password $ENV{SVNPASSWD} ";
}
######################################################
# check whether svn has been installed
#
if (! qx/svn --version/) {
die "[svn-patches] Can't execute 'svn': $!\n";
}
######################################################
# check whether patch has been installer
#
if (! qx/patch --version/) {
die "[svn-patches] Can't execute 'patch': $!\n";
}
######################################################
# check whether we are in a clean working copy
#
die "[svn-patches] Not a clean working copy.\n" if `svn status`;
######################################################
# retrieve last applied diff
#
my $svnadminDIR = ".svn";
$svnadminDIR = "_svn" if exists $ENV{SVN_ASP_DOT_NET_HACK};
my $prevDIFF;
my $prevDIFF_file = File::Spec->catfile($svnadminDIR, "_prevDIFF_");
if (-e $prevDIFF_file) {
open F, "<$prevDIFF_file" || die "[svn-patches] Can't open $prevDIFF_file: $!\n";
$prevDIFF = <F>;
chomp $prevDIFF;
close F;
}
######################################################
# apply diffs as many as possible or only one
#
my @diffs = glob(File::Spec->catfile($diffsDIR, "*.diff"));
die "[svn-patches] No .diff found in $diffsDIR.\n" if @diffs < 1;
@diffs = sort {
my ($a1) = $a =~ /\d+-\d+-(\d+)\.diff$/;
my ($b1) = $b =~ /\d+-\d+-(\d+)\.diff$/;
$a1 <=> $b1;
} @diffs;
my $nextDIFF; # diff being applied
if (!defined $prevDIFF) {
$nextDIFF = 0;
} else {
for ($nextDIFF = 0; $nextDIFF < @diffs; ++$nextDIFF) {
last if $diffs[$nextDIFF] eq $prevDIFF;
}
if ($nextDIFF == @diffs) {
print "[svn-patches] Can't find previous diff '$prevDIFF', can't go on patching\n";
exit -1;
} elsif ($nextDIFF == @diffs - 1) {
print "[svn-patches] All patches have been applied, great!\n";
unlink $prevDIFF_file;
exit 0;
} else {
++$nextDIFF;
}
}
my $ret = 0;
while ($nextDIFF < @diffs) {
print "------------------------------------------------------\n";
print "[svn-patches] Applying '$diffs[$nextDIFF]'...\n";
$ret = system "patch -N -p0 <" . $diffs[$nextDIFF];
if ($ret < 0) {
print "[svn-patches] Error happened when executed 'patch -p0': $!\n";
print "[svn-patches] run me again after you have fixed the problem.\n";
last;
} elsif ($ret > 0) { # conflict
$prevDIFF = $diffs[$nextDIFF];
copy_binary_files();
save_state();
print "[svn-patches] run me again after you have resolved conflicts.\n";
last;
} else {
$prevDIFF = $diffs[$nextDIFF];
copy_binary_files();
save_state();
++$nextDIFF;
print "[svn-patches] Successfully applied, great!\n";
# print "[svn-patches] Try to commit it...\n";
print "[svn-patches] run me again after you have checked and committed these changes.\n";
last;
}
}
######################################################
#
sub save_state {
print "[svn-patches] Save applying state to $prevDIFF_file.\n";
open F, ">$prevDIFF_file" || die "[svn-patches] Can't write $prevDIFF_file: $!\n";
print F "$prevDIFF\n";
close F;
}
######################################################
#
sub copy_binary_files {
my $idx = $prevDIFF;
$idx =~ s/\.diff$/\.idx/;
if (-e $idx) {
open F, "<$idx" || die "[svn-patches] Can't read $idx: $!\n";
my (@files, $prefix, $f);
@files = <F>;
chomp @files;
$prefix = $idx;
$prefix =~ s/\.idx$//;
for (my $i = 0; $i < @files; ++$i) {
$f = "$prefix-$i.bin";
print "[svn-patches] Copy $f to $files[$i].\n";
copy $f, $files[$i] || die "[svn-patches] Can't copy '$f' to '$files[$i]': $!\n";
}
close F;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@subversion.tigris.org
For additional commands, e-mail: users-help@subversion.tigris.org
Received on Tue Jan 9 05:44:51 2007