[Bps-public-commit] r11068 - in SVN-PropDB: . bin lib/Prophet
jesse at bestpractical.com
jesse at bestpractical.com
Thu Mar 13 17:00:48 EDT 2008
Author: jesse
Date: Thu Mar 13 17:00:47 2008
New Revision: 11068
Added:
SVN-PropDB/lib/Prophet/Conflict.pm
Modified:
SVN-PropDB/ (props changed)
SVN-PropDB/bin/merger
SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm
Log:
r28246 at 31b: jesse | 2008-03-13 17:00:43 -0400
* checkpoint
Modified: SVN-PropDB/bin/merger
==============================================================================
--- SVN-PropDB/bin/merger (original)
+++ SVN-PropDB/bin/merger Thu Mar 13 17:00:47 2008
@@ -44,23 +44,23 @@
sub import_changesets {
- my %args = validate(@_, { from => 1, to => 1 } );
+ my %args = validate( @_, { from => 1, to => 1 } );
my $source = $args{'from'};
my $target = $args{'to'};
- my $changesets_to_integrate = $source->fetch_changesets( after => $target->last_changeset_from_source($source->uuid) );
+ my $changesets_to_integrate
+ = $source->fetch_changesets( after => $target->last_changeset_from_source( $source->uuid ) );
for my $changeset (@$changesets_to_integrate) {
- next if ( $target->has_seen_changeset($changeset));
+ next if ( $target->has_seen_changeset($changeset) );
if ( $target->changeset_will_conflict($changeset) ) {
-
+
my $conflicts = $target->conflicts_from_changeset($changeset);
-
- # write out a nullification changeset beforehand,
- # - that way, the source update will apply cleanly
- # Then write out the source changeset
- # Then write out a new changeset which reverts the parts of the source changeset which target should win
+ # write out a nullification changeset beforehand,
+ # - that way, the source update will apply cleanly
+ # Then write out the source changeset
+ # Then write out a new changeset which reverts the parts of the source changeset which target should win
} else {
$target->integrate_changeset($changeset);
}
Added: SVN-PropDB/lib/Prophet/Conflict.pm
==============================================================================
--- (empty file)
+++ SVN-PropDB/lib/Prophet/Conflict.pm Thu Mar 13 17:00:47 2008
@@ -0,0 +1,136 @@
+use warnings;
+use strict;
+
+package Prophet::Conflict;
+
+use base qw/Class::Accessor/;
+
+__PACKAGE__->mk_accessors(qw/prophet_handle source_change target_change/);
+
+=head2 analyze_changeset Prophet::ChangeSet
+
+Take a look at a changeset. if there are any conflicts, populate the L<conflicting_changes> array on this object with a set of L<Prophet::ConflictingChange> objects.
+
+=cut
+
+sub analyze_changeset {
+ my $self = shift;
+ my ($changeset) = validate_pos( @_, { isa => 'Prophet::ChangeSet' } );
+
+ # XXX TODO
+
+# - a ConflictingChange if there are any conflicts in the change
+# - for each conflictingchange, we need to create a conflicting change property for each and every property that conflicts
+
+ for my $change ( $changeset->changes ) {
+ if ( my $change_conflicts = $self->generate_change_onflicts($change) ) {
+ push @{ $self->{conflicting_changes} }, $change_conflicts;
+ }
+ }
+
+ return 0;
+
+}
+
+sub generate_change_conflict {
+ my $self = shift;
+ my ($change) = validate_pos( @_, { isa => "Prophet::Change" } );
+
+ my $current_state = $self->prophet_handle->get_node_props( uuid => $change->node_uuid, type => $change->node_type );
+
+ my $file_op_conflict = '';
+
+ # It's ok to delete a node that exists
+ if ( $change->change_type eq 'delete' && !keys %$current_state ) {
+ $file_op_conflict = "delete_missing_file";
+ } elsif ( $change->change_type eq 'add_file' && keys %$current_state ) {
+ $file_op_conflict = "create_existing_file";
+ } elsif ( $change->change_type eq 'add_dir' && keys %$current_state ) {
+ $file_op_conflict = "create_existing_dir";
+ }
+
+ my @prop_conflicts;
+ for my $propchange ( $change->prop_changes ) {
+
+ # skip properties added by the change
+ next if ( !defined $current_state->{ $propchange->name } && !defined $propchange->old_value );
+
+ # If either the old version didn't have a value or the delta didn't have a value, then we know there's a conflict
+ my $s = {
+ source_old_value => $propchange->old_value,
+ target_old_value => $current_state->{$propchange_name},
+ source_new_value => $propchange->new_value
+ };
+
+ if ( !exists $current_state->{ $propchange->name }
+ || !defined $propchange->old_value
+ || ( $current_state->{ $propchange->name } ne $propchange->old_value ) )
+ {
+ push @prop_conflicts, Prophet::ConflictingPropChange->new($s);
+
+ }
+
+ }
+
+ my $change_conflict = Prophet::ConflictingChange->new(
+ { node_type => $change->node_type,
+ node_uuid => $change->node_uuid,
+ target_node_exists => ( keys %$current_state ? 1 : 0 ),
+ change_type => $change->change_type,
+ fileop_conflict => $fileop_conflict
+ }
+ );
+ push @{ $change_conflict->prop_conflicts }, @prop_conflicts;
+
+ return $change_conflict if ( $#prop_conflicts || $fileop_conflict );
+ return undef;
+}
+
+=head2 conflicting_changes
+
+Returns a referencew to an array of conflicting changes for this conflict
+
+
+=cut
+
+sub conflicting_changes {
+ my $self = shift;
+ $self->{'conflicting_changes'} ||= ();
+ return $self->{'conflicting_changes'};
+}
+
+sub generate_nullification_changeset {
+ my $self = shift;
+
+ my $nullification = Prophet::ChangeSet->new();
+ return $nullification;
+}
+
+package Prophet::ConflictingChange;
+
+use base qw/Class::Accessor/;
+
+# change_type is one of: create update delete
+__PACKAGE__->mk_accessors(qw/node_type node_uuid source_node_exists target_node_exists change_type fileop_conflict/);
+
+=head2 prop_conflicts
+
+Returns a reference to an array of Prophet::ConflictingPropChange objects
+
+=cut
+
+sub prop_conflicts {
+ my $self = shift;
+
+ $self->{'prop_conflicts'} ||= ();
+ return $self->{prop_conflicts};
+
+}
+
+package Prophet::ConflictingPropChange;
+
+use base qw/Class::Accessor/;
+
+__PACKAGE__->mk_accessors(qw/source_old_value target_value source_new_value/);
+
+1;
Modified: SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm (original)
+++ SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm Thu Mar 13 17:00:47 2008
@@ -126,92 +126,82 @@
}
+=head2 has_seen_changeset Prophet::ChangeSet
+
+Returns true if we've previously integrated this changeset, even if we originally recieved it from a different peer
+
+=cut
+
+
sub has_seen_changeset {
my $self = shift;
my ($changeset) = validate_pos( @_, { isa => "Prophet::ChangeSet" } );
- my $last_changeset_from_source
- = $self->last_changeset_from_source( $changeset->original_source_uuid || $changeset->source_uuid );
+ my $last = $self->last_changeset_from_source( $changeset->original_source_uuid || $changeset->source_uuid );
# if the source's sequence # is >= the changeset's sequence #, we can safely skip it
- return 1 if ( $last_changeset_from_source >= $changeset->sequence_no );
+ return 1 if ( $last >= $changeset->sequence_no );
+
}
+=head2 changeset_will_conflict Prophet::ChangeSet
+
+Returns true if any change that's part of this changeset won't apply cleanly to the head of the current replica
+=cut
sub changeset_will_conflict {
my $self = shift;
my ($changeset) = validate_pos( @_, { isa => "Prophet::ChangeSet" } );
- return 1 if ( keys %{$self->conflicts_from_changeset($changeset)});
+ return 1 if ( $self->conflicts_from_changeset($changeset));
+
return undef;
}
-sub conflicts_from_changeset{
- my $self = shift;
- my ($changeset) = validate_pos( @_, { isa => "Prophet::ChangeSet" } );
- my $conflict = Prophet::Conflict->new();
+=head2 conflicts_from_changeset Prophet::ChangeSet
- # XXX TODO
- die "Right here, we need to actually walk through all the changes and generate
-
- - a ConflictingChange if there are any conflicts in the change
- - for each conflictingchange, we need to create a conflicting change property for each and every property that conflicts
+Returns a L<Prophet::Conflict> object if the supplied L<Prophet::ChangeSet
+will generate conflicts if applied to the current replica.
+Returns undef if the current changeset wouldn't generate a conflict.
- for my $change ( $changeset->changes ) {
- return 1 if $self->change_will_conflict($change);
- }
+=cut
- return 0;
-}
-
-
-
-sub change_will_conflict {
+sub conflicts_from_changeset {
my $self = shift;
- my ($change) = validate_pos( @_, { isa => "Prophet::Change" } );
+ my ($changeset) = validate_pos( @_, { isa => "Prophet::ChangeSet" } );
- my $current_state = $self->prophet_handle->get_node_props( uuid => $change->node_uuid, type => $change->node_type );
+ my $conflict = Prophet::Conflict->new({ prophet_handle => $self->prophet_handle});
- # It's ok to delete a node that exists
- return 0 if ( $change->change_type eq 'delete' && keys %$current_state );
+ $conflict->analyze_changeset($changeset);
+
- # It's ok to create a node that doesn't exist
- return 0 if ( $change->change_type eq 'add_file' && !keys %$current_state );
- return 0 if ( $change->change_type eq 'add_dir' && !keys %$current_state );
-
- for my $propchange ( $change->prop_changes ) {
- # skip properties added by the change
- next if ( !defined $current_state->{ $propchange->name } && !defined $propchange->old_value );
-
- # If either the old version didn't have a value or the delta didn't have a value, then we
- # know there's a conflict
- # Ditto if they don't agree
- return 1
- if ( !exists $current_state->{ $propchange->name }
- || !defined $propchange->old_value
- || ( $current_state->{ $propchange->name } ne $propchange->old_value ) );
- }
+ return undef unless $#{$conflict->conflicting_changes()};
+
+ return $conflict;
- return 0;
}
+
sub integrate_changeset {
my $self = shift;
+ my ($changeset) = validate_pos(@_, { isa => 'Prophet::ChangeSet'});
+
+ if (my $conflict = $self->conflicts_from_changeset($changeset ) ) {
if (there's a conflict ) {
figure out our conflict resolution
generate a nullification change
# IMPORTANT: these should be an atomic unit. dying here would be poor.
# BUT WE WANT THEM AS THREEDIFFERENT SVN REVS
- integrate the nullification change
- integrate the original change
- integrate the conflict resolution change
+ #integrate the nullification change
+ # integrate the original change
+ # integrate the conflict resolution change
} else {
$self->prophet_handle->integrate_changeset(@_);
More information about the Bps-public-commit
mailing list