[Bps-public-commit] r11027 - in SVN-PropDB: bin doc lib/Prophet lib/Prophet/Sync lib/Prophet/Sync/Source t

jesse at bestpractical.com jesse at bestpractical.com
Fri Mar 7 17:40:36 EST 2008


Author: jesse
Date: Fri Mar  7 17:40:20 2008
New Revision: 11027

Added:
   SVN-PropDB/lib/Prophet/Sync/Source.pm
Removed:
   SVN-PropDB/t/replay.t
Modified:
   SVN-PropDB/   (props changed)
   SVN-PropDB/bin/merger
   SVN-PropDB/doc/glossary
   SVN-PropDB/doc/notes-on-merging
   SVN-PropDB/doc/todo
   SVN-PropDB/lib/Prophet/Change.pm
   SVN-PropDB/lib/Prophet/ChangeSet.pm
   SVN-PropDB/lib/Prophet/Handle.pm
   SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm

Log:
 r28180 at 104:  jesse | 2008-03-07 17:39:54 -0500
 * The merge tool doesn't crash. of course, it also doesn't deal with conflicts yet, either


Modified: SVN-PropDB/bin/merger
==============================================================================
--- SVN-PropDB/bin/merger	(original)
+++ SVN-PropDB/bin/merger	Fri Mar  7 17:40:20 2008
@@ -3,56 +3,37 @@
 use warnings;
 use strict;
 
+use Prophet::Sync::Source::SVN;
+use Params::Validate;
+use Getopt::Long;
+
 my $opts = {};
 
-use Getopt::Long;
 GetOptions( $opts, 'target=s', 'source=s' );
-use Prophet::Sync::Source::SVN;
-
 validate_options($opts);
 
-my $target = Prophet::Sync::Source::SVN->new( { url => $opts->{'target'} });
-my $source = Prophet::Sync::Source::SVN->new( {url => $opts->{'source'} });
-
-fatal_error("You appear to be trying to merge two identical replicas. ".
-            "Either you're trying to merge a replica to itself or ".
-            "someone did a bad job cloning your database") if ($target->unique_id eq $source->unique_id);
-
-if ( $target->accepts_changesets ) {
-    my $changesets_to_integrate = $source->fetch_changesets( after => $target->last_changeset_for_source(source => $source->unique_id) );
+my $target = Prophet::Sync::Source::SVN->new( { url => $opts->{'target'} } );
+my $source = Prophet::Sync::Source::SVN->new( { url => $opts->{'source'} } );
 
-    for my $changeset (@$changesets_to_integrate) {
-        next if ( $target->has_seen_changeset(source => $source->unique_id, changeset => $changeset->changeset_uuid));
-        if ( $target->changeset_will_conflict($changeset) ) {
-            die "Conflicts are hard";
-
-# make a decision about who wins the conflict
-# For parts of the conflict _source_ should win, write out a nullification changeset beforehand,
-# apply the changeset. it will now apply cleanly
-
-# For parts of the conflict _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->apply_changeset($changeset);
-        }
+if ( $target->uuid eq $source->uuid ) {
+    fatal_error( "You appear to be trying to merge two identical replicas. "
+            . "Either you're trying to merge a replica to itself or "
+            . "someone did a bad job cloning your database" );
+}
 
-        $target->record_changeset_for_source(source => $source->unique_id, changeset => $changeset->changeset_uuid);
-    }
+if ( !$target->accepts_changesets ) {
+    fatal_error( $target->url . " does not accept changesets. Perhaps it's unwritable or something" );
+}
 
-} else {
-    fatal_error($target->url." does not accept changesets. Perhaps it's unwritable or something");
+import_changesets( from => $source, to => $target );
 
-}
+exit(0);
 
 sub validate_options {
     my $opts = shift;
-for (qw(source target)){ 
-die "You need to specify a --$_ url" unless ($opts->{$_});
-}
+    for (qw(source target)) {
+        die "You need to specify a --$_ url" unless ( $opts->{$_} );
+    }
 }
 
 sub fatal_error {
@@ -62,158 +43,32 @@
 }
 
 
-package Prophet::MergeSource;
-use base 'Class::Accessor';
-
-__PACKAGE__->mk_accessors(qw'url unique_id');
-
-sub accepts_changesets {}
-sub has_seen_changeset {}
-sub changeset_will_conflict {}
-sub apply_changeset {}
-sub fetch_changesets {}
-sub last_changeset_seen {}
-
-
-=head2 Assumptions about merge sources
-
-    - a source represents a branch 
-    - a branch has had many revisions applied
-    - revisions we apply to a branch from another branch are always applied UNCHANGED
-    - in the event of a conflict, we will apply a "pre-changeset" to smooth the application and a "post-changeset" if the applied changeset doesn't match the desired outcome.
-
-    on pull
-        we end up applying every single source changeset this peer has seen since the last time we saw this peer, one by one
-        we possibly apply another two changesets for each changeset to smooth the application
-
-
-
-        we skip any source changeset we've seen from another source before
-        
-
-    on push,
-        we publish each changeset we've made targetly.
-            - including "after" fixup changesets
-            - not including "before" fixup changesets.
-
-
-
-
-
-
-=head2 assumptions about merge changesets
-
-we can get (and hash):
-    -original database uuid
-    -original database revno
-    -original database author
-    -all changes
+sub import_changesets {
+    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) );
 
+    for my $changeset (@$changesets_to_integrate) {
+        next if ( $target->has_seen_changeset($changeset));
+        if ( $target->changeset_will_conflict($changeset) ) {
 
+             # make a decision about who wins the conflict
+             # For parts of the conflict _source_ should win, write out a nullification changeset beforehand,
+             # apply the changeset. it will now apply cleanly
+             
+             # For parts of the conflict _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);
+        }
 
+        $target->record_changeset_integration($changeset);
+    }
+}
 
 
-Audrey at 1:
-
-    Createdb
-    Add ticket 1 - subject 'foo'; status 'new'
-
-
-Jesse at 1:
-
-    j pull from a at 1
-
-    j at 1 - foo;new
-    a at 1 - foo;new
-
-    j at 2  foo->bar
-    a at 2  foo->baz
-        
-    c at 1 pull from j at 2
-
-        c now has:
-            a at 1
-            j at 2
-
-
-    c at 2 bar->frotz
-
-    c at 3 pull from a at 2
-    
-        a hands c: a at 2:foo->baz     
-
-        Conflict
-
-        to target c applies a pre-fixup:  
-            frotz->foo
-        to target c applies a at 2:
-            foo->baz
-        to target c applies a conflict resolution
-            baz->frotz
-
-
-    c at 4 push to a at 2
-
-        beforehand, a's state: baz
-        beforehand, c's state: frotz
-
-        beforehand, c's unpushed transactions: 
-            j at 2 foo->bar
-            c at 2 bar->frotz
-            c at 3
-                    pre-fixup: frotz->foo
-                    a at 2: foo->baz
-                    post-fixup: baz->frotz
-
-            so, what do we push?
-            options:
-                fixup to get a to our earliest state unpushed. that's kind of stupid and results in us replaying everthing all the time.
-                compute a single large changeset from a at HEAD to c at HEAD and apply that?
-
-
-        
-investigate "rumor" peer to peer replication
-
-
-take 2
-
-    there's a new peer I've not seen before. I need to merge in all their changes:
-
-        if a changeset comes from a replica at rev lower than our last-seen version for replica, skip it
-
-        for each of their changesets, import it, applying fixups as needed
-    
-
-=head3 push
-    
-    get the source list of all merge tickets.
-    find all target revs which source has never seen.
-        - genuine target changesets
-        - third-party changesets that the source has no merge ticket for
-
-        - skip "before" fixup changesets 
-        - include "after" fixup changesets, since they're merge resolutions
-
-
-    iterate over these revs the source has never seen.
-    when the changeset applies cleanly, just apply it.
-
-    when the changeset does _not_ apply cleanly, 
-        - apply a 'before' fix up transaction
-        - apply the original changeset
-        - apply a merge resolution changeset.
-            - TODO: this doesn't feel quite right
-            - the resolution should be the same as the equivalent merge resolution transaction on the "pull" variant if it exists.
-	                    What info do we have here?
-	                        - record uuid
-	                        - nearest parent variant
-	                        - target variant
-	                        - source variant
-	                        - information from the 'future (target head)' about the eventual desired outcome 
-	                    
-    - audit stage:
-        compare target head and source head. - they should now be identical, since the last transactions we replayed were all target merge fixup changesets. (is that true?)
-
-=cut
-1;

Modified: SVN-PropDB/doc/glossary
==============================================================================
--- SVN-PropDB/doc/glossary	(original)
+++ SVN-PropDB/doc/glossary	Fri Mar  7 17:40:20 2008
@@ -20,34 +20,51 @@
 Database
     A term to describe a uniquely identified set of object types and records sharing a single 'base' revision and Replica identifier
     A database contains multiple Records
-    
+   
+    Has one: uuid
+
+
 Replica
     An instance of a database. Replicas are expected to contain all Changesets from any other replica they have been synchronized with, but those Changesets are not guaranteed to be in the same sequence on each replica
-    
+
+
+    Has one: uuid
+
 Changeset
     A changeset contains "old" and "new" versions of a set of database "Records", they can be of any Record Type.
+    
+    Has one: source_uuid, sequence_no
+    Has many: changes
+
+
 
 Change
     An entry in a changeset. Contains all the updates to a given record for this changeset
    
 Record
     A Record is composed of zero or more Attributes and a universally unique identifier. Each record is categorized into a Record Type.
-        
-    (Discussion:    Really? I was just storing them under different dirs in the database. the goal was to provide a little separation between different kinds of nodes for managability.
+       
+
+    Has one: uuid
+    Has many: attributes
 
-(ah fine. then need to forget about changing types ;)    
-indeed. 
 
 
 Record Type 
     A Record Type is a category or "bucket" for zero or more records applications may define specific behaviours for Records of a certain Record Type, but Prophet does no more than to tag Records with a Record Type.
 Record Types are uniquely identified with a textual name and a UUID
     
-    
+    Has one: textual label, uuid
+
+
+
 
 Attribute
     A key-value pair on a Record.    
 
+    Has one: key, value
+
+
 Conflict
     A Conflict occurs when a Changeset is being applied and the current state of a Replica meets any of the following criteria:
 

Modified: SVN-PropDB/doc/notes-on-merging
==============================================================================
--- SVN-PropDB/doc/notes-on-merging	(original)
+++ SVN-PropDB/doc/notes-on-merging	Fri Mar  7 17:40:20 2008
@@ -188,3 +188,147 @@
     
 Resolution
     When the local Replica 
+
+
+=head2 Assumptions about merge sources
+
+    - a source represents a branch 
+    - a branch has had many revisions applied
+    - revisions we apply to a branch from another branch are always applied UNCHANGED
+    - in the event of a conflict, we will apply a "pre-changeset" to smooth the application and a "post-changeset" if the applied changeset doesn't match the desired outcome.
+
+    on pull
+        we end up applying every single source changeset this peer has seen since the last time we saw this peer, one by one
+        we possibly apply another two changesets for each changeset to smooth the application
+
+
+
+        we skip any source changeset we've seen from another source before
+        
+
+    on push,
+        we publish each changeset we've made targetly.
+            - including "after" fixup changesets
+            - not including "before" fixup changesets.
+
+
+
+
+
+
+=head2 assumptions about merge changesets
+
+we can get (and hash):
+    -original database uuid
+    -original database revno
+    -original database author
+    -all changes
+
+
+
+
+
+
+Audrey at 1:
+
+    Createdb
+    Add ticket 1 - subject 'foo'; status 'new'
+
+
+Jesse at 1:
+
+    j pull from a at 1
+
+    j at 1 - foo;new
+    a at 1 - foo;new
+
+    j at 2  foo->bar
+    a at 2  foo->baz
+        
+    c at 1 pull from j at 2
+
+        c now has:
+            a at 1
+            j at 2
+
+
+    c at 2 bar->frotz
+
+    c at 3 pull from a at 2
+    
+        a hands c: a at 2:foo->baz     
+
+        Conflict
+
+        to target c applies a pre-fixup:  
+            frotz->foo
+        to target c applies a at 2:
+            foo->baz
+        to target c applies a conflict resolution
+            baz->frotz
+
+
+    c at 4 push to a at 2
+
+        beforehand, a's state: baz
+        beforehand, c's state: frotz
+
+        beforehand, c's unpushed transactions: 
+            j at 2 foo->bar
+            c at 2 bar->frotz
+            c at 3
+                    pre-fixup: frotz->foo
+                    a at 2: foo->baz
+                    post-fixup: baz->frotz
+
+            so, what do we push?
+            options:
+                fixup to get a to our earliest state unpushed. that's kind of stupid and results in us replaying everthing all the time.
+                compute a single large changeset from a at HEAD to c at HEAD and apply that?
+
+
+        
+investigate "rumor" peer to peer replication
+
+
+take 2
+
+    there's a new peer I've not seen before. I need to merge in all their changes:
+
+        if a changeset comes from a replica at rev lower than our last-seen version for replica, skip it
+
+        for each of their changesets, import it, applying fixups as needed
+    
+
+=head3 push
+    
+    get the source list of all merge tickets.
+    find all target revs which source has never seen.
+        - genuine target changesets
+        - third-party changesets that the source has no merge ticket for
+
+        - skip "before" fixup changesets 
+        - include "after" fixup changesets, since they're merge resolutions
+
+
+    iterate over these revs the source has never seen.
+    when the changeset applies cleanly, just apply it.
+
+    when the changeset does _not_ apply cleanly, 
+        - apply a 'before' fix up transaction
+        - apply the original changeset
+        - apply a merge resolution changeset.
+            - TODO: this doesn't feel quite right
+            - the resolution should be the same as the equivalent merge resolution transaction on the "pull" variant if it exists.
+	                    What info do we have here?
+	                        - record uuid
+	                        - nearest parent variant
+	                        - target variant
+	                        - source variant
+	                        - information from the 'future (target head)' about the eventual desired outcome 
+	                    
+    - audit stage:
+        compare target head and source head. - they should now be identical, since the last transactions we replayed were all target merge fixup changesets. (is that true?)
+
+=cut
+1;

Modified: SVN-PropDB/doc/todo
==============================================================================
--- SVN-PropDB/doc/todo	(original)
+++ SVN-PropDB/doc/todo	Fri Mar  7 17:40:20 2008
@@ -1,7 +1,8 @@
+
+
 - ability to record a merge ticket for a uuid
 - ability to read all merge tickets
 
-- 
 
 - ability to 'pull' non-conflicting updates from a remote db
 - ability to 'pull' conflicting updates from a remote db

Modified: SVN-PropDB/lib/Prophet/Change.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Change.pm	(original)
+++ SVN-PropDB/lib/Prophet/Change.pm	Fri Mar  7 17:40:20 2008
@@ -9,6 +9,11 @@
 use Params::Validate;
 __PACKAGE__->mk_accessors(qw/node_type node_uuid change_type/);
 
+
+# Valid values for change_type:
+# add_file add_dir update_file delete
+#
+
 sub prop_changes {
     my $self = shift;
     return @{$self->{prop_changes}};

Modified: SVN-PropDB/lib/Prophet/ChangeSet.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/ChangeSet.pm	(original)
+++ SVN-PropDB/lib/Prophet/ChangeSet.pm	Fri Mar  7 17:40:20 2008
@@ -7,7 +7,7 @@
 
 use base qw/Class::Accessor/;
 
-__PACKAGE__->mk_accessors(qw/changeset_uuid source_uuid/);
+__PACKAGE__->mk_accessors(qw/sequence_no source_uuid/);
 
 sub add_change {
     my $self = shift;

Modified: SVN-PropDB/lib/Prophet/Handle.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Handle.pm	(original)
+++ SVN-PropDB/lib/Prophet/Handle.pm	Fri Mar  7 17:40:20 2008
@@ -161,27 +161,25 @@
 
 
 my $MERGETICKET_METATYPE = '_merge_tickets';
-sub last_changeset_for_source {
+sub last_changeset_from_source {
     my $self = shift;
-    my %args = validate( @_, { source => 1, } );
+    my ($source)  = validate_pos( @_, { isa => 'Prophet::Sync::Source' } );
 
-    my %props = $self->get_node_props(uuid => $args{'source'}, type => $MERGETICKET_METATYPE);
+    my $props = eval {$self->get_node_props(uuid => $source->uuid, type => $MERGETICKET_METATYPE)};
     
-    return $props{'last-rev'};
-
-
+    return $props->{'last-changeset'};
 
 }
 
-sub record_changeset_for_source {
+sub record_changeset_integration {
     my $self = shift;
-    my %args = validate( @_, { source => 1,  changeset => 1} );
+    my ($changeset) = validate_pos(@_, { isa => 'Prophet::ChangeSet'});
 
-    my $props = eval { $self->get_node_props(uuid => $args{'source'}, type => $MERGETICKET_METATYPE)};
-    unless ($props->{'last-rev'}) {
-            eval { $self->create_node( uuid => $args{'source'}, type => $MERGETICKET_METATYPE, props => {} )};
+    my $props = eval { $self->get_node_props(uuid => $changeset->source_uuid, type => $MERGETICKET_METATYPE)};
+    unless ($props->{'last-changeset'}) {
+            eval { $self->create_node( uuid => $changeset->source_uuid, type => $MERGETICKET_METATYPE, props => {} )};
     }
-    $self->set_node_props(uuid => $args{'source'}, type => $MERGETICKET_METATYPE, props => { 'last-rev' => $args{'changeset'}});
+    $self->set_node_props(uuid => $changeset->source_uuid, type => $MERGETICKET_METATYPE, props => { 'last-changeset' => $changeset->sequence_no});
 
 }
 

Added: SVN-PropDB/lib/Prophet/Sync/Source.pm
==============================================================================
--- (empty file)
+++ SVN-PropDB/lib/Prophet/Sync/Source.pm	Fri Mar  7 17:40:20 2008
@@ -0,0 +1,8 @@
+use warnings;
+use strict;
+
+package Prophet::Sync::Source;
+use base qw/Class::Accessor/;
+
+
+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	Fri Mar  7 17:40:20 2008
@@ -2,8 +2,8 @@
 use strict;
 
 package Prophet::Sync::Source::SVN;
-use base qw/Class::Accessor/;
-use Params::Validate;
+use base qw/Prophet::Sync::Source/;
+use Params::Validate qw(:all);
 
 use SVN::Core;
 use SVN::Ra;
@@ -43,7 +43,7 @@
 
 }
 
-sub unique_id {
+sub uuid {
     my $self = shift;
     return $self->ra->get_uuid;
 }
@@ -71,40 +71,40 @@
 
 
 sub _recode_changeset {
-    my $self = shift;
+    my $self  = shift;
     my $entry = shift;
 
-        my $changeset = Prophet::ChangeSet->new(
-            {   changeset_uuid => $entry->{'revision'}.'@'.$self->unique_id,
-                source_uuid => $self->unique_id
-            }
-        );
-        for my $path ( keys %{ $entry->{'paths'} } ) {
-            if ( $path =~ qr|^(.+)/(.*?)/(.*?)$| ) {
-                my ( $prefix, $type, $record ) = ( $1, $2, $3 );
-                my $change = Prophet::Change->new(
-                    {   node_type   => $type,
-                        node_uuid   => $record,
-                        change_type => $entry->{'paths'}->{$path}->{'fs'}
-                    }
-                );
-                for my $name ( keys %{ $entry->{'paths'}->{$path}->{prop_deltas} } ) {
-                    warn "Changing $name for $change";
-                    $change->add_prop_change(
-                        name => $name,
-                        old => $entry->{paths}->{$path}->{prop_deltas}->{$name}->{'old'},
-                        new => $entry->{paths}->{$path}->{prop_deltas}->{$name}->{'new'},
-                    );
+    my $changeset = Prophet::ChangeSet->new(
+        {   sequence_no => $entry->{'revision'},
+            source_uuid => $self->uuid
+        }
+    );
+    for my $path ( keys %{ $entry->{'paths'} } ) {
+        if ( $path =~ qr|^(.+)/(.*?)/(.*?)$| ) {
+            my ( $prefix, $type, $record ) = ( $1, $2, $3 );
+            my $change = Prophet::Change->new(
+                {   node_type   => $type,
+                    node_uuid   => $record,
+                    change_type => $entry->{'paths'}->{$path}->{'fs'}
                 }
-
-                $changeset->add_change( change => $change );
-            } else {
-                     warn "Discarding change to a non-record: $path";
+            );
+            for my $name ( keys %{ $entry->{'paths'}->{$path}->{prop_deltas} } ) {
+                warn "Changing $name for $change";
+                $change->add_prop_change(
+                    name => $name,
+                    old  => $entry->{paths}->{$path}->{prop_deltas}->{$name}->{'old'},
+                    new  => $entry->{paths}->{$path}->{prop_deltas}->{$name}->{'new'},
+                );
             }
 
+            $changeset->add_change( change => $change );
+        } else {
+            warn "Discarding change to a non-record: $path";
         }
-        return $changeset;
+
     }
+    return $changeset;
+}
 sub accepts_changesets {
     my $self = shift;
     return 1 if $self->prophet_handle;
@@ -112,33 +112,108 @@
 }
 
 
-sub has_seen_changeset { warn "\tneed to implement has_seen_changeset"; return undef;}
-sub changeset_will_conflict { warn "\tneed to implement changeset_will_conflict"; return undef }
-sub apply_changeset {
+sub has_seen_changeset { 
+    my $self = shift;
+    my ($changeset) =  validate_pos(@_, {isa => "Prophet::ChangeSet"});
+
+    # find the last changeset for the source
+    my $last_changeset_from_source = $self->last_changeset_from_source($changeset->source_uuid);
+    
+    # if the source's sequence # is >= the changeset's sequence #, we can safely skip it
+  
+    warn "the liast changeset I saw from ".$changeset->source_uuid . " was $last_changeset_from_source";
+    warn "my changeset is ".$changeset->sequence_no;
+    if ($last_changeset_from_source >= $changeset->sequence_no) {
+        return 1;
+    }
+
+    
+    
+
+
+
+}
+
+sub changeset_will_conflict { 
+    my $self = shift;
+    my ($changeset) =  validate_pos(@_, {isa => "Prophet::ChangeSet"} );
+
+    warn "Checking ".$changeset->sequence_no."@".$changeset->source_uuid ." for conflicts";
+
+    for my $change ($changeset->changes) {
+        return 1 if $self->change_will_conflict($change);
+    }
+
+    return 0;
+}
+
+sub change_will_conflict {
     my $self = shift;
+    my ($change) =  validate_pos(@_, {isa => "Prophet::Change"} );
+
+    $change->change_type;
+    
+    my $current_state = $self->prophet_handle->get_node_props(uuid => $change->node_uuid, type => $change->node_type);
+
+     # It's ok to delete a node that exists
+     return 0 if ($change->change_type eq 'delete' && keys %$current_state) ;
+     
+     # 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 ) {
+        next if ( !defined $current_state->{ $propchange->name } && !defined $propchange->old_value );
+        return 1 if 
+        (      !exists $current_state->{ $propchange->name }
+            || !defined $propchange->old_value
+            || ( $current_state->{ $propchange->name } ne $propchange->old_value ) );
+    }
+
+    return 0;
+
+}
+
+
+
+
+sub integrate_changeset {
+    my $self      = shift;
     my $changeset = shift;
+
     # open up a change handle locally
-    warn "Applying Changeset ".$changeset->changeset_uuid;
+    warn "Applying Changeset " . $changeset->sequence_no;
 
     $self->prophet_handle->begin_edit();
 
-    for my $change ($changeset->changes) {
+    for my $change ( $changeset->changes ) {
         warn "\tApplying a change";
-        warn "\t".$change->change_type;
+        warn "\t" . $change->change_type;
 
         my %new_props = map { $_->name => $_->new_value } $change->prop_changes;
 
-        if ($change->change_type eq 'add_file') {
-                warn "\tAdded a file - ".$change->node_type,$change->node_uuid;
-                $self->prophet_handle->create_node(type => $change->node_type, uuid => $change->node_uuid, props => \%new_props);
-        } elsif ($change->change_type eq 'add_dir') {
-                warn "\tAdded a dir - ".$change->node_type,$change->node_uuid;
-        } elsif ($change->change_type eq 'update_file') {
-                warn "\tUpdated a file - ".$change->node_type,$change->node_uuid;
-                $self->prophet_handle->set_node_props(type => $change->node_type, uuid => $change->node_uuid, props => \%new_props);
-        } elsif ($change->change_type eq 'delete') {
-                warn "\tDeleted file - ".$change->node_type,$change->node_uuid;
-                $self->prophet_handle->delete_node(type => $change->node_type, uuid => $change->node_uuid);
+        if ( $change->change_type eq 'add_file' ) {
+            warn "\tAdded a file - " . $change->node_type, $change->node_uuid;
+            $self->prophet_handle->create_node(
+                type  => $change->node_type,
+                uuid  => $change->node_uuid,
+                props => \%new_props
+            );
+        } elsif ( $change->change_type eq 'add_dir' ) {
+            warn "\tAdded a dir - " . $change->node_type, $change->node_uuid;
+        } elsif ( $change->change_type eq 'update_file' ) {
+            warn "\tUpdated a file - " . $change->node_type, $change->node_uuid;
+            $self->prophet_handle->set_node_props(
+                type  => $change->node_type,
+                uuid  => $change->node_uuid,
+                props => \%new_props
+            );
+        } elsif ( $change->change_type eq 'delete' ) {
+            warn "\tDeleted file - " . $change->node_type, $change->node_uuid;
+            $self->prophet_handle->delete_node(
+                type => $change->node_type,
+                uuid => $change->node_uuid
+            );
         }
 
     }
@@ -152,28 +227,31 @@
 
 
 # XXX TODO this is hacky as hell and violates abstraction barriers in the name of doing things over the RA
-sub last_changeset_for_source {
+
+sub last_changeset_from_source {
     my $self = shift;
-    my %args = validate( @_, { source => 1 } );
+    # XXX TODO should htis be an object rather than a uuid?
+    my ($source) = validate_pos(@_, {type => SCALAR } );
     my ( $stream, $pool );
 
     # XXX HACK
-    my $filename = join( "/",
-        "_prophet", $Prophet::Handle::MERGETICKET_METATYPE,
-        $args{'source'} );
+    my $filename = join( "/", "_prophet", $Prophet::Handle::MERGETICKET_METATYPE, $source );
+    warn "Looking up $filename";
     my ( $rev_fetched, $props ) = eval {
-        $self->ra->get_file( $filename, $self->ra->get_latest_revnum,
-            $stream, $pool );
+        $self->ra->get_file( $filename, $self->ra->get_latest_revnum, $stream, $pool );
     };
-    return ( $props->{'last-rev'} );
+
+    return ( $props->{'last-changeset'} ||0 );
 
 }
 
-sub record_changeset_for_source {
+sub record_changeset_integration {
     my $self = shift;
+
     return undef unless ( $self->accepts_changesets );
-    my %args = validate( @_, { source => 1, changeset => 1 } );
-    $self->prophet_handle->record_changeset_for_source(%args);
+    my ($changeset) = validate_pos(@_, { isa => 'Prophet::ChangeSet'});
+
+    $self->prophet_handle->record_changeset_integration($changeset);
 
 }
 



More information about the Bps-public-commit mailing list