[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