[svk-commit] r2519 - in branches/delta-refactor: . lib/SVK/Command
lib/SVK/Path
nobody at bestpractical.com
nobody at bestpractical.com
Sat Jul 28 11:29:06 EDT 2007
Author: clkao
Date: Sat Jul 28 11:29:05 2007
New Revision: 2519
Added:
branches/delta-refactor/lib/SVK/Delta.pm (contents, props changed)
branches/delta-refactor/lib/SVK/DeltaOld.pm (contents, props changed)
Modified:
branches/delta-refactor/ (props changed)
branches/delta-refactor/lib/SVK/Command/Add.pm
branches/delta-refactor/lib/SVK/Command/Commit.pm
branches/delta-refactor/lib/SVK/Command/Copy.pm
branches/delta-refactor/lib/SVK/Command/Import.pm
branches/delta-refactor/lib/SVK/Command/Revert.pm
branches/delta-refactor/lib/SVK/Command/Status.pm
branches/delta-refactor/lib/SVK/Path/Checkout.pm
branches/delta-refactor/lib/SVK/Util.pm
branches/delta-refactor/lib/SVK/XD.pm
Log:
Merge my WIP for checkout_delta refactoring.
The purpose for this branch is to make checkout_delta make use of
root objects, this will provide the ability to make use of arbitary
root objects for generating delta editor calls, which then unlocks
svk from only being able to use svn's fs_root.
The branch shall:
* reimplement the methods in SVK::OldDelta with root-based
callconv in SVK::Delta.
* get rid of the use of compat_arg hash in SVK::Delta.
* fix the current run_delta callers that are modifying the checkout
metadata (such as revert and commit --import), and make the new
delta function able to handle it.
* retire the old delta functions and do a delta api name cleanup.
r3944 at ubuntu: clkao | 2006-10-28 05:10:09 +0800
branch for using delta in root level.
r3945 at ubuntu: clkao | 2006-10-28 05:53:06 +0800
First cut of delta refactor.
r7463 at ubuntu: clkao | 2007-01-27 21:23:25 +0800
merge down
r7464 at ubuntu: clkao | 2007-01-27 21:34:19 +0800
Apply changes in SVK::XD to SVK::Delta with merge.
r7465 at ubuntu: clkao | 2007-01-27 22:57:10 +0800
Turn most of checkout_delta into $target->run_delta.
r7466 at ubuntu: clkao | 2007-01-27 23:07:52 +0800
Ditto for Command::Commit.
r7467 at ubuntu: clkao | 2007-01-27 23:41:23 +0800
push most of the compat layers into _delta_dir2.
r7468 at ubuntu: clkao | 2007-01-28 01:01:06 +0800
No longer calling _delta_dir from _delta_dir2.
r7469 at ubuntu: clkao | 2007-01-28 01:31:03 +0800
Move old style args into %compatarg.
r7470 at ubuntu: clkao | 2007-01-28 01:52:20 +0800
split delta.pm and move the old code to deltaold.pm for easier refactoring.
r7471 at ubuntu: clkao | 2007-01-28 02:15:55 +0800
Make it easier to run the old code.
r7472 at ubuntu: clkao | 2007-01-28 02:23:21 +0800
unbreak crazy-replace.
r9549 at ubuntu: clkao | 2007-07-26 13:57:59 +0800
merge down from trunk.
r9550 at ubuntu: clkao | 2007-07-26 14:00:14 +0800
catch up with changes from olddelta.
r9551 at ubuntu: clkao | 2007-07-26 14:13:15 +0800
be very noisy about deprecated method
r9552 at ubuntu: clkao | 2007-07-26 14:17:24 +0800
move checout_delta1 to olddelta.
r9553 at ubuntu: clkao | 2007-07-26 14:19:20 +0800
switch to delta_rev2.
r9554 at ubuntu: clkao | 2007-07-26 14:35:09 +0800
new callconv for _node_props2
r9555 at ubuntu: clkao | 2007-07-26 14:50:52 +0800
node_props2
r9556 at ubuntu: clkao | 2007-07-26 15:05:27 +0800
time to do delta_file2
r9557 at ubuntu: clkao | 2007-07-26 15:08:59 +0800
cb_ignored
r9558 at ubuntu: clkao | 2007-07-26 15:11:33 +0800
minor cleanups for compat args.
r9559 at ubuntu: clkao | 2007-07-26 19:03:46 +0800
get_entry was missing.
r9560 at ubuntu: clkao | 2007-07-26 22:16:27 +0800
Revert needs to use the old delta for now.
r9561 at ubuntu: clkao | 2007-07-28 18:40:51 +0800
import should work again.
r9562 at ubuntu: clkao | 2007-07-28 18:56:29 +0800
Make node_props2 DTRT when node is unknown.
Modified: branches/delta-refactor/lib/SVK/Command/Add.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Command/Add.pm (original)
+++ branches/delta-refactor/lib/SVK/Command/Add.pm Sat Jul 28 11:29:05 2007
@@ -87,37 +87,40 @@
}
}
- $self->{xd}->checkout_delta
- ( $target->for_checkout_delta,
- xdroot => $target->create_xd_root,
- delete_verbose => 1,
- unknown_verbose => $self->{recursive},
- editor => SVK::Editor::Status->new
- ( notify => SVK::Notify->new
- ( cb_flush => sub {
- my ($path, $status) = @_;
- to_native($path, 'path');
- my $copath = $target->copath($path);
- my $report = $target->report->subdir($path);
-
- $target->contains_copath ($copath) or return;
- die loc ("%1 already added.\n", $report)
- if !$self->{recursive} && ($status->[0] eq 'R' || $status->[0] eq 'A');
-
- return unless $status->[0] eq 'D';
- lstat ($copath);
- $self->_do_add ('R', $copath, $report, !-d _)
- if -e _;
- })),
- cb_unknown => sub {
- my ($editor, $path) = @_;
- to_native($path, 'path');
- my $copath = $target->copath($path);
- my $report = $target->report->subdir($path);
- lstat ($copath);
- $self->_do_add ('A', $copath, $report, !-d _);
- },
- );
+ $target->run_delta(
+ SVK::Editor::Status->new(
+ notify => SVK::Notify->new(
+ cb_flush => sub {
+ my ( $path, $status ) = @_;
+ to_native( $path, 'path' );
+ my $copath = $target->copath($path);
+ my $report = $target->report->subdir($path);
+
+ $target->contains_copath($copath) or return;
+ die loc( "%1 already added.\n", $report )
+ if !$self->{recursive}
+ && ( $status->[0] eq 'R' || $status->[0] eq 'A' );
+
+ return unless $status->[0] eq 'D';
+ lstat($copath);
+ $self->_do_add( 'R', $copath, $report, !-d _ )
+ if -e _;
+ }
+ )
+ ),
+ { delete_verbose => 1,
+ unknown_verbose => $self->{recursive},
+ cb_unknown => sub {
+ my ( $editor, $path ) = @_;
+ to_native( $path, 'path' );
+ my $copath = $target->copath($path);
+ my $report = $target->report->subdir($path);
+ lstat($copath);
+ $self->_do_add( 'A', $copath, $report, !-d _ );
+ }
+ },
+ );
+
return;
}
Modified: branches/delta-refactor/lib/SVK/Command/Commit.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Command/Commit.pm (original)
+++ branches/delta-refactor/lib/SVK/Command/Commit.pm Sat Jul 28 11:29:05 2007
@@ -376,21 +376,20 @@
$status_editor = SVK::Editor::Status->new(notify => $notify);
}
- $self->{xd}->checkout_delta
- ( $target->for_checkout_delta,
- depth => $self->{recursive} ? undef : 0,
- $self->exclude_mirror ($target),
- xdroot => $target->create_xd_root,
- nodelay => 1,
- delete_verbose => 1,
- absent_ignore => 1,
- editor => $status_editor,
- cb_conflict => sub { shift->conflict(@_) },
- cb_unknown => sub {
- my ($self, $path) = @_;
- push @unversioned, "? $path\n";
- },
- );
+ $target->run_delta(
+ $status_editor,
+ { depth => $self->{recursive} ? undef: 0,
+ $self->exclude_mirror($target),
+ nodelay => 1,
+ delete_verbose => 1,
+ absent_ignore => 1,
+ cb_conflict => sub { shift->conflict(@_) },
+ cb_unknown => sub {
+ my ($self, $path) = @_;
+ push @unversioned, "? $path\n";
+ },
+ }
+ );
my $conflicts = grep {$_->[0] eq 'C'} @$targets;
@@ -635,24 +634,30 @@
sub run_delta {
my ($self, $target, $xdroot, $editor, %cb) = @_;
- $self->{xd}->checkout_delta
- ( $target->for_checkout_delta,
- depth => $self->{recursive} ? undef : 0,
- debug => $logger->is_debug(),
- xdroot => $xdroot,
- editor => $editor,
- send_delta => !$cb{send_fulltext},
- nodelay => $cb{send_fulltext},
- $self->exclude_mirror ($target),
- cb_exclude => sub { $logger->error(loc ("%1 is a mirrored path, please commit separately.",
- abs2rel ($_[1], $target->copath => $target->report))) },
- $self->{import} ?
- ( auto_add => 1,
- obstruct_as_replace => 1,
- absent_as_delete => 1) :
- ( absent_ignore => 1),
- cb_copyfrom => $cb{cb_copyfrom}
- );
+ $target->run_delta(
+ $editor,
+ { depth => $self->{recursive} ? undef: 0,
+ debug => $logger->is_debug(),
+ send_delta => !$cb{send_fulltext},
+ nodelay => $cb{send_fulltext},
+ $self->exclude_mirror($target),
+ cb_exclude => sub {
+ $logger->error(
+ loc("%1 is a mirrored path, please commit separately.",
+ abs2rel( $_[1], $target->copath => $target->report )
+ )
+ );
+ },
+ $self->{import}
+ ? ( auto_add => 1,
+ obstruct_as_replace => 1,
+ absent_as_delete => 1
+ )
+ : ( absent_ignore => 1 ),
+ cb_copyfrom => $cb{cb_copyfrom},
+ use_old_delta => ($self->{check_only} || $self->{import}),
+ }
+ );
delete $self->{save_message};
return;
}
Modified: branches/delta-refactor/lib/SVK/Command/Copy.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Command/Copy.pm (original)
+++ branches/delta-refactor/lib/SVK/Command/Copy.pm Sat Jul 28 11:29:05 2007
@@ -173,13 +173,15 @@
my ($self, $target) = @_;
my (@modified, @unknown);
$target = $self->{xd}->target_condensed($target); # anchor
- $self->{xd}->checkout_delta
- ( $target->for_checkout_delta,
- xdroot => $target->create_xd_root,
- editor => SVK::Editor::Status->new
- ( notify => SVK::Notify->new
- ( cb_flush => sub { push @modified, $_[0] })),
- cb_unknown => sub { push @unknown, $_[1] } );
+ $target->run_delta(
+ SVK::Editor::Status->new(
+ notify =>
+ SVK::Notify->new( cb_flush => sub { push @modified, $_[0] } )
+ ),
+ { cb_unknown => sub { push @unknown, $_[1] }
+ }
+ );
+
if (@modified || @unknown) {
my @reports = sort map { loc ("%1 is modified.\n", $target->report_copath ($_)) } @modified;
Modified: branches/delta-refactor/lib/SVK/Command/Import.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Command/Import.pm (original)
+++ branches/delta-refactor/lib/SVK/Command/Import.pm Sat Jul 28 11:29:05 2007
@@ -167,9 +167,15 @@
my ($editor, %cb) = $self->get_editor ($basetarget, $committed);
$self->{import} = 1;
- $self->run_delta (SVK::Path::Checkout->real_new
- ({ source => $basetarget,
- copath_anchor => $copath }), $root, $editor, %cb);
+ $self->run_delta(
+ SVK::Path::Checkout->real_new(
+ { source => $basetarget,
+ xd => $self->{xd},
+ copath_anchor => $copath
+ }
+ ),
+ $root, $editor, %cb
+ );
if ($self->{check_only}) {
print loc("Directory %1 will be imported to depotpath %2.\n",
Modified: branches/delta-refactor/lib/SVK/Command/Revert.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Command/Revert.pm (original)
+++ branches/delta-refactor/lib/SVK/Command/Revert.pm Sat Jul 28 11:29:05 2007
@@ -79,43 +79,60 @@
my ($self, $target) = @_;
my $xdroot = $target->create_xd_root;
- $self->{xd}->checkout_delta
- ( $target->for_checkout_delta,
- xdroot => $xdroot,
- depth => $self->{recursive} ? undef : 0,
- delete_verbose => 1,
- absent_verbose => 1,
- nodelay => 1,
- cb_conflict => sub { shift->conflict(@_) },
- cb_unknown => sub { shift->unknown(@_) },
- editor => SVK::Editor::Status->new
- ( notify => SVK::Notify->new
- ( cb_flush => sub {
- my ($path, $status) = @_;
- my $dpath = length $path ? $target->path_anchor."/$path" : $target->path_anchor;
- to_native($path);
- my $st = $status->[0];
- my $copath = $target->copath ($path);
-
- if ($st =~ /[DMRC!]/) {
- # conflicted items do not necessarily exist
- return $self->do_unschedule ($target, $copath)
- if ($st eq 'C' || $status->[2]) && !$xdroot->check_path ($dpath);
- return $self->do_revert($target, $copath, $dpath, $xdroot);
- } elsif ($st eq '?') {
- return unless $target->contains_copath ($copath);
- $logger->warn(loc("%1 is not versioned; ignored.",
- $target->report_copath ($copath)));
- return;
- }
-
- # Check that we are not reverting parents
- $target->contains_copath($copath) or return;
-
- $self->do_unschedule($target, $copath);
- },
- ),
- ));
+ $target->run_delta(
+ SVK::Editor::Status->new(
+ notify => SVK::Notify->new(
+ cb_flush => sub {
+ my ( $path, $status ) = @_;
+ my $dpath = length $path
+ ? $target->path_anchor . "/$path"
+ : $target->path_anchor;
+ to_native($path);
+ my $st = $status->[0];
+ my $copath = $target->copath($path);
+
+ if ( $st =~ /[DMRC!]/ ) {
+
+ # conflicted items do not necessarily exist
+ return $self->do_unschedule( $target, $copath )
+ if ( $st eq 'C' || $status->[2] )
+ && !$xdroot->check_path($dpath);
+ return $self->do_revert( $target, $copath, $dpath,
+ $xdroot );
+ } elsif ( $st eq '?' ) {
+ return unless $target->contains_copath($copath);
+ $logger->warn(
+ loc("%1 is not versioned; ignored.",
+ $target->report_copath($copath)
+ )
+ );
+ return;
+ }
+
+ # Check that we are not reverting parents
+ $target->contains_copath($copath) or return;
+
+ $self->do_unschedule( $target, $copath );
+ },
+ ),
+ ),
+ { depth => $self->{recursive} ? undef: 0,
+ delete_verbose => 1,
+ absent_verbose => 1,
+ nodelay => 1,
+ cb_conflict => sub {
+ shift->conflict(@_);
+ },
+ cb_unknown => sub {
+ shift->unknown(@_);
+ },
+ # XXX: we are unscheduling things a bit too early that
+ # interferes with the new delta code. this might be
+ # solved if we don't flush add_directory as soon as we get
+ # the editor calls.
+ use_old_delta => 1,
+ }
+ );
return;
}
Modified: branches/delta-refactor/lib/SVK/Command/Status.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Command/Status.pm (original)
+++ branches/delta-refactor/lib/SVK/Command/Status.pm Sat Jul 28 11:29:05 2007
@@ -121,27 +121,25 @@
undef, 1)
)
);
- $self->{xd}->checkout_delta
- ( $target->for_checkout_delta,
- xdroot => $target->create_xd_root,
- nodelay => 1,
- delete_verbose => 1,
- editor => $editor,
- cb_conflict => sub { shift->conflict(@_) },
- cb_obstruct => sub { shift->obstruct(@_) },
- $self->{verbose} ?
- (cb_unchanged => sub { shift->unchanged(@_) },
- ) :
- (),
- $self->{recursive} ? () : (depth => 1),
- $self->{no_ignore} ?
- (cb_ignored => sub { shift->ignored(@_) },
- ) :
- (),
- $self->{quiet} ?
- () :
- (cb_unknown => sub { shift->unknown(@_) } )
- );
+
+ $target->run_delta($editor,
+ { nodelay => 1,
+ delete_verbose => 1,
+ cb_conflict => sub { shift->conflict(@_) },
+ cb_obstruct => sub { shift->obstruct(@_) },
+ $self->{verbose}
+ ? ( cb_unchanged => sub { shift->unchanged(@_) }, )
+ : (),
+ $self->{recursive} ? () : ( depth => 1 ),
+ $self->{no_ignore}
+ ? ( cb_ignored => sub { shift->ignored(@_) },
+ )
+ : (),
+ $self->{quiet} ? ()
+ : ( cb_unknown => sub { shift->unknown(@_) } )
+ }
+ );
+
return;
}
Added: branches/delta-refactor/lib/SVK/Delta.pm
==============================================================================
--- (empty file)
+++ branches/delta-refactor/lib/SVK/Delta.pm Sat Jul 28 11:29:05 2007
@@ -0,0 +1,480 @@
+# BEGIN BPS TAGGED BLOCK {{{
+# COPYRIGHT:
+#
+# This software is Copyright (c) 2003-2006 Best Practical Solutions, LLC
+# <clkao at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of either:
+#
+# a) Version 2 of the GNU General Public License. You should have
+# received a copy of the GNU General Public License along with this
+# program. If not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 or visit
+# their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+# b) Version 1 of Perl's "Artistic License". You should have received
+# a copy of the Artistic License with this package, in the file
+# named "ARTISTIC". The license is also available at
+# http://opensource.org/licenses/artistic-license.php.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of the
+# GNU General Public License and is only of importance to you if you
+# choose to contribute your changes and enhancements to the community
+# by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with SVK,
+# to Best Practical Solutions, LLC, you confirm that you are the
+# copyright holder for those contributions and you grant Best Practical
+# Solutions, LLC a nonexclusive, worldwide, irrevocable, royalty-free,
+# perpetual, license to use, copy, create derivative works based on
+# those contributions, and sublicense and distribute those contributions
+# and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+package SVK::Delta;
+use strict;
+use warnings;
+
+use SVK::I18N;
+use SVK::Util qw(HAS_SYMLINK is_symlink get_encoder from_native to_native
+ md5_fh splitpath splitdir catdir abs2rel);
+use autouse 'File::Find' => qw(find);
+use SVK::Logger;
+
+use base 'SVK::DeltaOld';
+
+__PACKAGE__->mk_accessors(qw(cb_conflict cb_ignored cb_unchanged cb_resolve_rev));
+
+*_node_type = *SVK::DeltaOld::_node_type;
+
+sub run {
+ my ($self, $t1, $t2) = @_;
+}
+sub checkout_delta2 {
+ my ($self, $target, $editor, $opt) = @_;
+
+ # objective: move behaviour-related info into $self, and pass around context only
+ my $source = $target->source;
+ my $base_root = $target->create_xd_root;
+ my $base_kind = $base_root->check_path($source->path_anchor);
+
+ die "checkout_delta called with non-dir node"
+ unless $base_kind == $SVN::Node::dir;
+
+ my %arg = (
+ base_root_is_xd => 1,
+ encoder => get_encoder,
+ kind => $base_kind,
+ base_kind => $base_kind,
+ cb_resolve_rev => sub { $_[1] },
+ %$opt,
+ );
+
+ $self->cb_conflict(delete $arg{cb_conflict});
+ $self->cb_unchanged(delete $arg{cb_unchanged});
+ $self->cb_ignored(delete $arg{cb_ignored});
+
+ $self->cb_resolve_rev($arg{cb_resolve_rev});
+
+ $arg{cb_copyfrom} ||= $arg{expand_copy} ? sub { (undef, -1) }
+ : sub { my $path = $_[0]; $path =~ s/%/%25/g; ("file://".$source->repospath.$path, $_[1]) };
+
+ $editor = SVK::Editor::Delay->new($editor)
+ unless $arg{nodelay};
+
+ # XXX: translate $repospath to use '/'
+ my ($entry) = $self->xd->get_entry($target->copath, 1);
+ my $rev = $self->cb_resolve_rev->($source->path_anchor, $entry->{revision});
+ local $SIG{INT} = sub {
+ $editor->abort_edit;
+ die loc("Interrupted.\n");
+ };
+
+ my $baton = $editor->open_root($rev);
+ $self->_delta_dir2(
+ $base_root,
+ $target->path_anchor,
+ $target, $editor,
+ { targets => $source->{targets},
+ %arg,
+ baton => $baton,
+ root => 1,
+ base => 1,
+ type => 'directory'
+ }
+ );
+ $editor->close_directory($baton);
+ $editor->close_edit();
+
+}
+
+sub _compat_args {
+ my ($self, $base_root, $base_path, $target, $editor, $ctx) = @_;
+
+ my $source = $target->source;
+
+ return (
+ base_root => $base_root,
+
+ copath => $target->copath,
+ path => $source->path_anchor,
+ base_path => $base_path,
+
+ repos => $source->repos,
+ repospath => $source->repospath,
+ report => $target->report,
+
+ cb_conflict => $self->cb_conflict,
+ cb_unchanged => $self->cb_unchanged,
+ cb_ignored => $self->cb_ignored,
+
+ # compat for now
+ editor => $editor,
+ );
+
+}
+
+sub _delta_file2 {
+ my ($self, $base_root, $base_path, $target, $editor, $ctx) = @_;
+
+ my $source = $target->source;
+ my %arg = (
+ %$ctx,
+ xdroot => $base_root,
+ );
+ my %compatarg = _compat_args(@_);
+
+ $self->SUPER::_delta_file(%arg, %compatarg);
+}
+
+sub _delta_dir2 {
+ my ($self, $base_root, $base_path, $target, $editor, $ctx) = @_;
+
+ my $source = $target->source;
+ my %arg = (
+ %$ctx,
+ xdroot => $base_root,
+ );
+
+ my %compatarg = _compat_args(@_);
+
+ if ($arg{entry} && $arg{exclude} && exists $arg{exclude}{$arg{entry}}) {
+ $arg{cb_exclude}->($target->path_anchor, $target->copath) if $arg{cb_exclude};
+ return;
+ }
+ my $pool = SVN::Pool->new_default (undef);
+ my $cinfo = $arg{cinfo} ||= $self->checkout->get($target->copath);
+ my $schedule = $cinfo->{'.schedule'} || '';
+ $arg{add} = 1 if $arg{auto_add} && $arg{base_kind} == $SVN::Node::none ||
+ $schedule eq 'replace';
+
+ # compute targets for children
+ my $targets;
+ for (@{$arg{targets} || []}) {
+ my ($volume, $directories, $file) = splitpath ($_);
+ if ( my @dirs = splitdir($directories) ) {
+ my $path = $volume . shift(@dirs);
+ $file = catdir(grep length, @dirs, $file);
+ push @{$targets->{$path}}, $file
+ }
+ else {
+ $targets->{$file} = undef;
+ }
+ }
+ my $thisdir;
+ if ($targets) {
+ if (exists $targets->{''}) {
+ delete $targets->{''};
+ $thisdir = 1;
+ }
+ }
+ else {
+ $thisdir = 1;
+ }
+ # don't use depth when we are still traversing through targets
+ my $descend = defined $targets || !(defined $arg{depth} && $arg{depth} == 0);
+ # XXX: the top level entry is undefined, which should be fixed.
+ $self->cb_conflict->($editor, defined $arg{entry} ? $arg{entry} : '', $arg{baton})
+ if $thisdir && $self->cb_conflict && $cinfo->{'.conflict'};
+
+ # XXX: later
+ return 1 if $self->_node_deleted_or_absent(%compatarg, %arg, pool => $pool);
+ # if a node is replaced, it has no base, unless it was replaced with history.
+ $arg{base} = 0 if $schedule eq 'replace' && !$cinfo->{'.copyfrom'};
+ my ($entries, $baton) = ({});
+ if ($arg{add}) {
+ $baton = $arg{root} ? $arg{baton} :
+ $editor->add_directory($arg{entry}, $arg{baton},
+ $cinfo->{'.copyfrom'}
+ ? ($arg{cb_copyfrom}->(@{$cinfo}{qw/.copyfrom .copyfrom_rev/}))
+ : (undef, -1), $pool);
+ }
+
+ $entries = $base_root->dir_entries($base_path)
+ if $arg{base} && $arg{base_kind} == $SVN::Node::dir;
+
+ $baton ||= $arg{root} ? $arg{baton}
+ : $editor->open_directory($arg{entry}, $arg{baton},
+ $self->_delta_rev2($target, $cinfo), $pool);
+
+ # check scheduled addition
+ # XXX: does this work with copied directory?
+ my ($newprops, $fullprops) = $self->_node_props2($base_root, $base_path, $target, $editor, \%arg);
+
+ if ($descend) {
+
+ my $signature;
+ if ($self->{signature} && $arg{base_root_is_xd}) {
+ $signature = $self->{signature}->load ($arg{copath});
+ # if we are not iterating over all entries, keep the old signatures
+ $signature->{keepold} = 1 if defined $targets
+ }
+
+ # XXX: Merge this with @direntries so we have single entry to descendents
+ for my $entry (sort keys %$entries) {
+ my $newtarget;
+ if (defined $targets) {
+ next unless exists $targets->{$entry};
+ $newtarget = delete $targets->{$entry};
+ }
+ my $kind = $entries->{$entry}->kind;
+ my $unchanged = ($kind == $SVN::Node::file && $signature && !$signature->changed ($entry));
+ my $entry_target = $target->clone->descend($entry);
+ my ($ccinfo, $ccschedule) = $self->xd->get_entry($entry_target->copath, 1);
+ # a replace with history node requires handling the copy anchor in the
+ # latter direntries loop. we should really merge the two.
+ if ($ccschedule eq 'replace' && $ccinfo->{'.copyfrom'}) {
+ delete $entries->{$entry};
+ $targets->{$entry} = $newtarget if defined $targets;
+ next;
+ }
+ my $newentry = defined $arg{entry} ? "$arg{entry}/$entry" : $entry;
+ my $newpath = $entry_target->path_anchor;
+ if ($unchanged && !$ccschedule && !$ccinfo->{'.conflict'}) {
+ $self->cb_unchanged->($editor, $newentry, $baton,
+ $self->_delta_rev2($target, $ccinfo)
+ ) if $arg{cb_unchanged};
+ next;
+ }
+ my ($type, $st) = _node_type($entry_target->copath);
+ next unless defined $type;
+ my $delta = $type ? $type eq 'directory' ? '_delta_dir' : '_delta_file'
+ : $kind == $SVN::Node::file ? '_delta_file' : '_delta_dir';
+ my $obs = $type ? ($kind == $SVN::Node::dir xor $type eq 'directory') : 0;
+ # if the sub-delta returns 1 it means the node is modified. invlidate
+ # the signature cache
+ my $ret;
+ $delta .= '2';
+ $ret = $self->$delta($base_root, $base_path eq '/' ? "/$entry" : "$base_path/$entry",
+ $entry_target,
+ $editor, { %arg,
+ add => $arg{in_copy} || ($obs && $arg{obstruct_as_replace}),
+ type => $type,
+ # if copath exist, we have base only if they are of the same type
+ base => !$obs,
+ depth => defined $arg{depth} ? defined $targets ? $arg{depth} : $arg{depth} - 1: undef,
+ entry => $newentry,
+ kind => $arg{base_root_is_xd} ? $kind : $arg{xdroot}->check_path ($newpath),
+ base_kind => $kind,
+ targets => $newtarget,
+ baton => $baton,
+ root => 0,
+ st => $st,
+ cinfo => $ccinfo });
+
+ $ret and ($signature && $signature->invalidate ($entry));
+ }
+
+ if ($signature) {
+ $signature->flush;
+ undef $signature;
+ }
+ my $ignore = $self->xd->ignore($fullprops->{'svn:ignore'});
+
+ my @direntries;
+ # if we are at somewhere arg{copath} not exist, $arg{type} is empty
+ if ($arg{type} && !(defined $targets && !keys %$targets)) {
+ opendir my ($dir), $target->copath or Carp::confess "$target->copath: $!";
+ for (readdir($dir)) {
+ # Completely deny the existance of .svk; we shouldn't
+ # show this even with e.g. --no-ignore.
+ next if $_ eq '.svk' and $self->xd->{floating};
+
+ if (eval {from_native($_, 'path', $arg{encoder}); 1}) {
+ push @direntries, $_;
+ }
+ elsif ($arg{auto_add}) { # fatal for auto_add
+ die "$_: $@";
+ }
+ else {
+ print "$_: $@";
+ }
+ }
+ @direntries = sort grep { !m/^\.+$/ && !exists $entries->{$_} } @direntries;
+ }
+
+ for my $entry (@direntries) {
+ my $newtarget;
+ if (defined $targets) {
+ next unless exists $targets->{$entry};
+ $newtarget = delete $targets->{$entry};
+ }
+ my $entry_target = $target->clone->descend($entry);
+ my %newpaths = ( entry => defined $arg{entry} ? "$arg{entry}/$entry" : $entry,
+ targets => $newtarget, base_kind => $SVN::Node::none);
+ # XXX: what is this != thing in trinary?
+ $newpaths{kind} = $arg{base_root_is_xd} ? $SVN::Node::none :
+ $arg{xdroot}->check_path($target->path_anchor) != $SVN::Node::none;
+ my ($ccinfo, $sche) = $self->xd->get_entry($entry_target->copath, 1);
+ my $add = $sche || $arg{auto_add} || $newpaths{kind};
+ # If we are not at intermediate path, process ignore
+ # for unknowns, as well as the case of auto_add (import)
+ if (!defined $targets) {
+ if ((!$add || $arg{auto_add}) && $entry =~ m/$ignore/) {
+ $self->cb_ignored->($editor, $newpaths{entry}, $arg{baton})
+ if $self->cb_ignored;
+ next;
+ }
+ }
+ if ($ccinfo->{'.conflict'}) {
+ $self->cb_conflict->($editor, $newpaths{entry}, $arg{baton})
+ if $self->cb_conflict;
+ }
+ unless ($add || $ccinfo->{'.conflict'}) {
+ if ($arg{cb_unknown}) {
+ $arg{cb_unknown}->($editor, $newpaths{entry}, $arg{baton});
+ $self->_unknown_verbose(%arg, %newpaths,
+ copath => $entry_target->copath,
+ path => $target->path_anchor,
+ base_path => $base_path eq '/' ? "/$entry" : "$base_path/$entry")
+ if $arg{unknown_verbose};
+ }
+ next;
+ }
+ my ($type, $st) = _node_type($entry_target->copath) or next;
+ my $delta = $type eq 'directory' ? '_delta_dir': '_delta_file';
+ my $copyfrom = $ccinfo->{'.copyfrom'};
+ my ($fromroot) = $copyfrom ? $arg{xdroot}->get_revision_root($target->path_anchor, $ccinfo->{'.copyfrom_rev'}) : undef;
+ # XXX: actually we want to rerun the delta with base being the copy root,
+ # figure out why it needs to be in xdroot to work (see mirror/sync-crazy-replace.t)
+ $delta .= '2';
+ if ($copyfrom) {
+ $self->$delta($fromroot, $copyfrom, $entry_target, $editor,
+ { %arg, %newpaths,
+ add => 1,
+ baton => $baton,
+ root => 0, base => 0, cinfo => $ccinfo,
+ type => $type,
+ st => $st,
+ depth => defined $arg{depth} ? defined $targets ? $arg{depth} : $arg{depth} - 1: undef,
+ base => 1,
+ _really_in_copy => 1,
+ in_copy => $arg{expand_copy},
+ base_kind => $fromroot->check_path ($copyfrom),
+ base_root_is_xd => 0 });
+ }
+ else {
+ $self->$delta($base_root, $base_path eq '/' ? "/$entry" : "$base_path/$entry", $entry_target, $editor,
+ { %arg, %newpaths,
+ add => 1,
+ baton => $baton,
+ root => 0, base => 0, cinfo => $ccinfo,
+ type => $type,
+ st => $st,
+ depth => defined $arg{depth} ? defined $targets ? $arg{depth} : $arg{depth} - 1: undef });
+ }
+ }
+ }
+
+ if ($thisdir) {
+ $editor->change_dir_prop ($baton, $_, ref ($newprops->{$_}) ? undef : $newprops->{$_}, $pool)
+ for sort keys %$newprops;
+ }
+ if (defined $targets) {
+ $logger->warn(loc ("Unknown target: %1.", $_)) for sort keys %$targets;
+ }
+
+ $editor->close_directory ($baton, $pool)
+ unless $arg{root};
+ return 0;
+}
+
+
+sub _delta_rev2 {
+ my ($self, $target, $cinfo) = @_;
+ my $schedule = $cinfo->{'.schedule'} || '';
+ # XXX: uncomment this as mutation coverage test
+ # return $entry->{revision};
+
+ # Lookup the copy source rev for the case of open_directory inside
+ # add_directotry with history. But shouldn't do so for replaced
+ # items, because the rev here is used for delete_entry
+ my ($source_path, $source_rev) = $schedule ne 'replace' ?
+ $self->xd->_copy_source($cinfo, $target->copath) : ();
+ ($source_path, $source_rev) = ($target->path_anchor, $cinfo->{revision})
+ unless defined $source_path;
+ return $source_rev;
+
+}
+
+sub _node_props2 {
+ my ( $self, $base_root, $base_path, $target, $editor, $ctx ) = @_;
+
+ my $newprops;
+ my $kind = $target->root->check_path($target->path_anchor); # XXX: in ctx already, or not calling this at all
+ return ({}, {}) if $kind == $SVN::Node::unknown;
+ my $fullprop
+ = $kind ? $target->root->node_proplist( $target->path_anchor ) : {};
+ if ( !$ctx->{base} or $ctx->{in_copy} ) {
+ $newprops = $fullprop;
+ }
+ elsif ( !$ctx->{base_root_is_xd} && $ctx->{base} ) {
+ $newprops = $self->can('_prop_delta')
+ ->( $base_root->node_proplist($base_path), $fullprop )
+ if $ctx->{kind}
+ && $ctx->{base_kind}
+ && $self->can('_prop_changed')->(
+ $base_root, $base_path, $ctx->{ xdroot },
+ $target->path_anchor
+ );
+ }
+ else { # XXX: this logic should not be here
+ my $schedule = $ctx->{cinfo}{'.schedule'} || '';
+ $newprops = (!$schedule && $ctx->{auto_add} && $ctx->{kind} == $SVN::Node::none && $ctx->{type} eq 'file')
+ ? $self->xd->auto_prop($target->copath) : $ctx->{cinfo}{'.newprop'};
+ }
+ return ( $newprops, $fullprop );
+}
+
+
+if ($ENV{_REFACTORING}) {
+
+for my $method (qw/_delta_rev _delta_content _unknown_verbose _node_deleted _node_deleted_or_absent _prop_delta _prop_changed _node_props _delta_file _delta_dir _get_rev checkout_delta/) {
+ no strict 'refs';
+ *$method = sub {
+ warn "===> old $method is being called from delta run";
+ my $func = SVK::DeltaOld->can($method);
+ goto $func;
+ }
+}
+
+}
+1;
+
Added: branches/delta-refactor/lib/SVK/DeltaOld.pm
==============================================================================
--- (empty file)
+++ branches/delta-refactor/lib/SVK/DeltaOld.pm Sat Jul 28 11:29:05 2007
@@ -0,0 +1,672 @@
+# BEGIN BPS TAGGED BLOCK {{{
+# COPYRIGHT:
+#
+# This software is Copyright (c) 2003-2006 Best Practical Solutions, LLC
+# <clkao at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of either:
+#
+# a) Version 2 of the GNU General Public License. You should have
+# received a copy of the GNU General Public License along with this
+# program. If not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 or visit
+# their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+# b) Version 1 of Perl's "Artistic License". You should have received
+# a copy of the Artistic License with this package, in the file
+# named "ARTISTIC". The license is also available at
+# http://opensource.org/licenses/artistic-license.php.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of the
+# GNU General Public License and is only of importance to you if you
+# choose to contribute your changes and enhancements to the community
+# by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with SVK,
+# to Best Practical Solutions, LLC, you confirm that you are the
+# copyright holder for those contributions and you grant Best Practical
+# Solutions, LLC a nonexclusive, worldwide, irrevocable, royalty-free,
+# perpetual, license to use, copy, create derivative works based on
+# those contributions, and sublicense and distribute those contributions
+# and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+package SVK::DeltaOld;
+use strict;
+use warnings;
+
+use base 'Class::Accessor::Fast';
+use SVK::I18N;
+use SVK::Util qw(HAS_SYMLINK is_symlink get_encoder from_native to_native
+ md5_fh splitpath splitdir catdir abs2rel);
+use autouse 'File::Find' => qw(find);
+use SVK::Logger;
+
+__PACKAGE__->mk_accessors(qw(xd checkout));
+
+# XXX: checkout_delta is getting too complicated and too many options
+
+# Here be dragon. below is checkout_delta related function.
+
+sub _delta_rev {
+ my ($self, $arg) = @_;
+ my $entry = $arg->{cinfo};
+ my $schedule = $entry->{'.schedule'} || '';
+ # XXX: uncomment this as mutation coverage test
+ # return $entry->{revision};
+
+ # Lookup the copy source rev for the case of open_directory inside
+ # add_directotry with history. But shouldn't do so for replaced
+ # items, because the rev here is used for delete_entry
+ my ($source_path, $source_rev) = $schedule ne 'replace' ?
+ $self->xd->_copy_source($entry, $arg->{copath}) : ();
+ ($source_path, $source_rev) = ($arg->{path}, $entry->{revision})
+ unless defined $source_path;
+ return $source_rev;
+
+}
+
+sub _delta_content {
+ my ($self, %arg) = @_;
+
+ my $handle = $arg{editor}->apply_textdelta ($arg{baton}, $arg{md5}, $arg{pool});
+ return unless $handle && $#{$handle} > 0;
+
+ if ($arg{send_delta} && $arg{base}) {
+ my $spool = SVN::Pool->new_default ($arg{pool});
+ my $source = $arg{base_root}->file_contents ($arg{base_path}, $spool);
+ my $txstream = SVN::TxDelta::new
+ ($source, $arg{fh}, $spool);
+ SVN::TxDelta::send_txstream ($txstream, @$handle, $spool);
+ }
+ else {
+ SVN::TxDelta::send_stream ($arg{fh}, @$handle, SVN::Pool->new ($arg{pool}))
+ }
+}
+
+sub _unknown_verbose {
+ my ($self, %arg) = @_;
+ my $ignore = $self->xd->ignore;
+ # The caller should have processed the entry already.
+ my %seen = ($arg{copath} => 1);
+ my @new_targets;
+ if ($arg{targets}) {
+ENTRY: for my $entry (@{$arg{targets}}) {
+ my $now = '';
+ for my $dir (splitdir ($entry)) {
+ $now .= $now ? "/$dir" : $dir;
+ my $copath = SVK::Path::Checkout->copath ($arg{copath}, $now);
+ next if $seen{$copath};
+ $seen{$copath} = 1;
+ lstat $copath;
+ unless (-e _) {
+ $logger->warn( loc ("Unknown target: %1.", $copath));
+ next ENTRY;
+ }
+ unless (-r _) {
+ $logger->warn( loc ("Warning: %1 is unreadable.", $copath));
+ next ENTRY;
+ }
+ $arg{cb_unknown}->($arg{editor}, catdir($arg{entry}, $now), $arg{baton});
+ }
+ push @new_targets, SVK::Path::Checkout->copath ($arg{copath}, $entry);
+ }
+
+ return unless @new_targets;
+ }
+ my $nentry = $arg{entry};
+ to_native($nentry, 'path', $arg{encoder});
+ find ({ preprocess => sub { sort @_ },
+ wanted =>
+ sub {
+ $File::Find::prune = 1, return if m/$ignore/;
+ my $copath = catdir($File::Find::dir, $_);
+ return if $seen{$copath};
+ my $schedule = $self->{checkout}->get ($copath)->{'.schedule'} || '';
+ return if $schedule eq 'delete';
+ my $dpath = abs2rel($copath, $arg{copath} => $nentry, '/');
+ from_native($dpath, 'path');
+ $arg{cb_unknown}->($arg{editor}, $dpath, $arg{baton});
+ }}, defined $arg{targets} ? @new_targets : $arg{copath});
+}
+
+sub _node_deleted {
+ my ($self, %arg) = @_;
+ $arg{rev} = $self->_delta_rev(\%arg);
+ $arg{editor}->delete_entry (@arg{qw/entry rev baton pool/});
+
+ if ($arg{kind} == $SVN::Node::dir && $arg{delete_verbose}) {
+ foreach my $file (sort $self->{checkout}->find
+ ($arg{copath}, {'.schedule' => 'delete'})) {
+ next if $file eq $arg{copath};
+ $file = abs2rel($file, $arg{copath} => undef, '/');
+ from_native($file, 'path', $arg{encoder});
+ $arg{editor}->delete_entry ("$arg{entry}/$file", @arg{qw/rev baton pool/});
+ }
+ }
+}
+
+sub _node_deleted_or_absent {
+ my ($self, %arg) = @_;
+ my $schedule = $arg{cinfo}{'.schedule'} || '';
+
+ if ($schedule eq 'delete' || $schedule eq 'replace') {
+ my $should_do_delete = !$arg{_really_in_copy} || $arg{copath} eq ($arg{cinfo}{scheduleanchor} || '');
+ $self->_node_deleted (%arg)
+ if $should_do_delete;
+ # when doing add over deleted entry, descend into it
+ if ($schedule eq 'delete') {
+ $self->_unknown_verbose (%arg)
+ if $arg{cb_unknown} && $arg{unknown_verbose};
+ return $should_do_delete;
+ }
+ }
+
+ if ($arg{type}) {
+ if ($arg{kind} && !$schedule &&
+ (($arg{type} eq 'file') xor ($arg{kind} == $SVN::Node::file))) {
+ if ($arg{obstruct_as_replace}) {
+ $self->_node_deleted (%arg);
+ }
+ else {
+ $arg{cb_obstruct}->($arg{editor}, $arg{entry}, $arg{baton})
+ if $arg{cb_obstruct};
+ return 1;
+ }
+ }
+ }
+ else {
+ # deleted during base_root -> xdroot
+ if (!$arg{base_root_is_xd} && $arg{kind} == $SVN::Node::none) {
+ $self->_node_deleted (%arg);
+ return 1;
+ }
+ return 1 if $arg{absent_ignore};
+ # absent
+ my $type = $arg{kind} == $SVN::Node::dir ? 'directory' : 'file';
+
+ if ($arg{absent_as_delete}) {
+ $arg{rev} = $self->_delta_rev(\%arg);
+ $self->_node_deleted (%arg);
+ }
+ else {
+ my $func = "absent_$type";
+ $arg{editor}->$func (@arg{qw/entry baton pool/});
+ }
+ return 1 unless $type ne 'file' && $arg{absent_verbose};
+ }
+ return 0;
+}
+
+sub _prop_delta {
+ my ($baseprop, $newprop) = @_;
+ return $newprop unless $baseprop && keys %$baseprop;
+ return { map {$_ => undef} keys %$baseprop } unless $newprop && keys %$newprop;
+ my $changed;
+ for my $propname (keys %{ { %$baseprop, %$newprop } }) {
+ # deref propvalue
+ my @value = map { $_ ? ref ($_) ? '' : $_ : '' }
+ map {$_->{$propname}} ($baseprop, $newprop);
+ $changed->{$propname} = $newprop->{$propname}
+ unless $value[0] eq $value[1];
+ }
+ return $changed;
+}
+
+sub _prop_changed {
+ my ($root1, $path1, $root2, $path2) = @_;
+ ($root1, $root2) = map {$_->isa ('SVK::Root') ? $_->root : $_} ($root1, $root2);
+ return SVN::Fs::props_changed ($root1, $path1, $root2, $path2);
+}
+
+sub _node_props {
+ my ($self, %arg) = @_;
+ my $schedule = $arg{cinfo}{'.schedule'} || '';
+ my $props = $arg{kind} ? $schedule eq 'replace' ? {} : $arg{xdroot}->node_proplist ($arg{path}) :
+ $arg{base_kind} ? $arg{base_root}->node_proplist ($arg{base_path}) : {};
+ my $newprops = (!$schedule && $arg{auto_add} && $arg{kind} == $SVN::Node::none && $arg{type} eq 'file')
+ ? $self->xd->auto_prop ($arg{copath}) : $arg{cinfo}{'.newprop'};
+ my $fullprop = SVK::XD::_combine_prop ($props, $newprops);
+ if (!$arg{base} or $arg{in_copy}) {
+ $newprops = $fullprop;
+ }
+ elsif (!$arg{base_root_is_xd} && $arg{base}) {
+ $newprops = _prop_delta ($arg{base_root}->node_proplist ($arg{base_path}), $fullprop)
+ if $arg{kind} && $arg{base_kind} && _prop_changed (@arg{qw/base_root base_path xdroot path/});
+ }
+ return ($newprops, $fullprop)
+}
+
+sub _node_type {
+ my $copath = shift;
+ my $st = [lstat ($copath)];
+ return '' if !-e _;
+ unless (-r _) {
+ $logger->warn( loc ("Warning: $copath is unreadable."));
+ return;
+ }
+ return ('file', $st) if -f _ or is_symlink;
+ return ('directory', $st) if -d _;
+ $logger->warn( loc ("Warning: unsupported node type $copath."));
+ return ('', $st);
+}
+
+use Fcntl ':mode';
+
+sub _delta_file {
+ my ($self, %arg) = @_;
+ my $pool = SVN::Pool->new_default (undef);
+ my $cinfo = $arg{cinfo} ||= $self->{checkout}->get ($arg{copath});
+ my $schedule = $cinfo->{'.schedule'} || '';
+ my $modified;
+
+ if ($arg{cb_conflict} && $cinfo->{'.conflict'}) {
+ ++$modified;
+ $arg{cb_conflict}->($arg{editor}, $arg{entry}, $arg{baton});
+ }
+
+ return 1 if $self->_node_deleted_or_absent (%arg, pool => $pool);
+
+ my ($newprops, $fullprops) = $self->_node_props (%arg);
+ if (HAS_SYMLINK && (defined $fullprops->{'svn:special'} xor S_ISLNK($arg{st}[2]))) {
+ # special case obstructure for links, since it's not standard
+ return 1 if $self->_node_deleted_or_absent (%arg,
+ type => 'link',
+ pool => $pool);
+ if ($arg{obstruct_as_replace}) {
+ $schedule = 'replace';
+ $fullprops = $newprops = $self->xd->auto_prop($arg{copath}) || {};
+ }
+ else {
+ return 1;
+ }
+ }
+ $arg{add} = 1 if $arg{auto_add} && $arg{base_kind} == $SVN::Node::none ||
+ $schedule eq 'replace';
+
+ my $fh = SVK::XD::get_fh ($arg{xdroot}, '<', $arg{path}, $arg{copath}, $fullprops);
+ my $mymd5 = md5_fh ($fh);
+ my ($baton, $md5);
+
+ $arg{base} = 0 if $arg{in_copy} || $schedule eq 'replace';
+
+ unless ($schedule || $arg{add} ||
+ ($arg{base} && $mymd5 ne ($md5 = $arg{base_root}->file_md5_checksum ($arg{base_path})))) {
+ $arg{cb_unchanged}->($arg{editor}, $arg{entry}, $arg{baton},
+ $self->_delta_rev(\%arg)
+ ) if ($arg{cb_unchanged} && !$modified);
+ return $modified;
+ }
+
+ $baton = $arg{editor}->add_file ($arg{entry}, $arg{baton},
+ $cinfo->{'.copyfrom'} ?
+ ($arg{cb_copyfrom}->(@{$cinfo}{qw/.copyfrom .copyfrom_rev/}))
+ : (undef, -1), $pool)
+ if $arg{add};
+
+ $baton ||= $arg{editor}->open_file ($arg{entry}, $arg{baton}, $self->_delta_rev(\%arg), $pool)
+ if keys %$newprops;
+
+ $arg{editor}->change_file_prop ($baton, $_, ref ($newprops->{$_}) ? undef : $newprops->{$_}, $pool)
+ for sort keys %$newprops;
+
+ if (!$arg{base} ||
+ $mymd5 ne ($md5 ||= $arg{base_root}->file_md5_checksum ($arg{base_path}))) {
+ seek $fh, 0, 0;
+ $baton ||= $arg{editor}->open_file ($arg{entry}, $arg{baton}, $self->_delta_rev(\%arg), $pool);
+ $self->_delta_content (%arg, baton => $baton, pool => $pool,
+ fh => $fh, md5 => $arg{base} ? $md5 : undef);
+ }
+
+ $arg{editor}->close_file ($baton, $mymd5, $pool) if $baton;
+ return 1;
+}
+
+sub _delta_dir {
+ my ($self, %arg) = @_;
+ if ($arg{entry} && $arg{exclude} && exists $arg{exclude}{$arg{entry}}) {
+ $arg{cb_exclude}->($arg{path}, $arg{copath}) if $arg{cb_exclude};
+ return;
+ }
+ my $pool = SVN::Pool->new_default (undef);
+ my $cinfo = $arg{cinfo} ||= $self->{checkout}->get ($arg{copath});
+ my $schedule = $cinfo->{'.schedule'} || '';
+ $arg{add} = 1 if $arg{auto_add} && $arg{base_kind} == $SVN::Node::none ||
+ $schedule eq 'replace';
+
+ # compute targets for children
+ my $targets;
+ for (@{$arg{targets} || []}) {
+ my ($volume, $directories, $file) = splitpath ($_);
+ if ( my @dirs = splitdir($directories) ) {
+ my $path = $volume . shift(@dirs);
+ $file = catdir(grep length, @dirs, $file);
+ push @{$targets->{$path}}, $file
+ }
+ else {
+ $targets->{$file} = undef;
+ }
+ }
+ my $thisdir;
+ if ($targets) {
+ if (exists $targets->{''}) {
+ delete $targets->{''};
+ $thisdir = 1;
+ }
+ }
+ else {
+ $thisdir = 1;
+ }
+ # don't use depth when we are still traversing through targets
+ my $descend = defined $targets || !(defined $arg{depth} && $arg{depth} == 0);
+ # XXX: the top level entry is undefined, which should be fixed.
+ $arg{cb_conflict}->($arg{editor}, defined $arg{entry} ? $arg{entry} : '', $arg{baton})
+ if $thisdir && $arg{cb_conflict} && $cinfo->{'.conflict'};
+
+ return 1 if $self->_node_deleted_or_absent (%arg, pool => $pool);
+ # if a node is replaced, it has no base, unless it was replaced with history.
+ $arg{base} = 0 if $schedule eq 'replace' && !$cinfo->{'.copyfrom'};
+ my ($entries, $baton) = ({});
+ if ($arg{add}) {
+ $baton = $arg{root} ? $arg{baton} :
+ $arg{editor}->add_directory ($arg{entry}, $arg{baton},
+ $cinfo->{'.copyfrom'} ?
+ ($arg{cb_copyfrom}->(@{$cinfo}{qw/.copyfrom .copyfrom_rev/}))
+ : (undef, -1), $pool);
+ }
+
+ $entries = $arg{base_root}->dir_entries ($arg{base_path})
+ if $arg{base} && $arg{base_kind} == $SVN::Node::dir;
+
+ $baton ||= $arg{root} ? $arg{baton}
+ : $arg{editor}->open_directory ($arg{entry}, $arg{baton},
+ $self->_delta_rev(\%arg), $pool);
+
+ # check scheduled addition
+ # XXX: does this work with copied directory?
+ my ($newprops, $fullprops) = $self->_node_props (%arg);
+
+ if ($descend) {
+
+ my $signature;
+ if ($self->{signature} && $arg{base_root_is_xd}) {
+ $signature = $self->{signature}->load ($arg{copath});
+ # if we are not iterating over all entries, keep the old signatures
+ $signature->{keepold} = 1 if defined $targets
+ }
+
+ # XXX: Merge this with @direntries so we have single entry to descendents
+ for my $entry (sort keys %$entries) {
+ my $newtarget;
+ my $copath = $entry;
+ if (defined $targets) {
+ next unless exists $targets->{$copath};
+ $newtarget = delete $targets->{$copath};
+ }
+ to_native ($copath, 'path', $arg{encoder});
+ my $kind = $entries->{$entry}->kind;
+ my $unchanged = ($kind == $SVN::Node::file && $signature && !$signature->changed ($entry));
+ $copath = SVK::Path::Checkout->copath ($arg{copath}, $copath);
+ my ($ccinfo, $ccschedule) = $self->xd->get_entry($copath, 1);
+ # a replace with history node requires handling the copy anchor in the
+ # latter direntries loop. we should really merge the two.
+ if ($ccschedule eq 'replace' && $ccinfo->{'.copyfrom'}) {
+ delete $entries->{$entry};
+ $targets->{$entry} = $newtarget if defined $targets;
+ next;
+ }
+ my $newentry = defined $arg{entry} ? "$arg{entry}/$entry" : $entry;
+ my $newpath = $arg{path} eq '/' ? "/$entry" : "$arg{path}/$entry";
+ if ($unchanged && !$ccschedule && !$ccinfo->{'.conflict'}) {
+ $arg{cb_unchanged}->($arg{editor}, $newentry, $baton,
+ $self->_delta_rev({ %arg,
+ cinfo => $ccinfo,
+ path => $newpath,
+ copath => $copath })
+ ) if $arg{cb_unchanged};
+ next;
+ }
+ my ($type, $st) = _node_type ($copath);
+ next unless defined $type;
+ my $delta = $type ? $type eq 'directory' ? \&_delta_dir : \&_delta_file
+ : $kind == $SVN::Node::file ? \&_delta_file : \&_delta_dir;
+ my $obs = $type ? ($kind == $SVN::Node::dir xor $type eq 'directory') : 0;
+ # if the sub-delta returns 1 it means the node is modified. invlidate
+ # the signature cache
+ $self->$delta ( %arg,
+ add => $arg{in_copy} || ($obs && $arg{obstruct_as_replace}),
+ type => $type,
+ # if copath exist, we have base only if they are of the same type
+ base => !$obs,
+ depth => defined $arg{depth} ? defined $targets ? $arg{depth} : $arg{depth} - 1: undef,
+ entry => $newentry,
+ kind => $arg{base_root_is_xd} ? $kind : $arg{xdroot}->check_path ($newpath),
+ base_kind => $kind,
+ targets => $newtarget,
+ baton => $baton,
+ root => 0,
+ st => $st,
+ cinfo => $ccinfo,
+ base_path => $arg{base_path} eq '/' ? "/$entry" : "$arg{base_path}/$entry",
+ path => $newpath,
+ copath => $copath)
+ and ($signature && $signature->invalidate ($entry));
+ }
+
+ if ($signature) {
+ $signature->flush;
+ undef $signature;
+ }
+ my $ignore = $self->xd->ignore($fullprops->{'svn:ignore'});
+
+ my @direntries;
+ # if we are at somewhere arg{copath} not exist, $arg{type} is empty
+ if ($arg{type} && !(defined $targets && !keys %$targets)) {
+ opendir my ($dir), $arg{copath} or Carp::confess "$arg{copath}: $!";
+ for (readdir($dir)) {
+ # Completely deny the existance of .svk; we shouldn't
+ # show this even with e.g. --no-ignore.
+ next if $_ eq '.svk' and $self->xd->{floating};
+
+ if (eval {from_native($_, 'path', $arg{encoder}); 1}) {
+ push @direntries, $_;
+ }
+ elsif ($arg{auto_add}) { # fatal for auto_add
+ die "$_: $@";
+ }
+ else {
+ print "$_: $@";
+ }
+ }
+ @direntries = sort grep { !m/^\.+$/ && !exists $entries->{$_} } @direntries;
+ }
+
+ for my $copath (@direntries) {
+ my $entry = $copath;
+ my $newtarget;
+ if (defined $targets) {
+ next unless exists $targets->{$copath};
+ $newtarget = delete $targets->{$copath};
+ }
+ to_native ($copath, 'path', $arg{encoder});
+ my %newpaths = ( copath => SVK::Path::Checkout->copath ($arg{copath}, $copath),
+ entry => defined $arg{entry} ? "$arg{entry}/$entry" : $entry,
+ path => $arg{path} eq '/' ? "/$entry" : "$arg{path}/$entry",
+ base_path => $arg{base_path} eq '/' ? "/$entry" : "$arg{base_path}/$entry",
+ targets => $newtarget, base_kind => $SVN::Node::none);
+ $newpaths{kind} = $arg{base_root_is_xd} ? $SVN::Node::none :
+ $arg{xdroot}->check_path ($newpaths{path}) != $SVN::Node::none;
+ my ($ccinfo, $sche) = $self->xd->get_entry($newpaths{copath}, 1);
+ my $add = $sche || $arg{auto_add} || $newpaths{kind};
+ # If we are not at intermediate path, process ignore
+ # for unknowns, as well as the case of auto_add (import)
+ if (!defined $targets) {
+ if ((!$add || $arg{auto_add}) && $entry =~ m/$ignore/) {
+ $arg{cb_ignored}->($arg{editor}, $newpaths{entry}, $arg{baton})
+ if $arg{cb_ignored};
+ next;
+ }
+ }
+ if ($ccinfo->{'.conflict'}) {
+ $arg{cb_conflict}->($arg{editor}, $newpaths{entry}, $arg{baton})
+ if $arg{cb_conflict};
+ }
+ unless ($add || $ccinfo->{'.conflict'}) {
+ if ($arg{cb_unknown}) {
+ $arg{cb_unknown}->($arg{editor}, $newpaths{entry}, $arg{baton});
+ $self->_unknown_verbose (%arg, %newpaths)
+ if $arg{unknown_verbose};
+ }
+ next;
+ }
+ my ($type, $st) = _node_type ($newpaths{copath}) or next;
+ my $delta = $type eq 'directory' ? \&_delta_dir : \&_delta_file;
+ my $copyfrom = $ccinfo->{'.copyfrom'};
+ my ($fromroot) = $copyfrom ? $arg{xdroot}->get_revision_root($newpaths{path}, $ccinfo->{'.copyfrom_rev'}) : undef;
+ $self->$delta ( %arg, %newpaths, add => 1, baton => $baton,
+ root => 0, base => 0, cinfo => $ccinfo,
+ type => $type,
+ st => $st,
+ depth => defined $arg{depth} ? defined $targets ? $arg{depth} : $arg{depth} - 1: undef,
+ $copyfrom ?
+ ( base => 1,
+ _really_in_copy => 1,
+ in_copy => $arg{expand_copy},
+ base_kind => $fromroot->check_path ($copyfrom),
+ base_root_is_xd => 0,
+ base_root => $fromroot,
+ base_path => $copyfrom) : (),
+ );
+ }
+
+ }
+
+ if ($thisdir) {
+ $arg{editor}->change_dir_prop ($baton, $_, ref ($newprops->{$_}) ? undef : $newprops->{$_}, $pool)
+ for sort keys %$newprops;
+ }
+ if (defined $targets) {
+ $logger->warn(loc ("Unknown target: %1.", $_)) for sort keys %$targets;
+ }
+
+ $arg{editor}->close_directory ($baton, $pool)
+ unless $arg{root};
+ return 0;
+}
+
+sub _get_rev {
+ $_[0]->{checkout}->get ($_[1])->{revision};
+}
+
+sub checkout_delta {
+ my ($xd, %arg) = @_;
+ my $self = __PACKAGE__->new(\%arg);
+ $self->xd($xd);
+ $self->checkout($xd->{checkout});
+ $arg{base_root} ||= $arg{xdroot}; # xdroot is the
+ $arg{base_path} ||= $arg{path}; # path is -> string name of file in repo
+ $arg{encoder} = get_encoder;
+ Carp::cluck unless defined $arg{base_path};
+ my $kind = $arg{base_kind} = $arg{base_root}->check_path ($arg{base_path});
+ $arg{base_root_is_xd} = $arg{base_root}->same_root($arg{xdroot});
+ $arg{kind} = $arg{base_root_is_xd} ? $kind : $arg{xdroot}->check_path ($arg{path});
+ die "checkout_delta called with non-dir node"
+ unless $kind == $SVN::Node::dir;
+ my ($copath, $repospath) = @arg{qw/copath repospath/};
+ $arg{editor}{_debug}++
+ if $arg{debug};
+ $arg{editor} = SVK::Editor::Delay->new ($arg{editor})
+ unless $arg{nodelay};
+
+ # XXX: translate $repospath to use '/'
+ $arg{cb_copyfrom} ||= $arg{expand_copy} ? sub { (undef, -1) }
+ : sub { my $path = $_[0]; $path =~ s/%/%25/g; ("file://$repospath$path", $_[1]) };
+ local $SIG{INT} = sub {
+ $arg{editor}->abort_edit;
+ die loc("Interrupted.\n");
+ };
+
+ my ($entry) = $self->get_entry($arg{copath}, 1);
+ my $baton = $arg{editor}->open_root ($entry->{revision});
+ $self->_delta_dir (%arg, baton => $baton, root => 1, base => 1, type => 'directory');
+ $arg{editor}->close_directory ($baton);
+ $arg{editor}->close_edit ();
+}
+
+sub get_entry {
+ my ($self, $copath, $dont_clone) = @_;
+ my $entry = $self->checkout->get($copath, $dont_clone);
+ return ($entry, $entry->{'.schedule'} || '');
+}
+
+sub checkout_delta1 {
+ my ($self, $target, $editor, $opt) = @_;
+
+ my $source = $target->source;
+ my $base_root = $target->create_xd_root;
+ my $base_kind = $base_root->check_path($source->path_anchor);
+
+ die "checkout_delta called with non-dir node"
+ unless $base_kind == $SVN::Node::dir;
+
+ my %arg = (
+ base_root => $base_root,
+ xdroot => $base_root,
+ base_root_is_xd => 1,
+ encoder => get_encoder,
+
+ copath => $target->copath,
+ path => $source->path_anchor,
+ base_path => $source->path_anchor,
+
+ targets => $source->{targets},
+ repos => $source->repos,
+ repospath => $source->repospath,
+ report => $target->report,
+
+ kind => $base_kind,
+ base_kind => $base_kind,
+ cb_resolve_rev => sub { $_[1] },
+ %$opt,
+ );
+
+ $arg{cb_copyfrom} ||= $arg{expand_copy} ? sub { (undef, -1) }
+ : sub { my $path = $_[0]; $path =~ s/%/%25/g; ("file://".$source->repospath.$path, $_[1]) };
+
+ $editor = SVK::Editor::Delay->new($editor)
+ unless $arg{nodelay};
+
+ # XXX: translate $repospath to use '/'
+ my ($entry) = $self->xd->get_entry($target->copath, 1);
+ my $rev = $arg{cb_resolve_rev}->($source->path_anchor, $entry->{revision});
+ local $SIG{INT} = sub {
+ $editor->abort_edit;
+ die loc("Interrupted.\n");
+ };
+
+ my $baton = $editor->open_root($rev);
+ $arg{editor} = $editor;
+ $self->_delta_dir(%arg, baton => $baton, root => 1, base => 1, type => 'directory');
+ $editor->close_directory($baton);
+ $editor->close_edit();
+
+}
+
+1;
Modified: branches/delta-refactor/lib/SVK/Path/Checkout.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Path/Checkout.pm (original)
+++ branches/delta-refactor/lib/SVK/Path/Checkout.pm Sat Jul 28 11:29:05 2007
@@ -379,6 +379,21 @@
return $self->source;
}
+=head1 run_delta( $editor, $opt )
+
+Run delta between base of checkout and the checkout through $editor.
+
+=cut
+
+sub run_delta {
+ my ( $self, $editor, $opt ) = @_;
+ require SVK::Delta;
+ my $delta = SVK::Delta->new( { xd => $self->xd, checkout => $self->xd->{checkout} } );
+ $opt->{use_old_delta}
+ ? $delta->checkout_delta1( $self, $editor, $opt )
+ : $delta->checkout_delta2( $self, $editor, $opt );
+}
+
=head1 SEE ALSO
L<SVK::Path>
Modified: branches/delta-refactor/lib/SVK/Util.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/Util.pm (original)
+++ branches/delta-refactor/lib/SVK/Util.pm Sat Jul 28 11:29:05 2007
@@ -73,6 +73,8 @@
str2time time2str reformat_svn_date
find_dotsvk
+
+ compile_apr_fnmatch
);
use SVK::Version; our $VERSION = $SVK::VERSION;
@@ -1005,6 +1007,59 @@
return substr ($path, 0, length ($parent)+1) eq "$parent/";
}
+# Emulates APR's apr_fnmatch function with flags=0, which is what
+# Subversion uses. Converts a string in fnmatch format to a Perl regexp.
+# Code is based on Barrie Slaymaker's Regexp::Shellish.
+sub compile_apr_fnmatch {
+ my $re = shift;
+
+ $re =~ s@
+ ( \\.
+ | \[ # character class
+ [!^]? # maybe negation (^ and ! are both supported)
+ (?: (?:\\.|[^\\\]]) # one item
+ (?: - # possibly followed by a dash and another
+ (?:\\.|[^\\\]]))? # item
+ )* # 0 or more entries (zero case will be checked specially below)
+ (\]?) # if this ] doesn't match, that means we fell off end of string!
+ | .
+ )
+ @
+ if ( $1 eq '?' ) {
+ '.' ;
+ } elsif ( $1 eq '*' ) {
+ '.*' ;
+ } elsif ( substr($1, 0, 1) eq '[') {
+ if ($1 eq '[]') { # should never match
+ '[^\s\S]';
+ } elsif ($1 eq '[!]' or $1 eq '[^]') { # 0-length match
+ '';
+ } else {
+ my $temp = $1;
+ my $failed = $2 eq '';
+ if ($failed) {
+ '[^\s\S]';
+ } else {
+ $temp =~ s/(\\.|.)/$1 eq '-' ? '-' : quotemeta(substr($1, -1))/ges;
+ # the previous step puts in backslashes at beginning and end; remove them
+ $temp =~ s/^\\\[/[/;
+ $temp =~ s/\\\]$/]/;
+ # if it started with [^ or [!, it now starts with [\^ or [\!; fix.
+ $temp =~ s/^\[ # literal [
+ \\ # literal backslash
+ [!^] # literal ! or ^
+ /[^/x;
+ $temp;
+ }
+ }
+ } else {
+ quotemeta(substr( $1, -1 ) ); # ie, either quote it, or if it's \x, quote x
+ }
+ @gexs ;
+
+ return qr/\A$re\Z/s;
+}
+
1;
__END__
Modified: branches/delta-refactor/lib/SVK/XD.pm
==============================================================================
--- branches/delta-refactor/lib/SVK/XD.pm (original)
+++ branches/delta-refactor/lib/SVK/XD.pm Sat Jul 28 11:29:05 2007
@@ -58,7 +58,7 @@
use SVK::I18N;
use SVK::Util qw( get_anchor abs_path abs_path_noexist abs2rel splitdir catdir splitpath $SEP
HAS_SYMLINK is_symlink is_executable mimetype mimetype_is_text
- md5_fh get_prompt traverse_history make_path dirname
+ md5_fh get_prompt traverse_history make_path dirname compile_apr_fnmatch
from_native to_native get_encoder get_depot_anchor );
use Data::Hierarchy 0.30;
use autouse 'File::Find' => qw(find);
@@ -702,33 +702,33 @@
$target->anchorify unless $target->source->{targets};
# check for if the file/dir is modified.
- $self->checkout_delta ( $target->for_checkout_delta,
- %arg,
- xdroot => $target->create_xd_root,
- absent_as_delete => 1,
- delete_verbose => 1,
- absent_verbose => 1,
- editor => SVK::Editor::Status->new
- ( notify => SVK::Notify->new
- ( cb_flush => sub {
- my ($path, $status) = @_;
- my $copath = $target->copath($path);
- $target->contains_copath($copath) or return;
-
- my $st = $status->[0];
- if ($st eq 'M') {
- push @modified, $copath;
- }
- elsif ($st eq 'D') {
- push @deleted, $copath;
- }
- else {
- push @scheduled, $copath;
- }
- })),
- cb_unknown => sub {
- push @unknown, $target->copath($_[1]);
- }
+ $target->run_delta(
+ SVK::Editor::Status->new(
+ notify => SVK::Notify->new(
+ cb_flush => sub {
+ my ( $path, $status ) = @_;
+ my $copath = $target->copath($path);
+ $target->contains_copath($copath) or return;
+
+ my $st = $status->[0];
+ if ( $st eq 'M' ) {
+ push @modified, $copath;
+ } elsif ( $st eq 'D' ) {
+ push @deleted, $copath;
+ } else {
+ push @scheduled, $copath;
+ }
+ }
+ )
+ ),
+ { %arg, # current callers args: quiet, no_rm, force_delete
+ absent_as_delete => 1,
+ delete_verbose => 1,
+ absent_verbose => 1,
+ cb_unknown => sub {
+ push @unknown, $target->copath( $_[1] );
+ }
+ }
);
# use Data::Dumper; warn Dumper \@unknown, \@modified, \@scheduled;
@@ -909,7 +909,6 @@
=cut
-# XXX: checkout_delta is getting too complicated and too many options
my %ignore_cache;
sub ignore {
@@ -919,7 +918,7 @@
no warnings;
my $ignore = SVK::Config->svnconfig ?
SVK::Config->svnconfig->{config}->
- get ('miscellany', 'global-ignores', '') : '';
+ get('miscellany', 'global-ignores', '') : '';
my @ignore = split / /,
($ignore || "*.o *.lo *.la #*# .*.rej *.rej .*~ *~ .#* .DS_Store");
push @ignore, 'svk-commit*.tmp';
@@ -933,600 +932,9 @@
return join('|', map {$ignore_cache{$_} ||= compile_apr_fnmatch($_)} (@ignore));
}
-# Emulates APR's apr_fnmatch function with flags=0, which is what
-# Subversion uses. Converts a string in fnmatch format to a Perl regexp.
-# Code is based on Barrie Slaymaker's Regexp::Shellish.
-sub compile_apr_fnmatch {
- my $re = shift;
-
- $re =~ s@
- ( \\.
- | \[ # character class
- [!^]? # maybe negation (^ and ! are both supported)
- (?: (?:\\.|[^\\\]]) # one item
- (?: - # possibly followed by a dash and another
- (?:\\.|[^\\\]]))? # item
- )* # 0 or more entries (zero case will be checked specially below)
- (\]?) # if this ] doesn't match, that means we fell off end of string!
- | .
- )
- @
- if ( $1 eq '?' ) {
- '.' ;
- } elsif ( $1 eq '*' ) {
- '.*' ;
- } elsif ( substr($1, 0, 1) eq '[') {
- if ($1 eq '[]') { # should never match
- '[^\s\S]';
- } elsif ($1 eq '[!]' or $1 eq '[^]') { # 0-length match
- '';
- } else {
- my $temp = $1;
- my $failed = $2 eq '';
- if ($failed) {
- '[^\s\S]';
- } else {
- $temp =~ s/(\\.|.)/$1 eq '-' ? '-' : quotemeta(substr($1, -1))/ges;
- # the previous step puts in backslashes at beginning and end; remove them
- $temp =~ s/^\\\[/[/;
- $temp =~ s/\\\]$/]/;
- # if it started with [^ or [!, it now starts with [\^ or [\!; fix.
- $temp =~ s/^\[ # literal [
- \\ # literal backslash
- [!^] # literal ! or ^
- /[^/x;
- $temp;
- }
- }
- } else {
- quotemeta(substr( $1, -1 ) ); # ie, either quote it, or if it's \x, quote x
- }
- @gexs ;
-
- return qr/\A$re\Z/s;
-}
-
-# Here be dragon. below is checkout_delta related function.
-
-sub _delta_rev {
- my ($self, $arg) = @_;
- my $entry = $arg->{cinfo};
- my $schedule = $entry->{'.schedule'} || '';
- # XXX: uncomment this as mutation coverage test
- # return $entry->{revision};
-
- # Lookup the copy source rev for the case of open_directory inside
- # add_directotry with history. But shouldn't do so for replaced
- # items, because the rev here is used for delete_entry
- my ($source_path, $source_rev) = $schedule ne 'replace' ?
- $self->_copy_source($entry, $arg->{copath}) : ();
- ($source_path, $source_rev) = ($arg->{path}, $entry->{revision})
- unless defined $source_path;
- return $source_rev;
-}
-
-sub _delta_content {
- my ($self, %arg) = @_;
-
- my $handle = $arg{editor}->apply_textdelta ($arg{baton}, $arg{md5}, $arg{pool});
- return unless $handle && $#{$handle} > 0;
-
- if ($arg{send_delta} && $arg{base}) {
- my $spool = SVN::Pool->new_default ($arg{pool});
- my $source = $arg{base_root}->file_contents ($arg{base_path}, $spool);
- my $txstream = SVN::TxDelta::new
- ($source, $arg{fh}, $spool);
- SVN::TxDelta::send_txstream ($txstream, @$handle, $spool);
- }
- else {
- SVN::TxDelta::send_stream ($arg{fh}, @$handle, SVN::Pool->new ($arg{pool}))
- }
-}
-
-sub _unknown_verbose {
- my ($self, %arg) = @_;
- my $ignore = $self->ignore;
- # The caller should have processed the entry already.
- my %seen = ($arg{copath} => 1);
- my @new_targets;
- if ($arg{targets}) {
-ENTRY: for my $entry (@{$arg{targets}}) {
- my $now = '';
- for my $dir (splitdir ($entry)) {
- $now .= $now ? "/$dir" : $dir;
- my $copath = SVK::Path::Checkout->copath ($arg{copath}, $now);
- next if $seen{$copath};
- $seen{$copath} = 1;
- lstat $copath;
- unless (-e _) {
- $logger->warn( loc ("Unknown target: %1.", $copath));
- next ENTRY;
- }
- unless (-r _) {
- $logger->warn( loc ("Warning: %1 is unreadable.", $copath));
- next ENTRY;
- }
- $arg{cb_unknown}->($arg{editor}, catdir($arg{entry}, $now), $arg{baton});
- }
- push @new_targets, SVK::Path::Checkout->copath ($arg{copath}, $entry);
- }
-
- return unless @new_targets;
- }
- my $nentry = $arg{entry};
- to_native($nentry, 'path', $arg{encoder});
- find ({ preprocess => sub { sort @_ },
- wanted =>
- sub {
- $File::Find::prune = 1, return if m/$ignore/;
- my $copath = catdir($File::Find::dir, $_);
- return if $seen{$copath};
- my $schedule = $self->{checkout}->get ($copath)->{'.schedule'} || '';
- return if $schedule eq 'delete';
- my $dpath = abs2rel($copath, $arg{copath} => $nentry, '/');
- from_native($dpath, 'path');
- $arg{cb_unknown}->($arg{editor}, $dpath, $arg{baton});
- }}, defined $arg{targets} ? @new_targets : $arg{copath});
-}
-
-sub _node_deleted {
- my ($self, %arg) = @_;
- $arg{rev} = $self->_delta_rev(\%arg);
- $arg{editor}->delete_entry (@arg{qw/entry rev baton pool/});
-
- if ($arg{kind} == $SVN::Node::dir && $arg{delete_verbose}) {
- foreach my $file (sort $self->{checkout}->find
- ($arg{copath}, {'.schedule' => 'delete'})) {
- next if $file eq $arg{copath};
- $file = abs2rel($file, $arg{copath} => undef, '/');
- from_native($file, 'path', $arg{encoder});
- $arg{editor}->delete_entry ("$arg{entry}/$file", @arg{qw/rev baton pool/});
- }
- }
-}
-
-sub _node_deleted_or_absent {
- my ($self, %arg) = @_;
- my $schedule = $arg{cinfo}{'.schedule'} || '';
-
- if ($schedule eq 'delete' || $schedule eq 'replace') {
- my $should_do_delete = !$arg{_really_in_copy} || $arg{copath} eq ($arg{cinfo}{scheduleanchor} || '');
- $self->_node_deleted (%arg)
- if $should_do_delete;
- # when doing add over deleted entry, descend into it
- if ($schedule eq 'delete') {
- $self->_unknown_verbose (%arg)
- if $arg{cb_unknown} && $arg{unknown_verbose};
- return $should_do_delete;
- }
- }
-
- if ($arg{type}) {
- if ($arg{kind} && !$schedule &&
- (($arg{type} eq 'file') xor ($arg{kind} == $SVN::Node::file))) {
- if ($arg{obstruct_as_replace}) {
- $self->_node_deleted (%arg);
- }
- else {
- $arg{cb_obstruct}->($arg{editor}, $arg{entry}, $arg{baton})
- if $arg{cb_obstruct};
- return 1;
- }
- }
- }
- else {
- # deleted during base_root -> xdroot
- if (!$arg{base_root_is_xd} && $arg{kind} == $SVN::Node::none) {
- $self->_node_deleted (%arg);
- return 1;
- }
- return 1 if $arg{absent_ignore};
- # absent
- my $type = $arg{kind} == $SVN::Node::dir ? 'directory' : 'file';
-
- if ($arg{absent_as_delete}) {
- $arg{rev} = $self->_delta_rev(\%arg);
- $self->_node_deleted (%arg);
- }
- else {
- my $func = "absent_$type";
- $arg{editor}->$func (@arg{qw/entry baton pool/});
- }
- return 1 unless $type ne 'file' && $arg{absent_verbose};
- }
- return 0;
-}
-
-sub _prop_delta {
- my ($baseprop, $newprop) = @_;
- return $newprop unless $baseprop && keys %$baseprop;
- return { map {$_ => undef} keys %$baseprop } unless $newprop && keys %$newprop;
- my $changed;
- for my $propname (keys %{ { %$baseprop, %$newprop } }) {
- # deref propvalue
- my @value = map { $_ ? ref ($_) ? '' : $_ : '' }
- map {$_->{$propname}} ($baseprop, $newprop);
- $changed->{$propname} = $newprop->{$propname}
- unless $value[0] eq $value[1];
- }
- return $changed;
-}
-
-sub _prop_changed {
- my ($root1, $path1, $root2, $path2) = @_;
- ($root1, $root2) = map {$_->isa ('SVK::Root') ? $_->root : $_} ($root1, $root2);
- return SVN::Fs::props_changed ($root1, $path1, $root2, $path2);
-}
-
-sub _node_props {
- my ($self, %arg) = @_;
- my $schedule = $arg{cinfo}{'.schedule'} || '';
- my $props = $arg{kind} ? $schedule eq 'replace' ? {} : $arg{xdroot}->node_proplist ($arg{path}) :
- $arg{base_kind} ? $arg{base_root}->node_proplist ($arg{base_path}) : {};
- my $newprops = (!$schedule && $arg{auto_add} && $arg{kind} == $SVN::Node::none && $arg{type} eq 'file')
- ? $self->auto_prop ($arg{copath}) : $arg{cinfo}{'.newprop'};
- my $fullprop = _combine_prop ($props, $newprops);
- if (!$arg{base} or $arg{in_copy}) {
- $newprops = $fullprop;
- }
- elsif (!$arg{base_root_is_xd} && $arg{base}) {
- $newprops = _prop_delta ($arg{base_root}->node_proplist ($arg{base_path}), $fullprop)
- if $arg{kind} && $arg{base_kind} && _prop_changed (@arg{qw/base_root base_path xdroot path/});
- }
- return ($newprops, $fullprop)
-}
-
-sub _node_type {
- my $copath = shift;
- my $st = [lstat ($copath)];
- return '' if !-e _;
- unless (-r _) {
- $logger->warn( loc ("Warning: $copath is unreadable."));
- return;
- }
- return ('file', $st) if -f _ or is_symlink;
- return ('directory', $st) if -d _;
- $logger->warn( loc ("Warning: unsupported node type $copath."));
- return ('', $st);
-}
-
-use Fcntl ':mode';
-
-sub _delta_file {
- my ($self, %arg) = @_;
- my $pool = SVN::Pool->new_default (undef);
- my $cinfo = $arg{cinfo} ||= $self->{checkout}->get ($arg{copath});
- my $schedule = $cinfo->{'.schedule'} || '';
- my $modified;
-
- if ($arg{cb_conflict} && $cinfo->{'.conflict'}) {
- ++$modified;
- $arg{cb_conflict}->($arg{editor}, $arg{entry}, $arg{baton});
- }
-
- return 1 if $self->_node_deleted_or_absent (%arg, pool => $pool);
-
- my ($newprops, $fullprops) = $self->_node_props (%arg);
- if (HAS_SYMLINK && (defined $fullprops->{'svn:special'} xor S_ISLNK($arg{st}[2]))) {
- # special case obstructure for links, since it's not standard
- return 1 if $self->_node_deleted_or_absent (%arg,
- type => 'link',
- pool => $pool);
- if ($arg{obstruct_as_replace}) {
- $schedule = 'replace';
- $fullprops = $newprops = $self->auto_prop($arg{copath}) || {};
- }
- else {
- return 1;
- }
- }
- $arg{add} = 1 if $arg{auto_add} && $arg{base_kind} == $SVN::Node::none ||
- $schedule eq 'replace';
-
- my $fh = get_fh ($arg{xdroot}, '<', $arg{path}, $arg{copath}, $fullprops);
- my $mymd5 = md5_fh ($fh);
- my ($baton, $md5);
-
- $arg{base} = 0 if $arg{in_copy} || $schedule eq 'replace';
-
- unless ($schedule || $arg{add} ||
- ($arg{base} && $mymd5 ne ($md5 = $arg{base_root}->file_md5_checksum ($arg{base_path})))) {
- $arg{cb_unchanged}->($arg{editor}, $arg{entry}, $arg{baton},
- $self->_delta_rev(\%arg)
- ) if ($arg{cb_unchanged} && !$modified);
- return $modified;
- }
-
- $baton = $arg{editor}->add_file ($arg{entry}, $arg{baton},
- $cinfo->{'.copyfrom'} ?
- ($arg{cb_copyfrom}->(@{$cinfo}{qw/.copyfrom .copyfrom_rev/}))
- : (undef, -1), $pool)
- if $arg{add};
-
- $baton ||= $arg{editor}->open_file ($arg{entry}, $arg{baton}, $self->_delta_rev(\%arg), $pool)
- if keys %$newprops;
-
- $arg{editor}->change_file_prop ($baton, $_, ref ($newprops->{$_}) ? undef : $newprops->{$_}, $pool)
- for sort keys %$newprops;
-
- if (!$arg{base} ||
- $mymd5 ne ($md5 ||= $arg{base_root}->file_md5_checksum ($arg{base_path}))) {
- seek $fh, 0, 0;
- $baton ||= $arg{editor}->open_file ($arg{entry}, $arg{baton}, $self->_delta_rev(\%arg), $pool);
- $self->_delta_content (%arg, baton => $baton, pool => $pool,
- fh => $fh, md5 => $arg{base} ? $md5 : undef);
- }
-
- $arg{editor}->close_file ($baton, $mymd5, $pool) if $baton;
- return 1;
-}
-
-sub _delta_dir {
- my ($self, %arg) = @_;
- if ($arg{entry} && $arg{exclude} && exists $arg{exclude}{$arg{entry}}) {
- $arg{cb_exclude}->($arg{path}, $arg{copath}) if $arg{cb_exclude};
- return;
- }
- my $pool = SVN::Pool->new_default (undef);
- my $cinfo = $arg{cinfo} ||= $self->{checkout}->get ($arg{copath});
- my $schedule = $cinfo->{'.schedule'} || '';
- $arg{add} = 1 if $arg{auto_add} && $arg{base_kind} == $SVN::Node::none ||
- $schedule eq 'replace';
-
- # compute targets for children
- my $targets;
- for (@{$arg{targets} || []}) {
- my ($volume, $directories, $file) = splitpath ($_);
- if ( my @dirs = splitdir($directories) ) {
- my $path = $volume . shift(@dirs);
- $file = catdir(grep length, @dirs, $file);
- push @{$targets->{$path}}, $file
- }
- else {
- $targets->{$file} = undef;
- }
- }
- my $thisdir;
- if ($targets) {
- if (exists $targets->{''}) {
- delete $targets->{''};
- $thisdir = 1;
- }
- }
- else {
- $thisdir = 1;
- }
- # don't use depth when we are still traversing through targets
- my $descend = defined $targets || !(defined $arg{depth} && $arg{depth} == 0);
- # XXX: the top level entry is undefined, which should be fixed.
- $arg{cb_conflict}->($arg{editor}, defined $arg{entry} ? $arg{entry} : '', $arg{baton})
- if $thisdir && $arg{cb_conflict} && $cinfo->{'.conflict'};
-
- return 1 if $self->_node_deleted_or_absent (%arg, pool => $pool);
- # if a node is replaced, it has no base, unless it was replaced with history.
- $arg{base} = 0 if $schedule eq 'replace' && !$cinfo->{'.copyfrom'};
- my ($entries, $baton) = ({});
- if ($arg{add}) {
- $baton = $arg{root} ? $arg{baton} :
- $arg{editor}->add_directory ($arg{entry}, $arg{baton},
- $cinfo->{'.copyfrom'} ?
- ($arg{cb_copyfrom}->(@{$cinfo}{qw/.copyfrom .copyfrom_rev/}))
- : (undef, -1), $pool);
- }
-
- $entries = $arg{base_root}->dir_entries ($arg{base_path})
- if $arg{base} && $arg{base_kind} == $SVN::Node::dir;
-
- $baton ||= $arg{root} ? $arg{baton}
- : $arg{editor}->open_directory ($arg{entry}, $arg{baton},
- $self->_delta_rev(\%arg), $pool);
-
- # check scheduled addition
- # XXX: does this work with copied directory?
- my ($newprops, $fullprops) = $self->_node_props (%arg);
-
- if ($descend) {
-
- my $signature;
- if ($self->{signature} && $arg{base_root_is_xd}) {
- $signature = $self->{signature}->load ($arg{copath});
- # if we are not iterating over all entries, keep the old signatures
- $signature->{keepold} = 1 if defined $targets
- }
-
- # XXX: Merge this with @direntries so we have single entry to descendents
- for my $entry (sort keys %$entries) {
- my $newtarget;
- my $copath = $entry;
- if (defined $targets) {
- next unless exists $targets->{$copath};
- $newtarget = delete $targets->{$copath};
- }
- to_native ($copath, 'path', $arg{encoder});
- my $kind = $entries->{$entry}->kind;
- my $unchanged = ($kind == $SVN::Node::file && $signature && !$signature->changed ($entry));
- $copath = SVK::Path::Checkout->copath ($arg{copath}, $copath);
- my ($ccinfo, $ccschedule) = $self->get_entry($copath, 1);
- # a replace with history node requires handling the copy anchor in the
- # latter direntries loop. we should really merge the two.
- if ($ccschedule eq 'replace' && $ccinfo->{'.copyfrom'}) {
- delete $entries->{$entry};
- $targets->{$entry} = $newtarget if defined $targets;
- next;
- }
- my $newentry = defined $arg{entry} ? "$arg{entry}/$entry" : $entry;
- my $newpath = $arg{path} eq '/' ? "/$entry" : "$arg{path}/$entry";
- if ($unchanged && !$ccschedule && !$ccinfo->{'.conflict'}) {
- $arg{cb_unchanged}->($arg{editor}, $newentry, $baton,
- $self->_delta_rev({ %arg,
- cinfo => $ccinfo,
- path => $newpath,
- copath => $copath })
- ) if $arg{cb_unchanged};
- next;
- }
- my ($type, $st) = _node_type ($copath);
- next unless defined $type;
- my $delta = $type ? $type eq 'directory' ? \&_delta_dir : \&_delta_file
- : $kind == $SVN::Node::file ? \&_delta_file : \&_delta_dir;
- my $obs = $type ? ($kind == $SVN::Node::dir xor $type eq 'directory') : 0;
- # if the sub-delta returns 1 it means the node is modified. invlidate
- # the signature cache
- $self->$delta ( %arg,
- add => $arg{in_copy} || ($obs && $arg{obstruct_as_replace}),
- type => $type,
- # if copath exist, we have base only if they are of the same type
- base => !$obs,
- depth => defined $arg{depth} ? defined $targets ? $arg{depth} : $arg{depth} - 1: undef,
- entry => $newentry,
- kind => $arg{base_root_is_xd} ? $kind : $arg{xdroot}->check_path ($newpath),
- base_kind => $kind,
- targets => $newtarget,
- baton => $baton,
- root => 0,
- st => $st,
- cinfo => $ccinfo,
- base_path => $arg{base_path} eq '/' ? "/$entry" : "$arg{base_path}/$entry",
- path => $newpath,
- copath => $copath)
- and ($signature && $signature->invalidate ($entry));
- }
-
- if ($signature) {
- $signature->flush;
- undef $signature;
- }
- my $ignore = $self->ignore ($fullprops->{'svn:ignore'});
-
- my @direntries;
- # if we are at somewhere arg{copath} not exist, $arg{type} is empty
- if ($arg{type} && !(defined $targets && !keys %$targets)) {
- opendir my ($dir), $arg{copath} or Carp::confess "$arg{copath}: $!";
- for (readdir($dir)) {
- # Completely deny the existance of .svk; we shouldn't
- # show this even with e.g. --no-ignore.
- next if $_ eq '.svk' and $self->{floating};
-
- if (eval {from_native($_, 'path', $arg{encoder}); 1}) {
- push @direntries, $_;
- }
- elsif ($arg{auto_add}) { # fatal for auto_add
- die "$_: $@";
- }
- else {
- print "$_: $@";
- }
- }
- @direntries = sort grep { !m/^\.+$/ && !exists $entries->{$_} } @direntries;
- }
-
- for my $copath (@direntries) {
- my $entry = $copath;
- my $newtarget;
- if (defined $targets) {
- next unless exists $targets->{$copath};
- $newtarget = delete $targets->{$copath};
- }
- to_native ($copath, 'path', $arg{encoder});
- my %newpaths = ( copath => SVK::Path::Checkout->copath ($arg{copath}, $copath),
- entry => defined $arg{entry} ? "$arg{entry}/$entry" : $entry,
- path => $arg{path} eq '/' ? "/$entry" : "$arg{path}/$entry",
- base_path => $arg{base_path} eq '/' ? "/$entry" : "$arg{base_path}/$entry",
- targets => $newtarget, base_kind => $SVN::Node::none);
- $newpaths{kind} = $arg{base_root_is_xd} ? $SVN::Node::none :
- $arg{xdroot}->check_path ($newpaths{path}) != $SVN::Node::none;
- my ($ccinfo, $sche) = $self->get_entry($newpaths{copath}, 1);
- my $add = $sche || $arg{auto_add} || $newpaths{kind};
- # If we are not at intermediate path, process ignore
- # for unknowns, as well as the case of auto_add (import)
- if (!defined $targets) {
- if ((!$add || $arg{auto_add}) && $entry =~ m/$ignore/) {
- $arg{cb_ignored}->($arg{editor}, $newpaths{entry}, $arg{baton})
- if $arg{cb_ignored};
- next;
- }
- }
- if ($ccinfo->{'.conflict'}) {
- $arg{cb_conflict}->($arg{editor}, $newpaths{entry}, $arg{baton})
- if $arg{cb_conflict};
- }
- unless ($add || $ccinfo->{'.conflict'}) {
- if ($arg{cb_unknown}) {
- $arg{cb_unknown}->($arg{editor}, $newpaths{entry}, $arg{baton});
- $self->_unknown_verbose (%arg, %newpaths)
- if $arg{unknown_verbose};
- }
- next;
- }
- my ($type, $st) = _node_type ($newpaths{copath}) or next;
- my $delta = $type eq 'directory' ? \&_delta_dir : \&_delta_file;
- my $copyfrom = $ccinfo->{'.copyfrom'};
- my ($fromroot) = $copyfrom ? $arg{xdroot}->get_revision_root($newpaths{path}, $ccinfo->{'.copyfrom_rev'}) : undef;
- $self->$delta ( %arg, %newpaths, add => 1, baton => $baton,
- root => 0, base => 0, cinfo => $ccinfo,
- type => $type,
- st => $st,
- depth => defined $arg{depth} ? defined $targets ? $arg{depth} : $arg{depth} - 1: undef,
- $copyfrom ?
- ( base => 1,
- _really_in_copy => 1,
- in_copy => $arg{expand_copy},
- base_kind => $fromroot->check_path ($copyfrom),
- base_root_is_xd => 0,
- base_root => $fromroot,
- base_path => $copyfrom) : (),
- );
- }
-
- }
-
- if ($thisdir) {
- $arg{editor}->change_dir_prop ($baton, $_, ref ($newprops->{$_}) ? undef : $newprops->{$_}, $pool)
- for sort keys %$newprops;
- }
- if (defined $targets) {
- $logger->warn(loc ("Unknown target: %1.", $_)) for sort keys %$targets;
- }
-
- $arg{editor}->close_directory ($baton, $pool)
- unless $arg{root};
- return 0;
-}
-
-sub _get_rev {
- $_[0]->{checkout}->get ($_[1])->{revision};
-}
-
+require SVK::DeltaOld;
sub checkout_delta {
- my ($self, %arg) = @_;
- $arg{base_root} ||= $arg{xdroot}; # xdroot is the
- $arg{base_path} ||= $arg{path}; # path is -> string name of file in repo
- $arg{encoder} = get_encoder;
- Carp::cluck unless defined $arg{base_path};
- my $kind = $arg{base_kind} = $arg{base_root}->check_path ($arg{base_path});
- $arg{base_root_is_xd} = $arg{base_root}->same_root($arg{xdroot});
- $arg{kind} = $arg{base_root_is_xd} ? $kind : $arg{xdroot}->check_path ($arg{path});
- die "checkout_delta called with non-dir node"
- unless $kind == $SVN::Node::dir;
- my ($copath, $repospath) = @arg{qw/copath repospath/};
- $arg{editor}{_debug}++
- if $arg{debug};
- $arg{editor} = SVK::Editor::Delay->new ($arg{editor})
- unless $arg{nodelay};
-
- # XXX: translate $repospath to use '/'
- $arg{cb_copyfrom} ||= $arg{expand_copy} ? sub { (undef, -1) }
- : sub { my $path = $_[0]; $path =~ s/%/%25/g; ("file://$repospath$path", $_[1]) };
- local $SIG{INT} = sub {
- $arg{editor}->abort_edit;
- die loc("Interrupted.\n");
- };
-
- my ($entry) = $self->get_entry($arg{copath}, 1);
- my $baton = $arg{editor}->open_root ($entry->{revision});
- $self->_delta_dir (%arg, baton => $baton, root => 1, base => 1, type => 'directory');
- $arg{editor}->close_directory ($baton);
- $arg{editor}->close_edit ();
+ goto \&SVK::DeltaOld::checkout_delta;
}
=item get_entry($copath)
@@ -1754,7 +1162,7 @@
}
elsif ($schedule ne 'add' && $schedule ne 'replace') {
Carp::cluck 'hate' unless defined $path;
- $props = $root->node_proplist ($path);
+ $props = $root->node_proplist($path);
}
return _combine_prop ($props, $entry->{'.newprop'});
}
More information about the svk-commit
mailing list