[Bps-public-commit] r11539 - in SVN-PropDB: . doc lib/Prophet/Handle
jesse at bestpractical.com
jesse at bestpractical.com
Sat Apr 5 17:29:22 EDT 2008
Author: jesse
Date: Sat Apr 5 17:28:42 2008
New Revision: 11539
Added:
SVN-PropDB/lib/Prophet/Handle/
SVN-PropDB/lib/Prophet/Handle/SVN.pm
Modified:
SVN-PropDB/ (props changed)
SVN-PropDB/doc/todo
SVN-PropDB/lib/Prophet/Handle.pm
SVN-PropDB/lib/Prophet/Record.pm
Log:
r29238 at 31b: jesse | 2008-04-05 11:28:03 -1000
* Extracting out Handle::SVN methods
Modified: SVN-PropDB/doc/todo
==============================================================================
--- SVN-PropDB/doc/todo (original)
+++ SVN-PropDB/doc/todo Sat Apr 5 17:28:42 2008
@@ -1,84 +1,77 @@
Todo Saturday
- get a state_db exposed within the ::ForeignReplica driver
- - replace app::cache with state dbs
+ - replace app::cache with state dbs
+
+ - multiple Prophet::Handle can point to the same repo_handle but with different db_root
+ - repo_handle should be the actual storage layer
+ - current use of repo_handle
+ - prophet::record: get_logs/youngest_rev/reivision_root/node_proplist
+ - prophet::handle: revision_root, current_edit
+ - prophet::test : youngest_rev
+ - move {begin,end}_edit, node* methods into repo handler (Prophet::Handle::SVN) ?
+ record_resolution & other friends belong higher level than actual storage, ditto for record_changeset,
+ node* and edit* functions are needed from the storage layer
+ rebless is probbly good for now, i will work on the callers that creates proper handles.
+
+ - what if we move them to ..no. moving them to ::Replica::SVN is wrong.
+
+ - Should I start on that and make Handle->new() rebless to handle::svn new for now?
+
+
+
+
- implement a simple Prophet::Replica::Hiveminder for "personal tasks only"
- write tests for RT-prophet-prophet-RT sync
- lunch
- implement uuids for prophet databases
- - (use the uuid as a prefix for the db root?)
- - design/implement basic non-svn sync protocol
-
- - sketch out RT scrips replacement
-
- - dinner
-
-Saturday done
-
- - extract the reusable bits of Prophet::Replica::RT to
- Prophet::ForeignReplica
+ - (use the uuid as a prefix for the db root?)
+ - each replica should know the uuid of the database it is a replica of
+ - merging between replicas with different uuids should require a 'force' argument of some kind.
+
+ - push to a remote prophet
+ - bundle all changesets newer than that which we know the other party hasn't seen up as a serialized structure (FeedSync? Storable ;)?)
+ - send them to remote to "pull from textfile
+ - also need to send along all merge tickets we have
+
+ - design/implement basic non-svn sync protocol
+ - refactor Source::integrate_changeset to get the merge ticket and let the source(dst) decide how to record them, rather than having handle do the job
+ - sketch out RT scrips replacement
------------------
+ - dinner
-- prophet databases need uuids.
- - each replica should know the uuid of the database it is a replica of
- - merging between replicas with different uuids should require a 'force' argument of some kind.
-- refactor Source::integrate_changeset to get the merge ticket and let the source(dst) decide how to record them, rather than having handle does the job
-- also need to send along all merge tickets we have
+Saturday done
-- validation on bug tracker fields
- - severity
+ - extract the reusable bits of Prophet::Replica::RT to
+ Prophet::ForeignReplica
-- ability to add comments to a bug (visible history entries)
- - maybe long-prop edits
-- push to a remote prophet
-
- - bundle all changesets newer than that which we know the other party hasn't seen up as a serialized structure (FeedSync? Storable ;)?)
- - send them to remote to "pull from textfile
-
-- pull from a remote RT by url and query
- - be able to tell RT to only give us history entries newer than the $DATE of RT history entry with ID #1234 (which is the sequence number we used for the merge ticket from the remote RT).
-
- - massage this crap into a sequence of prophet changesets
- - apply.
- - rt id & record uuid bidirectional mapping how?
- - uuid as a link?
- - get rt to store uuid on all tickets at create time?
- - jesse thinks rt should do this anyway
-
-- issues:
- - how do we validate RT queues?
- - how do we deal with RT rejecting a commit because the committer doesn't have acls?
- - how do we map random remote sd users to rt users?
-
-- push to a remote RT
- - get the remote RT to give us its merge tickets ?!
- - for every txn RT hasn't seen, send the txn to remote.
- - if it's a record-create, we need to do some clever mapping.
- "find an RT ticket with the uuid $UUID"
- map that back to local
+Todo after saturday:
+- validation on bug tracker fields - severity
- Replace this todo list with a svb database
- elegant support for large attachments
- RESTy web server API to let third-parties build non-perl apps against a Prophet Depot
-
- define a value for a property that is a reference to another record or set of records
Archive:
+- ability to add comments to a bug (visible history entries)
+ - maybe long-prop edits
+
+
- when committing any change:
- record the original depot uuid and change sequence_no as revprops
Modified: SVN-PropDB/lib/Prophet/Handle.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Handle.pm (original)
+++ SVN-PropDB/lib/Prophet/Handle.pm Sat Apr 5 17:28:42 2008
@@ -7,12 +7,8 @@
use Data::Dumper;
use Data::UUID;
-use SVN::Core;
-use SVN::Repos;
-use SVN::Fs;
+our $DEBUG = 0;
-our $DEBUG = '0';
-__PACKAGE__->mk_accessors(qw(repo_path repo_handle current_edit _pool));
use constant db_root => '_prophet';
@@ -24,83 +20,10 @@
sub new {
my $class = shift;
- my $self = {};
- bless $self, $class;
- my %args = validate( @_, { repository => 1, db_root => 0 } );
- $self->repo_path( $args{'repository'} );
- $self->_connect();
- $self->_pool( SVN::Pool->new );
-
- return $self;
-}
-
-=head2 current_root
-
-Returns a handle to the svn filesystem's HEAD
-
-=cut
-
-sub current_root {
- my $self = shift;
- $self->repo_handle->fs->revision_root( $self->repo_handle->fs->youngest_rev );
+ use Prophet::Handle::SVN;
+ return Prophet::Handle::SVN->new(@_);
}
-sub _connect {
- my $self = shift;
- my $repos = eval { SVN::Repos::open( $self->repo_path ); };
-
- # If we couldn't open the repository handle, we should create it
- if ( $@ && !-d $self->repo_path ) {
- $repos = SVN::Repos::create( $self->repo_path, undef, undef, undef, undef, $self->_pool );
- }
-
- $self->repo_handle($repos);
- $self->_create_nonexistent_dir( $self->db_root );
-}
-
-sub _create_nonexistent_dir {
- my $self = shift;
- my $dir = shift;
- my $pool = SVN::Pool->new_default;
- my $root = $self->current_edit ? $self->current_edit->root : $self->current_root;
-
- unless ( $root->is_dir($dir) ) {
- my $inside_edit = $self->current_edit ? 1 : 0;
- $self->begin_edit() unless ($inside_edit);
- $self->current_edit->root->make_dir($dir);
- $self->commit_edit() unless ($inside_edit);
- }
-}
-
-=head2 begin_edit
-
-Starts a new transaction within the replica's backend database. Sets L</current_edit> to that edit object.
-
-Returns $self->current_edit.
-
-=cut
-
-sub begin_edit {
- my $self = shift;
- my $fs = $self->repo_handle->fs;
- $self->current_edit( $fs->begin_txn( $fs->youngest_rev ) );
- return $self->current_edit;
-}
-
-=head2 commit_edit
-
-Finalizes L</current_edit> and sets the 'svn:author' change-prop to the current user.
-
-=cut
-
-sub commit_edit {
- my $self = shift;
- my $txn = shift;
- $self->current_edit->change_prop( 'svn:author', ( $ENV{'PROPHET_USER'} || $ENV{'USER'} ) );
- $self->current_edit->commit;
- $self->current_edit(undef);
-
-}
=head2 integrate_changeset L<Prophet::ChangeSet>
@@ -220,182 +143,13 @@
} else {
Carp::confess( " I have never heard of the change type: " . $change->change_type );
}
- my $changed = $self->current_edit->root->paths_changed;
-
-}
-
-=head2 create_node { type => $TYPE, uuid => $uuid, props => { key-value pairs }}
-
-Create a new record of type C<$type> with uuid C<$uuid> within the current replica.
-
-Sets the record's properties to the key-value hash passed in as the C<props> argument.
-
-If called from within an edit, it uses the current edit. Otherwise it manufactures and finalizes one of its own.
-
-=cut
-
-sub create_node {
- my $self = shift;
- my %args = validate( @_, { uuid => 1, props => 1, type => 1 } );
-
- $self->_create_nonexistent_dir( join( '/', $self->db_root, $args{'type'} ) );
-
- my $inside_edit = $self->current_edit ? 1 : 0;
- $self->begin_edit() unless ($inside_edit);
-
- my $file = $self->file_for( uuid => $args{uuid}, type => $args{'type'} );
- $self->current_edit->root->make_file($file);
- {
- my $stream = $self->current_edit->root->apply_text( $file, undef );
-
- # print $stream Dumper( $args{'props'} );
- close $stream;
- }
- $self->_set_node_props(
- uuid => $args{uuid},
- props => $args{props},
- type => $args{'type'}
- );
- $self->commit_edit() unless ($inside_edit);
-
-}
-
-sub _set_node_props {
- my $self = shift;
- my %args = validate( @_, { uuid => 1, props => 1, type => 1 } );
-
- my $file = $self->file_for( uuid => $args{uuid}, type => $args{type} );
- foreach my $prop ( keys %{ $args{'props'} } ) {
- eval { $self->current_edit->root->change_node_prop( $file, $prop, $args{'props'}->{$prop}, undef ) };
- Carp::confess($@) if ($@);
- }
-}
-
-=head2 delete_node {uuid => $uuid, type => $type }
-
-Deletes the node C<$uuid> of type C<$type> from the current replica.
-
-Manufactures its own new edit if C<$self->current_edit> is undefined.
-
-=cut
-
-sub delete_node {
- my $self = shift;
- my %args = validate( @_, { uuid => 1, type => 1 } );
-
- my $inside_edit = $self->current_edit ? 1 : 0;
- $self->begin_edit() unless ($inside_edit);
-
- $self->current_edit->root->delete( $self->file_for( uuid => $args{uuid}, type => $args{type} ) );
- $self->commit_edit() unless ($inside_edit);
- return 1;
+
}
-=head2 set_node_props { uuid => $uuid, type => $type, props => {hash of kv pairs }}
-
-
-Updates the record of type C<$type> with uuid C<$uuid> to set each property defined by the props hash. It does NOT alter any property not defined by the props hash.
-
-Manufactures its own current edit if none exists.
-
-=cut
-
-sub set_node_props {
- my $self = shift;
- my %args = validate( @_, { uuid => 1, props => 1, type => 1 } );
- my $inside_edit = $self->current_edit ? 1 : 0;
- $self->begin_edit() unless ($inside_edit);
- my $file = $self->file_for( uuid => $args{uuid}, type => $args{'type'} );
- $self->_set_node_props(
- uuid => $args{uuid},
- props => $args{props},
- type => $args{'type'}
- );
- $self->commit_edit() unless ($inside_edit);
-
-}
-
-=head2 uuid
-
-Returns the uuid of the repilica
-
-=cut
-
-sub uuid {
- my $self = shift;
- return $self->repo_handle->fs->get_uuid;
-}
-
-=head2 get_node_props {uuid => $uuid, type => $type, root => $root }
-
-Returns a hashref of all properties for the record of type $type with uuid C<$uuid>.
-
-'root' is an optional argument which you can use to pass in an alternate historical version of the replica to inspect. Code to look at the immediately previous version of a record might look like:
-
- $handle->get_node_props(
- type => $record->type,
- uuid => $record->uuid,
- root => $self->repo_handle->fs->revision_root( $self->repo_handle->fs->youngest_rev - 1 )
- );
-=cut
-
-sub get_node_props {
- my $self = shift;
- my %args = validate( @_, { uuid => 1, type => 1, root => undef } );
- my $root = $args{'root'} || $self->current_root;
- return $root->node_proplist( $self->file_for( uuid => $args{'uuid'}, type => $args{'type'} ) );
-}
-
-=head2 file_for { uuid => $UUID, type => $type }
-
-Returns a file path within the repository (starting from the root)
-
-=cut
-
-sub file_for {
- my $self = shift;
- my %args = validate( @_, { uuid => 1, type => 1 } );
- Carp::cluck unless $args{uuid};
- my $file = join( "/", $self->directory_for_type( type => $args{'type'} ), $args{'uuid'} );
- return $file;
-
-}
-
-sub directory_for_type {
- my $self = shift;
- my %args = validate( @_, { type => 1 } );
- Carp::cluck unless defined $args{type};
- return join( "/", $self->db_root, $args{'type'} );
-
-}
-
-=head2 node_exists {uuid => $uuid, type => $type, root => $root }
-
-Returns true if the node in question exists. False otherwise
-
-=cut
-
-sub node_exists {
- my $self = shift;
- my %args = validate( @_, { uuid => 1, type => 1, root => undef } );
-
- my $root = $args{'root'} || $self->current_root;
- return $root->check_path( $self->file_for( uuid => $args{'uuid'}, type => $args{'type'} ) );
-
-}
-
-sub type_exists {
- my $self = shift;
- my %args = validate( @_, { type => 1, root => undef } );
-
- my $root = $args{'root'} || $self->current_root;
- return $root->check_path( $self->directory_for_type( type => $args{'type'}, ) );
-
-}
our $MERGETICKET_METATYPE = '_merge_tickets';
Added: SVN-PropDB/lib/Prophet/Handle/SVN.pm
==============================================================================
--- (empty file)
+++ SVN-PropDB/lib/Prophet/Handle/SVN.pm Sat Apr 5 17:28:42 2008
@@ -0,0 +1,291 @@
+use warnings;
+use strict;
+
+package Prophet::Handle::SVN;
+use base qw/Prophet::Handle/;
+
+use SVN::Core;
+use SVN::Repos;
+use SVN::Fs;
+
+our $DEBUG = '0';
+use Params::Validate qw(:all);
+
+__PACKAGE__->mk_accessors(qw(repo_path repo_handle current_edit _pool));
+
+
+
+sub new {
+ my $class = shift;
+ my $self = {};
+ bless $self, $class;
+
+ my %args = validate( @_, { repository => 1, db_root => 0 } );
+ $self->repo_path( $args{'repository'} );
+ $self->_connect();
+ $self->_pool( SVN::Pool->new );
+ return $self;
+}
+
+
+=head2 uuid
+
+Returns the uuid of the repilica
+
+=cut
+
+sub uuid {
+ my $self = shift;
+ return $self->repo_handle->fs->get_uuid;
+}
+
+
+
+=head2 current_root
+
+Returns a handle to the svn filesystem's HEAD
+
+=cut
+
+sub current_root {
+ my $self = shift;
+ $self->repo_handle->fs->revision_root( $self->repo_handle->fs->youngest_rev );
+}
+
+sub _connect {
+ my $self = shift;
+ my $repos = eval { SVN::Repos::open( $self->repo_path ); };
+
+ # If we couldn't open the repository handle, we should create it
+ if ( $@ && !-d $self->repo_path ) {
+ $repos = SVN::Repos::create( $self->repo_path, undef, undef, undef, undef, $self->_pool );
+ }
+
+ $self->repo_handle($repos);
+ $self->_create_nonexistent_dir( $self->db_root );
+}
+
+sub _create_nonexistent_dir {
+ my $self = shift;
+ my $dir = shift;
+ my $pool = SVN::Pool->new_default;
+ my $root = $self->current_edit ? $self->current_edit->root : $self->current_root;
+
+ unless ( $root->is_dir($dir) ) {
+ my $inside_edit = $self->current_edit ? 1 : 0;
+ $self->begin_edit() unless ($inside_edit);
+ $self->current_edit->root->make_dir($dir);
+ $self->commit_edit() unless ($inside_edit);
+ }
+}
+
+=head2 begin_edit
+
+Starts a new transaction within the replica's backend database. Sets L</current_edit> to that edit object.
+
+Returns $self->current_edit.
+
+=cut
+
+sub begin_edit {
+ my $self = shift;
+ my $fs = $self->repo_handle->fs;
+ $self->current_edit( $fs->begin_txn( $fs->youngest_rev ) );
+ return $self->current_edit;
+}
+
+=head2 commit_edit
+
+Finalizes L</current_edit> and sets the 'svn:author' change-prop to the current user.
+
+=cut
+
+sub commit_edit {
+ my $self = shift;
+ my $txn = shift;
+ $self->current_edit->change_prop( 'svn:author', ( $ENV{'PROPHET_USER'} || $ENV{'USER'} ) );
+ $self->current_edit->commit;
+ $self->current_edit(undef);
+
+}
+
+
+
+=head2 create_node { type => $TYPE, uuid => $uuid, props => { key-value pairs }}
+
+Create a new record of type C<$type> with uuid C<$uuid> within the current replica.
+
+Sets the record's properties to the key-value hash passed in as the C<props> argument.
+
+If called from within an edit, it uses the current edit. Otherwise it manufactures and finalizes one of its own.
+
+=cut
+
+sub create_node {
+ my $self = shift;
+ my %args = validate( @_, { uuid => 1, props => 1, type => 1 } );
+
+ $self->_create_nonexistent_dir( join( '/', $self->db_root, $args{'type'} ) );
+
+ my $inside_edit = $self->current_edit ? 1 : 0;
+ $self->begin_edit() unless ($inside_edit);
+
+ my $file = $self->file_for( uuid => $args{uuid}, type => $args{'type'} );
+ $self->current_edit->root->make_file($file);
+ {
+ my $stream = $self->current_edit->root->apply_text( $file, undef );
+
+ # print $stream Dumper( $args{'props'} );
+ close $stream;
+ }
+ $self->_set_node_props(
+ uuid => $args{uuid},
+ props => $args{props},
+ type => $args{'type'}
+ );
+ $self->commit_edit() unless ($inside_edit);
+
+}
+
+sub _set_node_props {
+ my $self = shift;
+ my %args = validate( @_, { uuid => 1, props => 1, type => 1 } );
+
+ my $file = $self->file_for( uuid => $args{uuid}, type => $args{type} );
+ foreach my $prop ( keys %{ $args{'props'} } ) {
+ eval { $self->current_edit->root->change_node_prop( $file, $prop, $args{'props'}->{$prop}, undef ) };
+ Carp::confess($@) if ($@);
+ }
+}
+
+=head2 delete_node {uuid => $uuid, type => $type }
+
+Deletes the node C<$uuid> of type C<$type> from the current replica.
+
+Manufactures its own new edit if C<$self->current_edit> is undefined.
+
+=cut
+
+sub delete_node {
+ my $self = shift;
+ my %args = validate( @_, { uuid => 1, type => 1 } );
+
+ my $inside_edit = $self->current_edit ? 1 : 0;
+ $self->begin_edit() unless ($inside_edit);
+
+ $self->current_edit->root->delete( $self->file_for( uuid => $args{uuid}, type => $args{type} ) );
+ $self->commit_edit() unless ($inside_edit);
+ return 1;
+}
+
+=head2 set_node_props { uuid => $uuid, type => $type, props => {hash of kv pairs }}
+
+
+Updates the record of type C<$type> with uuid C<$uuid> to set each property defined by the props hash. It does NOT alter any property not defined by the props hash.
+
+Manufactures its own current edit if none exists.
+
+=cut
+
+sub set_node_props {
+ my $self = shift;
+ my %args = validate( @_, { uuid => 1, props => 1, type => 1 } );
+
+ my $inside_edit = $self->current_edit ? 1 : 0;
+ $self->begin_edit() unless ($inside_edit);
+
+ my $file = $self->file_for( uuid => $args{uuid}, type => $args{'type'} );
+ $self->_set_node_props(
+ uuid => $args{uuid},
+ props => $args{props},
+ type => $args{'type'}
+ );
+ $self->commit_edit() unless ($inside_edit);
+
+}
+
+
+
+=head2 get_node_props {uuid => $uuid, type => $type, root => $root }
+
+Returns a hashref of all properties for the record of type $type with uuid C<$uuid>.
+
+'root' is an optional argument which you can use to pass in an alternate historical version of the replica to inspect. Code to look at the immediately previous version of a record might look like:
+
+ $handle->get_node_props(
+ type => $record->type,
+ uuid => $record->uuid,
+ root => $self->repo_handle->fs->revision_root( $self->repo_handle->fs->youngest_rev - 1 )
+ );
+
+
+=cut
+
+sub get_node_props {
+ my $self = shift;
+ my %args = validate( @_, { uuid => 1, type => 1, root => undef } );
+ my $root = $args{'root'} || $self->current_root;
+ return $root->node_proplist( $self->file_for( uuid => $args{'uuid'}, type => $args{'type'} ) );
+}
+
+
+
+
+
+
+=head2 file_for { uuid => $UUID, type => $type }
+
+Returns a file path within the repository (starting from the root)
+
+=cut
+
+sub file_for {
+ my $self = shift;
+ my %args = validate( @_, { uuid => 1, type => 1 } );
+ Carp::cluck unless $args{uuid};
+ my $file = join( "/", $self->directory_for_type( type => $args{'type'} ), $args{'uuid'} );
+ return $file;
+
+}
+
+sub directory_for_type {
+ my $self = shift;
+ my %args = validate( @_, { type => 1 } );
+ Carp::cluck unless defined $args{type};
+ return join( "/", $self->db_root, $args{'type'} );
+
+}
+
+
+
+=head2 node_exists {uuid => $uuid, type => $type, root => $root }
+
+Returns true if the node in question exists. False otherwise
+
+=cut
+
+sub node_exists {
+ my $self = shift;
+ my %args = validate( @_, { uuid => 1, type => 1, root => undef } );
+
+ my $root = $args{'root'} || $self->current_root;
+ return $root->check_path( $self->file_for( uuid => $args{'uuid'}, type => $args{'type'} ) );
+
+}
+
+sub type_exists {
+ my $self = shift;
+ my %args = validate( @_, { type => 1, root => undef } );
+
+ my $root = $args{'root'} || $self->current_root;
+ return $root->check_path( $self->directory_for_type( type => $args{'type'}, ) );
+
+}
+
+
+
+1;
+
+
+
+
Modified: SVN-PropDB/lib/Prophet/Record.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Record.pm (original)
+++ SVN-PropDB/lib/Prophet/Record.pm Sat Apr 5 17:28:42 2008
@@ -250,88 +250,6 @@
return 1;
}
-=head2 storage_node
-
-Returns the path of this node within the Prophet repository. (Really, it delegates this to L<Prophet::Handle/file_for>.
-
-=cut
-
-sub storage_node {
- my $self = shift;
- return $self->handle->file_for( type => $self->type, uuid => $self->uuid );
-}
-
-=head2 history
-
-Returns an array of L<Prophet::HistoryEntry> objects ordered from oldest to newest. It is important to note that Prophet's merge algorithms guarantee that _local_ record history will never be reordered but that different replicas will often have different history orderings based on when replicas were merged or synced.
-
-=cut
-
-sub history {
- my $self = shift;
- my $oldest_rev = 0;
- my @history;
- $self->handle->repo_handle->get_logs(
- [ $self->storage_node ],
- $self->handle->repo_handle->fs->youngest_rev,
- $oldest_rev, 1, 0, sub { $self->_history_entry_callback( \@history, @_ ) }
- );
- $self->_compute_history_deltas( \@history );
- return \@history;
-}
-
-sub _history_entry_callback {
- my $self = shift;
- my ( $accumulator, $paths, $rev, $author, $date, $msg ) = @_;
- my @nodes = keys %$paths;
- die "We should only have one node!" unless ( $#nodes == 0 );
-
- my $node = $paths->{ $nodes[0] };
- my $data = Prophet::HistoryEntry->new( handle => $self->handle );
-
- $data->rev($rev);
- $data->author($author);
- $data->date($date);
- $data->msg($msg);
- $data->action( $node->action() );
- $data->copy_from( $node->copyfrom_path() );
- $data->copy_from_rev( $node->copyfrom_rev() );
- $data->props( $self->handle->repo_handle->fs()->revision_root($rev)->node_proplist( $nodes[0] ) );
-
- push @$accumulator, $data;
-}
-
-sub _compute_history_deltas {
- my $self = shift;
- my $log_ref = shift;
- @$log_ref = reverse @$log_ref;
- my $last_props = {};
- for my $i ( 0 .. $#{$log_ref} ) {
-
- my $props = $log_ref->[$i]->props;
-
- for my $key ( keys %$props ) {
-
- if ( !exists $last_props->{$key} ) {
- $log_ref->[$i]->prop_changes->{$key}->{'add'} = $props->{$key};
- } elsif ( $last_props->{$key} ne $props->{$key} ) {
- $log_ref->[$i]->prop_changes->{$key}->{'add'} = $props->{$key};
- $log_ref->[$i]->prop_changes->{$key}->{'del'} = $last_props->{$key};
- }
- }
- foreach my $key ( keys %$last_props ) {
- if ( !exists $props->{$key} ) {
- $log_ref->[$i]->prop_changes->{$key}->{'del'} = $last_props->{$key};
- }
- }
-
- $last_props = $props;
- }
-
- return $log_ref;
-
-}
-
=head2 format_summary
returns a formated string that is the summary for the record.
More information about the Bps-public-commit
mailing list