[Bps-public-commit] Prophet branch, master, updated. cb4889de413f5497394c14ab7cfe893c9875ba63

jesse jesse at bestpractical.com
Tue Jul 7 16:35:24 EDT 2009


The branch, master has been updated
       via  cb4889de413f5497394c14ab7cfe893c9875ba63 (commit)
       via  0b8bf4fee509f4bffcfe086e4239499d647b83e3 (commit)
       via  35ff302990eeee5a6a7099b770e27fda9894ba03 (commit)
       via  6e48c78ac98acf607203a2fac0f3b5067ddc13db (commit)
       via  a7251c33620357a9be0efb748983363287ec6d9e (commit)
       via  1285e73dd8ea3e9c3afd6daeaf1b89054227a76c (commit)
       via  9d08f73fc23894f580ad19a6132bdfcc93247183 (commit)
       via  9f8fc6ab5d1cd04894878d7a834918dd4f080fb0 (commit)
       via  2b3047d5941bcd1f39276b6e5bdb79838b607fc4 (commit)
       via  fef857c0e3fa5010aa6e5ed75d44d1281b86e80f (commit)
       via  9ff21d8419a90cfa4a603319f9738ad849e97280 (commit)
       via  51ae9fe731fc99cb9570e531737d269ed28a9264 (commit)
       via  8defa13a5d4162cb9f95bf9046b570f4659cab7c (commit)
       via  4c7aba772f3f9d30ac6fe43569ceaed190aa709a (commit)
       via  64cc2ffd1ed6b5a907e3ae8cb4ef0e3d8e8e29d7 (commit)
       via  eb41224ca6bc47e1447b4b2fea44f181ebacaac8 (commit)
       via  00486b0b4111839cb2deb88f7ca4fda9cf1e1275 (commit)
       via  206525ef612f9e395c641cdc2f07d9a29e170a13 (commit)
       via  a1316914f448020702470436298912aebc683132 (commit)
       via  a2fc3bb4c194e6eedca05e870201173f0b7aebce (commit)
       via  6b13137a8ae91d0d444a1e000f1a25149699321b (commit)
       via  f66404963e5310b5916c94b9c3b43a0d09a4cce9 (commit)
       via  28fa29a29c9fdf8ea8e300526d6223d4a97a1378 (commit)
       via  3faa5b45b03e4b4dacd14269d1866fb35fa2477b (commit)
       via  a781ff5b1c9878aad5760d03dbd57bfb7c6b0a63 (commit)
       via  c25d49f1a372cc744814b3d7f6d29b0aabdd6c2a (commit)
       via  2d306e0d96a396254a2d0c3740de76dec0d89215 (commit)
       via  7c63419d11df4e39fcc278e82695e91c0893c0e7 (commit)
       via  7fde2443ca389c60ab8cb294854df61efd90e175 (commit)
       via  fd24abf70b083688a5f0349826541a8b80503c8a (commit)
       via  849abbb6f4826e24e2157ac0b5d62a2a17eacd7b (commit)
       via  032770b54b43a734cf4991d3663a642a383ed909 (commit)
       via  723b5f782014599fa53fdbc35f724b79161a3cd5 (commit)
       via  76646e0721480fdbb821b42c2805ca2e7a0c9e8d (commit)
       via  d5faf258c2229b64b731f983c6723d1247645c25 (commit)
       via  3d578d36f53fa5bd4ed96cff62d91781518e2422 (commit)
       via  19ee6b82d3a7a62ac08e937273987d2f3a790d9d (commit)
       via  ee0c8885e38cf9ee42f8f3ee85329e80d8dc14ff (commit)
       via  36397111f49b83c95351aab41c373e66c443b2ab (commit)
       via  b50d1d02112e12cf0b1c4881b6c1fba4ded1b7e7 (commit)
       via  6b4d60db12a6b7f8b228ce88b2b6266d26999464 (commit)
       via  8efc16d02ddba7765d7141b6d4d3d6b0cbe114fe (commit)
       via  b2284f04cd90756342fce0226ad82a2ab304af6f (commit)
       via  1020026b97d2aa3f2d142112c61ed1bf2f053d9a (commit)
       via  3e159946003d4802598dda6c53acfe06279abccb (commit)
       via  3480992a9efd08d4b64c5b94f823c27b13262f42 (commit)
       via  89937cf5d7b1e2ffedd8483a48cb149c7341f626 (commit)
       via  ffb88044d00175b46298aec71abce07a4c405b15 (commit)
       via  a65f6607f9268635493614b0e1bd3834ba211c49 (commit)
      from  64485be53f3ea769e1358ed49a5c75ea1d4f722a (commit)

Summary of changes:
 MANIFEST                                |    5 +
 MANIFEST.SKIP                           |    1 +
 Makefile.PL                             |    1 +
 lib/Prophet/App.pm                      |   10 +-
 lib/Prophet/CLI.pm                      |    7 +-
 lib/Prophet/CLI/Command.pm              |    5 +
 lib/Prophet/CLI/Command/Aliases.pm      |  210 ++++++++++++++--------
 lib/Prophet/CLI/Command/Clone.pm        |    8 +-
 lib/Prophet/CLI/Command/Config.pm       |  151 +++++++++++++---
 lib/Prophet/CLI/Command/Pull.pm         |   50 +++--
 lib/Prophet/CLI/Command/Shell.pm        |   12 ++-
 lib/Prophet/CLI/Dispatcher.pm           |   60 +++----
 lib/Prophet/CLI/TextEditorCommand.pm    |   33 +++-
 lib/Prophet/CLIContext.pm               |   54 +++++--
 lib/Prophet/Config.pm                   |  299 +++++++++++++++++--------------
 lib/Prophet/Record.pm                   |    5 +-
 lib/Prophet/Test.pm                     |   22 +++-
 lib/Prophet/Test/Editor.pm              |   97 ++++++++++
 t/Settings/t/database-settings-editor.t |   97 ++++++++++
 t/Settings/t/sync-database-settings.t   |    3 -
 t/aliases.t                             |  173 ++++++++++++++----
 t/cli-arg-parsing.t                     |   11 +-
 t/cli-arg-translation.t                 |    4 +-
 t/cli.t                                 |   19 ++-
 t/config.t                              |  108 ++++++++----
 t/data/aliases.tmpl                     |    9 +
 t/data/settings-first.tmpl              |   19 ++
 t/data/settings-second.tmpl             |   19 ++
 t/data/settings-third.tmpl              |   19 ++
 t/scripts/aliases-editor.pl             |   24 +++
 t/scripts/settings-editor.pl            |   51 ++++++
 t/test_app.conf                         |   14 +-
 t/testing.conf                          |    1 -
 33 files changed, 1184 insertions(+), 417 deletions(-)
 create mode 100644 lib/Prophet/Test/Editor.pm
 create mode 100644 t/Settings/t/database-settings-editor.t
 create mode 100644 t/data/aliases.tmpl
 create mode 100644 t/data/settings-first.tmpl
 create mode 100644 t/data/settings-second.tmpl
 create mode 100644 t/data/settings-third.tmpl
 create mode 100755 t/scripts/aliases-editor.pl
 create mode 100755 t/scripts/settings-editor.pl
 delete mode 100644 t/testing.conf

- Log -----------------------------------------------------------------
commit a65f6607f9268635493614b0e1bd3834ba211c49
Author: Christine Spang <spang at mit.edu>
Date:   Fri Jun 12 14:17:45 2009 +0300

    add dep on Config::GitLike

diff --git a/Makefile.PL b/Makefile.PL
index eaba2ce..6992289 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -21,6 +21,7 @@ requires( 'Mouse'         => '0.21' );
 requires('XML::Atom::SimpleFeed');
 requires( 'Path::Dispatcher' => '0.09' );    # Path::Dispatcher::Declarative
 requires('Time::Progress');
+requires('Config::GitLike' => '1.0');
 
 build_requires( 'Test::Exception' => '0.26' );
 build_requires( 'Test::Script::Run' => '0.02' );

commit ffb88044d00175b46298aec71abce07a4c405b15
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:23:59 2009 +0300

    Rip out old config system core and replace with Config::GitLike.

diff --git a/lib/Prophet/App.pm b/lib/Prophet/App.pm
index dda9fdd..ea8d726 100644
--- a/lib/Prophet/App.pm
+++ b/lib/Prophet/App.pm
@@ -12,7 +12,8 @@ has handle => (
     default => sub {
         my $self = shift;
 
-        if   ($ENV{'PROPHET_REPO'} !~ /^[\w\+]+\:/ ) {
+        if ( defined $ENV{'PROPHET_REPO'}
+                && $ENV{'PROPHET_REPO'} !~ /^[\w\+]+\:/ ) {
             my $path = $ENV{PROPHET_REPO};
             $path = File::Spec->rel2abs(glob($path)) unless File::Spec->file_name_is_absolute($path);
             $ENV{PROPHET_REPO} = "file://$path";
@@ -27,7 +28,10 @@ has config => (
     isa     => 'Prophet::Config',
     default => sub {
         my $self = shift;
-        return Prophet::Config->new(app_handle => $self);
+        return Prophet::Config->new(
+            app_handle => $self,
+            confname => 'prophetrc',
+        );
     },
     documentation => "This is the config instance for the running application",
 );
@@ -226,7 +230,7 @@ sub log_fatal {
 
 sub current_user_email {
     my $self = shift;
-    return $self->config->get('email_address') || $ENV{'PROPHET_EMAIL'} || $ENV{'EMAIL'};
+    return $self->config->get( key => 'user.email-address' ) || $ENV{'PROPHET_EMAIL'} || $ENV{'EMAIL'};
 
 }
 
diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index 44f287b..ba4040e 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -2,6 +2,7 @@ package Prophet::Config;
 use Any::Moose;
 use File::Spec;
 use Prophet::Util;
+extends 'Config::GitLike';
 
 has app_handle => (
     is => 'ro',
@@ -10,151 +11,86 @@ has app_handle => (
     required => 0
 );
 
-has config_files => ( 
-    is => 'rw',
-    isa => 'ArrayRef' ,
-    default =>sub  {[]}
-);
+# reload config after setting values
+after set => sub  {
+    my $self = shift;
 
-has config => (
-    is          => 'rw',
-    isa         => 'HashRef',
-    lazy        => 0,
-    default     => sub {shift->load_from_files;},
-);
+    $self->load;
+};
 
-sub get  { $_[0]->config->{$_[1]} }
-sub set  { $_[0]->config->{$_[1]} = $_[2] }
-sub list { keys %{ $_[0]->config } }
+# per-replica config filename
+override dir_file => sub { 'config' };
 
-sub aliases {
-    return $_[0]->config->{_aliases} || {};
-}
-
-
-sub sources {
-    return $_[0]->config->{_sources};
-}
+# Override the replica config file with the PROPHET_APP_CONFIG
+# env var if it's set. Also, don't walk up the given path if no replica
+# config is found.
+override load_dirs => sub {
+    my $self = shift;
 
+    $self->load_file( $self->replica_config_file )
+        if -f $self->replica_config_file;
+};
 
-sub app_config_file {
+# If PROPHET_APP_CONFIG is set, don't load anything else
+override user_file => sub {
     my $self = shift;
 
-    return $self->file_if_exists($ENV{'PROPHET_APP_CONFIG'})
-        || $self->file_if_exists( $self->replica_config_file)
-        || $self->file_if_exists( File::Spec->catfile( $ENV{'HOME'} => '.prophetrc' ))
-        || $self->replica_config_file
-}
+    return exists $ENV{PROPHET_APP_CONFIG} ? '' : $self->SUPER::user_file(@_);
+};
 
-sub replica_config_file {
+override global_file => sub {
     my $self = shift;
-     return 
-     $self->file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' )) ||
-     $self->file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'prophetrc' )) ||
-      File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' );
-}
 
+    return exists $ENV{PROPHET_APP_CONFIG} ? '' : $self->SUPER::global_file(@_);
+};
 
-sub load_from_files {
+# grab all values in the 'alias' section and strip away the section name
+sub aliases {
     my $self = shift;
-    my @config = @_;
-    @config = grep { -f $_ } $self->app_config_file if !@config;
-    my $config = {};
 
-    for my $file (@config) {
-        $self->load_from_file($file => $config);
-        push @{$self->config_files}, $file;
-    }
+    my %aliases = $self->get_regexp( key => '^alias\.' );
 
-    return $config;
-}
+    my %new_aliases = map {
+        my $alias = $_;
+        $alias =~ s/^alias\.//;
+        ( $alias => $aliases{$_} );
+    } keys %aliases;
 
-sub load_from_file {
-    my $self   = shift;
-    my $file   = shift;
-    my $config = shift || {};
-
-    for my $line (Prophet::Util->slurp($file) ) {
-        $line =~ s/\#.*$//; # strip comments
-        next unless ($line =~ /^(.*?)\s*=\s*(.*)$/);
-        my $key = $1;
-        my $val = $2;
-        if ($key =~ m!alias\s+(.+)!) {
-            $config->{_aliases}->{$1} = $val;
-        } elsif ($key =~ m!source\s+(.+)!) {
-            $config->{_sources}->{$1} = $val;
-        } else { 
-            $config->{$key} = $val;
-        }
-    }
-    $config->{_aliases} ||= {}; # default aliases is null.
-    $config->{_sources} ||= {}; # default to no sources.
+    return wantarray ? %new_aliases : \%new_aliases;
 }
 
-sub display_name_for_uuid {
+# grab all values in the 'source' section and strip away the section name
+sub sources {
     my $self = shift;
-    my $uuid = shift;
 
-    my $friendly = $self->get("display_$uuid");
-    return defined($friendly) ? $friendly : $uuid;
-}
+    my %sources = $self->get_regexp( key => '^source\.' );
 
-=head2 file_if_exists FILENAME
+    my %new_sources = map {
+        my $source = $_;
+        $source =~ s/^source\.//;
+        ( $source => $sources{$_} );
+    } keys %sources;
 
-Returns the given filename if it exists on the filesystem, and an
-empty string otherwise.
-
-=cut
+    return wantarray ? %new_sources : \%new_sources;
+}
 
-sub file_if_exists {
+sub replica_config_file {
     my $self = shift;
-    my $file = shift || ''; # quiet warnings
 
-    return (-e $file) ? $file : '';
+    return exists $ENV{PROPHET_APP_CONFIG} ? $ENV{PROPHET_APP_CONFIG}
+                : File::Spec->catfile(
+                    $self->app_handle->handle->fs_root, $self->dir_file
+    );
 }
 
-=head2 save FILENAME
-
-save the current config to file, if the file is not supplied,
-save to $self->app_config_file
-
-=cut
-
-#XXX TODO this won't save comments, which I think we should do.
-#in case of overwriting your file( you will hate me for that ), 
-#I chose to update alias and source lines only for now.
-
-sub save {
+# friendly replica names go in the [display] section
+sub display_name_for_uuid {
     my $self = shift;
-    my $file = shift || $self->app_config_file;
-
-    my @lines;
-    if ( $self->file_if_exists($file) ) {
-        @lines = Prophet::Util->slurp($file);
-    }
-
-    open my $fh, '>', $file or die "can't save config to $file: $!";
-    for my $line (@lines) {
-
-        # skip old aliases and sources
-        next if $line =~ /^ \s* (?:alias|source) \s+ .+ \s* = \s* .+/x;
-        print $fh $line;
-    }
-
-    if ( $self->sources ) {
-        for my $source ( keys %{ $self->sources } ) {
-            print $fh "source $source = " . $self->sources->{$source} . "\n";
-        }
-    }
-    if ( $self->aliases ) {
-        for my $alias ( keys %{ $self->aliases } ) {
-            print $fh "alias $alias = " . $self->aliases->{$alias} . "\n";
-        }
-    }
-    close $fh;
-    return 1;
-}
+    my $uuid = shift;
 
+    my $friendly = $self->get( key => "display.$uuid" );
+    return defined($friendly) ? $friendly : $uuid;
+}
 
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;

commit 89937cf5d7b1e2ffedd8483a48cb149c7341f626
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:26:15 2009 +0300

    By default, don't load any config files in tests.

diff --git a/lib/Prophet/Test.pm b/lib/Prophet/Test.pm
index ab62988..6e7dbca 100644
--- a/lib/Prophet/Test.pm
+++ b/lib/Prophet/Test.pm
@@ -23,6 +23,9 @@ our $REPO_BASE = File::Temp::tempdir();
 Test::More->import;
 diag( "Replicas can be found in $REPO_BASE" );
 
+# by default, load no configuration file
+$ENV{PROPHET_APP_CONFIG} = '';
+
 {
     no warnings 'redefine';
     require Test::More;

commit 3480992a9efd08d4b64c5b94f823c27b13262f42
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:27:10 2009 +0300

    Update summary format config key to new style.

diff --git a/lib/Prophet/Record.pm b/lib/Prophet/Record.pm
index 9ef1d6e..b0ed10d 100644
--- a/lib/Prophet/Record.pm
+++ b/lib/Prophet/Record.pm
@@ -641,8 +641,9 @@ L<_default_summary_format> if nothing better can be found.
 
 sub _summary_format {
     my $self = shift;
-    return $self->app_handle->config->get('summary_format_'.$self->type)
-        || $self->app_handle->config->get('default_summary_format')
+    return
+        $self->app_handle->config->get( key => $self->type.'.summary-format' )
+        || $self->app_handle->config->get( key => 'record.summary-format' )
         || $self->_default_summary_format;
 }
 

commit 3e159946003d4802598dda6c53acfe06279abccb
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:28:40 2009 +0300

    Update config command and its tests.

diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index ea61ad6..2f69352 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -13,38 +13,20 @@ sub run {
         print $self->no_config_files;
         return;
     }
+    print "Config files:\n\n";
     for my $file (@files) {
-        print "Config files:\n\n";
         print "$file\n";
     }
     print "\nYour configuration:\n\n";
-    for my $item ( $config->list ) {
-        if ( $item eq '_aliases' ) {
-            if ( my $aliases = $config->aliases ) {
-                for my $key ( keys %$aliases ) {
-                    print "alias $key = $aliases->{$key}\n";
-                }
-            }
-        }
-        elsif ( $item eq '_sources' ) {
-            if ( my $sources = $config->sources ) {
-                for my $key ( keys %$sources ) {
-                    print "source $key = $sources->{$key}\n";
-                }
-            }
-        }
-        else {
-            print $item . " = " . $config->get($item) . "\n";
-        }
-    }
+    $config->dump;
 }
 
 sub no_config_files {
     my $self = shift;
     return "No configuration files found. "
-         . " Either create a file called 'config' inside of "
-         . $self->handle->fs_root
-         . " or set the PROPHET_APP_CONFIG environment variable.\n\n";
+         . " Either create a file called
+         '".$self->handle->app_handle->config->replica_config_file.
+         "' or set the PROPHET_APP_CONFIG environment variable.\n\n";
 }
 
 __PACKAGE__->meta->make_immutable;
diff --git a/t/config.t b/t/config.t
index 47c64bd..a7ff1f4 100644
--- a/t/config.t
+++ b/t/config.t
@@ -2,51 +2,79 @@
 #
 use warnings;
 use strict;
-use Prophet::Test 'no_plan';
+use Prophet::Test tests => 11;
+use File::Copy;
 use File::Temp qw'tempdir';
-    $ENV{'PROPHET_REPO'} = tempdir( CLEANUP => ! $ENV{PROPHET_DEBUG}  ) . '/repo-' . $$;
-delete $ENV{'PROPHET_APP_CONFIG'};
 
-use_ok('Prophet::CLI');
-# Test basic config file parsing
-use_ok('Prophet::Config');
-my $config = Prophet::Config->new(app_handle => Prophet::CLI->new->app_handle);
-
-isa_ok($config => 'Prophet::Config');
-can_ok($config  => 'load_from_files');
+my $repo = $ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
 
-can_ok($config, 'get');
-can_ok($config, 'set');
-can_ok($config, 'list');
-can_ok($config, 'aliases');
+# since we don't initialize the db for these tests, make the repo dir
+mkdir $ENV{PROPHET_REPO};
 
-is($config->get('_does_not_exist'), undef);
-is($config->set('_does_not_exist' => 'hey you!'), 'hey you!');
-is($config->get('_does_not_exist'), 'hey you!');
-is_deeply([$config->list], ['_does_not_exist'], "The deep structures match");
+use_ok('Prophet::CLI');
 
 # load up a prophet app instance
 
-
 my $a = Prophet::CLI->new();
 can_ok($a, 'app_handle');
 can_ok($a->app_handle, 'config');
 my $c = $a->config;
 
+$c->load;
+
+is( $c->config_files->[0], undef, 'no config files loaded' );
+
 # interrogate its config to see if we have any config options set
-my @keys = $c->list;
-is (scalar @keys,0);
+my @keys = $c->dump;
+is( scalar @keys, 0, 'no config options are set' );
 
 # set a config file 
-{ local $ENV{'PROPHET_APP_CONFIG'} = 't/test_app.conf';
-my $conf = Prophet::Config->new(app_handle => Prophet::CLI->new->app_handle);
-# interrogate its config to see if we have any config options set
-my @keys = $conf->list;
-is (scalar @keys,4);
-# test the alias
-is($conf->aliases->{tlist}, "ticket list", "Got correct alias");
-}
+{
+    copy 't/test_app.conf', $repo;
+    local $ENV{'PROPHET_APP_CONFIG'}
+        = File::Spec->catfile($repo,'test_app.conf');
+
+    my $conf = Prophet::Config->new(
+        app_handle => Prophet::CLI->new->app_handle,
+        confname => 'testrc',
+    );
+    $conf->load;
+    # make sure we only have the one test config file loaded
+    is( length @{$conf->config_files}, 1, 'only test conf is loaded' );
+
+    # interrogate its config to see if we have any config options set
+    my @data = $conf->dump;
+    is( scalar @data, 6, '3 config options are set' );
+    # test the aliases sub
+    is( $conf->aliases->{tlist}, 'ticket list', 'Got correct alias' );
+    # test automatic reload after setting
+    $conf->set(
+        key => 'source.sd',
+        value => 'http://fsck.com/sd/',
+        filename => File::Spec->catfile($repo, 'test_app.conf'),
+    );
+    is( $conf->get( key => 'source.sd' ), 'http://fsck.com/sd/',
+        'automatic reload after set' );
+    # test the sources sub
+    is( $conf->sources->{sd}, 'http://fsck.com/sd/', 'Got correct alias' );
+
+    # run the cli "config" command
+    # make sure it matches with our file
+    my $got = run_command('config');
+    my $expect = <<EOF
+Configuration:
 
+Config files:
 
-# run the cli "show config" command 
-# make sure it matches with our file
+$repo/test_app.conf
+
+Your configuration:
+
+alias.tlist=ticket list
+source.sd=http://fsck.com/sd/
+test.foo=bar
+test.re=rawr
+EOF
+    ;
+    is($got, $expect, 'output of config command');
+}

commit 1020026b97d2aa3f2d142112c61ed1bf2f053d9a
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:28:59 2009 +0300

    Update test config file to new format.

diff --git a/t/test_app.conf b/t/test_app.conf
index 18daa28..1dd02bd 100644
--- a/t/test_app.conf
+++ b/t/test_app.conf
@@ -1,6 +1,8 @@
-#This is not a config directive
-foo=bar
-# nor is this
-re = rawr
-# This is an alias
-alias tlist = ticket list
+[test]
+    #This is not a config directive
+    foo=bar
+    # nor is this
+    re = rawr
+[alias]
+    # This is an alias
+    tlist = ticket list

commit b2284f04cd90756342fce0226ad82a2ab304af6f
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:29:31 2009 +0300

    Kill t/testing.conf which isn't used for anything.

diff --git a/t/testing.conf b/t/testing.conf
deleted file mode 100644
index de908b4..0000000
--- a/t/testing.conf
+++ /dev/null
@@ -1 +0,0 @@
-default_summary_format = %s,$uuid | %s,summary | %s,status

commit 8efc16d02ddba7765d7141b6d4d3d6b0cbe114fe
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:30:40 2009 +0300

    Update alias command and its tests for new config API.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 0875bbb..36f6cf1 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -11,6 +11,8 @@ sub run {
     my $self     = shift;
     my $template = $self->make_template;
 
+    my $config = $self->app_handle->config;
+
     if ( $self->context->has_arg('show') ) {
         print $template. "\n";
         return;
@@ -22,17 +24,18 @@ sub run {
     }
 
     if ( $self->has_arg('set') || $self->has_arg('delete') ) {
-        my $aliases = $self->app_handle->config->aliases;
-        my $need_to_save;
 
         if ( $self->has_arg('set') ) {
             my $value = $self->arg('set');
             if ( $value =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) {
-                if ( exists $aliases->{$1} ) {
-                    if ( $aliases->{$1} ne $2 ) {
-                        my $old = $aliases->{$1};
-                        $aliases->{$1} = $2;
-                        $need_to_save = 1;
+                my $old = $config->get( key => "alias.$1" );
+                if ( defined $old ) {
+                    if ( $old ne $2 ) {
+                        $config->set(
+                            key => "alias.$1",
+                            value => $2,
+                            filename => $config->replica_config_file,
+                        );
                         print
                           "changed alias '$1' from '$old' to '$2'\n";
                     }
@@ -41,27 +44,32 @@ sub run {
                     }
                 }
                 else {
-                    $need_to_save = 1;
-                    $aliases->{$1} = $2;
+                    $config->set(
+                        key => "alias.$1",
+                        value => $2,
+                        filename => $config->replica_config_file,
+                    );
                     print "added alias '$1 = $2'\n";
                 }
             }
         }
         elsif ( $self->has_arg('delete') ) {
             my $key = $self->arg('delete');
-            if ( exists $aliases->{$key} ) {
-                $need_to_save = 1;
-                print "deleted alias '$key = $aliases->{$key}'\n";
-                delete $aliases->{$key};
+
+            if ( defined $config->get( key => "alias.$key" ) ) {
+                print "deleted alias '$key = "
+                      .$config->get( key => "alias.$key" )."'\n";
+
+                $config->set(
+                    key => "alias.$key",
+                    filename => $config->replica_config_file,
+                );
             }
             else {
                 print "didn't find alias '$key'\n";
             }
         }
 
-        if ($need_to_save) {
-            $self->app_handle->config->save;
-        }
     }
     else {
 
@@ -71,8 +79,6 @@ sub run {
             $done = $self->try_to_edit( template => \$template );
         }
     }
-
-
 }
 
 sub make_template {
@@ -117,27 +123,43 @@ sub process_template {
     my ($config) = $self->parse_template($updated);
 
     my $aliases = $self->app_handle->config->aliases;
+    my $c = $self->app_handle->config;
+
+    my @added = grep { !$aliases->{$_} } sort keys %$config;
 
-    my @added = grep { ! $aliases->{$_} } sort keys %$config;
     my @changed =
-      grep { $config->{$_} && $aliases->{$_} ne $config->{$_} } sort keys %$aliases;
+      grep { $config->{$_} && $aliases->{$_} ne $config->{$_}
+      } sort keys %$aliases;
+
     my @deleted = grep { !$config->{$_} } sort keys %$aliases;
 
+    # TODO: 'set' all at once after implementing hash sets
     for my $add ( @added ) {
         print 'Added alias ' . "'$add' = '$config->{$add}'\n";
+        $c->set(
+            key => "alias.$add",
+            value => $config->{$add},
+            filename => $c->replica_config_file,
+        );
     }
 
     for my $change (@changed) {
         print 'Changed alias ' . "'$change' from '$aliases->{$change}' to '$config->{$change}'\n";
+        $c->set(
+            key => "alias.$change",
+            value => $config->{$change},
+            filename => $c->replica_config_file,
+        );
     }
 
     for my $delete ( @deleted ) {
         print "Deleted alias '$delete'\n";
+        $c->set(
+            key => "alias.$delete",
+            filename => $c->replica_config_file,
+        );
     }
 
-    $self->app_handle->config->set(_aliases => $config );
-    $self->app_handle->config->save;
-
     return 1;
 }
 
diff --git a/t/aliases.t b/t/aliases.t
index ae007e2..a7ac708 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -3,16 +3,22 @@
 use warnings;
 use strict;
 use Prophet::Test 'no_plan';
-use File::Temp qw/tempfile/;
+use File::Temp qw/tempdir tempfile/;
 
-$ENV{'PROPHET_APP_CONFIG'} = (tempfile(UNLINK => 1))[1];
+$ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
+$ENV{'PROPHET_APP_CONFIG'} = (tempfile(UNLINK => !$ENV{PROPHET_DEBUG}))[1];
+diag("Using config file $ENV{PROPHET_APP_CONFIG}");
+
+# since we don't initialize the db for these tests, make the repo dir
+mkdir $ENV{PROPHET_REPO};
 
 use_ok('Prophet::CLI');
 use_ok('Prophet::Config');
-my $aliases = Prophet::Config->new(app_handle =>
-        Prophet::CLI->new->app_handle)->aliases;
 
-is_deeply( $aliases, {}, 'initial alias is empty' );
+my $config = Prophet::CLI->new()->config;
+$config->load;
+
+is_deeply( scalar $config->aliases, {}, 'initial alias is empty' );
 
 my @cmds = (
     {
@@ -47,7 +53,7 @@ my @cmds = (
     {
         cmd => [ '--add', 'pull -a=pull --all' ],
         output  => qr/added alias 'pull -a = pull --all/,
-        comment => 'readd a new alias',
+        comment => 'read a new alias',
     },
     {
         cmd => [ '--add', 'pull -l=pull --local' ],
@@ -63,8 +69,11 @@ for my $item ( @cmds ) {
 
 
 # check aliases in config
-$aliases = Prophet::Config->new(app_handle =>
-        Prophet::CLI->new->app_handle)->aliases;
+my $aliases = Prophet::Config->new(
+    app_handle => Prophet::CLI->new->app_handle,
+    confname => 'testrc'
+)->aliases;
+
 is_deeply(
     $aliases,
     {
@@ -80,7 +89,10 @@ open my $fh, '<', $ENV{'PROPHET_APP_CONFIG'}
   or die "failed to open $ENV{'PROPHET_APP_CONFIG'}: $!";
 { local $/; $content = <$fh>; }
 is( $content, <<EOF, 'content in config' );
-alias pull -l = pull --local
-alias pull -a = pull --all
+
+[alias]
+	pull -a = pull --all
+	pull -l = pull --local
 EOF
 
+# TODO: need tests for interactive alias editing

commit 6b4d60db12a6b7f8b228ce88b2b6266d26999464
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:32:31 2009 +0300

    Update pull command to use new config API.

diff --git a/lib/Prophet/CLI/Command/Pull.pm b/lib/Prophet/CLI/Command/Pull.pm
index c79bc3f..dec663e 100644
--- a/lib/Prophet/CLI/Command/Pull.pm
+++ b/lib/Prophet/CLI/Command/Pull.pm
@@ -12,21 +12,18 @@ sub run {
 
     my $previous_sources = $self->app_handle->config->sources;
 
-
     my $explicit_from = '';
-    
+
     if ($self->has_arg('from')) {
         $explicit_from = $self->arg('from') ;
         push @from, $explicit_from;
     }
-
     elsif ($self->has_arg('all')){
         for my $source (values %$previous_sources) {
             my ($url, $uuid ) = split(qr/ \| /,$source,2);
             push @from, $url;
 
         }
-
     }
 
     $self->validate_args;
@@ -49,17 +46,19 @@ sub record_pull_from_source {
     my $self = shift;
     my $source = shift;
     my $from_uuid = shift;
-    my $previous_sources = $self->app_handle->config->sources;
+    my %previous_sources = $self->app_handle->config->sources;
     my %sources_by_url = map {
-            my $meta = $_;
-            my ($url,$uuid);
-            ($url, $uuid ) = split(qr/ \| /,$meta,2);
-         ($previous_sources->{$meta} => $url )
-     } keys %$previous_sources;
+            my $name = $_;
+            my ($url, $uuid);
+            ($url, $uuid ) = split(qr/ \| /, $previous_sources{$name}, 2);
+         ($url => $uuid )
+     } keys %previous_sources;
     if ( !exists $sources_by_url{$source}) {
-        $previous_sources->{$source} = $source ." | ".$from_uuid;
-        $self->app_handle->config->set(_sources => $previous_sources );
-        $self->app_handle->config->save;
+        $self->app_handle->config->set(
+            key => "source.'$source'",
+            value => "$source | $from_uuid",
+            filename => $self->app_handle->config->replica_config_file,
+        );
     }
 }
 

commit b50d1d02112e12cf0b1c4881b6c1fba4ded1b7e7
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 16 13:33:00 2009 +0300

    Update clone command to use new config API.

diff --git a/lib/Prophet/CLI/Command/Clone.pm b/lib/Prophet/CLI/Command/Clone.pm
index f729b64..fa87866 100644
--- a/lib/Prophet/CLI/Command/Clone.pm
+++ b/lib/Prophet/CLI/Command/Clone.pm
@@ -42,8 +42,12 @@ sub run {
     }
 
     $target->initialize(%init_args);
-    $self->app_handle->config->set( _sources => { $self->arg('from') => $self->arg('from') });
-    $self->app_handle->config->save;
+
+    $self->app_handle->config->set(
+        key => 'source.'.$self->arg('from'),
+        value => $self->arg('from'),
+        filename => $self->app_handle->config->replica_config_file,
+    );
 
     if ( $source->can('database_settings') ) {
         my $remote_db_settings = $source->database_settings;

commit 36397111f49b83c95351aab41c373e66c443b2ab
Author: Christine Spang <spang at mit.edu>
Date:   Thu Jun 18 15:23:31 2009 +0300

    Update aliases command to use group_set.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 36f6cf1..b9b7230 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -72,7 +72,6 @@ sub run {
 
     }
     else {
-
         my $done = 0;
 
         while ( !$done ) {
@@ -86,7 +85,7 @@ sub make_template {
 
     my $content = '';
    
-    $content .= "# Format: alias new_cmd = cmd\n"
+    $content .= "# Format: new_cmd = cmd\n"
       unless $self->context->has_arg('show');
 
     # get all settings records
@@ -94,7 +93,7 @@ sub make_template {
 
     if ( $aliases ) {
         for my $key ( keys %$aliases ) {
-            $content .= "alias $key = $aliases->{$key}\n";
+            $content .= "$key = $aliases->{$key}\n";
         }
     }
 
@@ -107,7 +106,7 @@ sub parse_template {
 
     my %parsed;
     for my $line ( split( /\n/, $template ) ) {
-        if ( $line =~ /^\s*alias\s+(.+?)\s*=\s*(.+?)\s*$/ ) {
+        if ( $line =~ /^\s*([^#].*?)\s*=\s*(.+?)\s*$/ ) {
             $parsed{$1} = $2;
         }
     }
@@ -133,34 +132,48 @@ sub process_template {
 
     my @deleted = grep { !$config->{$_} } sort keys %$aliases;
 
-    # TODO: 'set' all at once after implementing hash sets
-    for my $add ( @added ) {
-        print 'Added alias ' . "'$add' = '$config->{$add}'\n";
-        $c->set(
-            key => "alias.$add",
-            value => $config->{$add},
-            filename => $c->replica_config_file,
+    # attempt to set all added/changed/deleted aliases at once
+    my @to_set = (
+        (map { { key => "alias.'$_'", value => $config->{$_} } }
+            (@added, @changed)),
+        (map { { key => "alias.'$_'" } } @deleted),
+    );
+
+    eval {
+        $c->group_set(
+            $c->replica_config_file,
+            \@to_set,
         );
-    }
-
-    for my $change (@changed) {
-        print 'Changed alias ' . "'$change' from '$aliases->{$change}' to '$config->{$change}'\n";
-        $c->set(
-            key => "alias.$change",
-            value => $config->{$change},
-            filename => $c->replica_config_file,
+    };
+    # if we fail, prompt the user to re-edit
+    # TODO: this doesn't really work correctly.
+    # Also, handle_template_errors gives messages that are very
+    # much tailored towards SD's ticket editing facility.
+    # Should genericise that.
+    if ($@) {
+        warn $@;
+        return $self->handle_template_errors(
+            rtype => 'aliases',
+            template_ref => $args{template},
+            bad_template => $args{edited},
+            error => "$@",
         );
     }
+    # otherwise, print out what changed and return happily
+    else {
+        for my $add ( @added ) {
+            print 'Added alias ' . "'$add' = '$config->{$add}'\n";
+        }
+        for my $change (@changed) {
+            print "Changed alias '$change' from '$aliases->{$change}'"
+                  ."to '$config->{$change}'\n";
+        }
+        for my $delete ( @deleted ) {
+            print "Deleted alias '$delete'\n";
+        }
 
-    for my $delete ( @deleted ) {
-        print "Deleted alias '$delete'\n";
-        $c->set(
-            key => "alias.$delete",
-            filename => $c->replica_config_file,
-        );
+        return 1;
     }
-
-    return 1;
 }
 
 __PACKAGE__->meta->make_immutable;

commit ee0c8885e38cf9ee42f8f3ee85329e80d8dc14ff
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 10:22:32 2009 +0300

    Initial --user/--global flags for editing aliases

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index b9b7230..416e430 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -5,6 +5,15 @@ use Params::Validate qw/validate/;
 extends 'Prophet::CLI::Command';
 with 'Prophet::CLI::TextEditorCommand';
 
+has config_filename => (
+    is => 'rw',
+    isa => 'Str',
+    lazy => 1,
+    default => sub {
+        $_[0]->app_handle->config->replica_config_file;
+    },
+);
+
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  a => 'add', d => 'delete', s => 'show' };
 
 sub run {
@@ -18,6 +27,13 @@ sub run {
         return;
     }
 
+    if ($self->has_arg('global')) {
+        $self->config_filename($config->global_file);
+    }
+    elsif ($self->has_arg('user')) {
+        $self->config_filename($config->user_file);
+    }
+
     # --add is the same as --set
     if ( $self->context->has_arg('add') ) {
         $self->context->set_arg('set', $self->arg('add') )
@@ -34,7 +50,7 @@ sub run {
                         $config->set(
                             key => "alias.$1",
                             value => $2,
-                            filename => $config->replica_config_file,
+                            filename => $self->config_filename,
                         );
                         print
                           "changed alias '$1' from '$old' to '$2'\n";
@@ -47,7 +63,7 @@ sub run {
                     $config->set(
                         key => "alias.$1",
                         value => $2,
-                        filename => $config->replica_config_file,
+                        filename => $self->config_filename,
                     );
                     print "added alias '$1 = $2'\n";
                 }
@@ -62,7 +78,7 @@ sub run {
 
                 $config->set(
                     key => "alias.$key",
-                    filename => $config->replica_config_file,
+                    filename => $self->config_filename,
                 );
             }
             else {
@@ -141,7 +157,7 @@ sub process_template {
 
     eval {
         $c->group_set(
-            $c->replica_config_file,
+            $self->config_filename,
             \@to_set,
         );
     };

commit 19ee6b82d3a7a62ac08e937273987d2f3a790d9d
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 10:23:39 2009 +0300

    Update Prophet::Config pod a bit

diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index ba4040e..a15d409 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -8,7 +8,7 @@ has app_handle => (
     is => 'ro',
     weak_ref => 1,
     isa => 'Prophet::App',
-    required => 0
+    required => 1
 );
 
 # reload config after setting values
@@ -105,54 +105,74 @@ Prophet::Config
 
 =head1 SYNOPSIS
 
-    In the Prophet config file (see L</app_config_file>):
+From, for example, a class that inherits from Prophet::App:
+
+    has config => (
+        is      => 'rw',
+        isa     => 'Prophet::Config',
+        default => sub {
+            my $self = shift;
+            return Prophet::Config->new(
+                app_handle => $self,
+                confname => 'prophetrc',
+            );
+        },
+    );
 
-      prefer_luids: 1
-      summary_format_ticket = %4s },$luid | %-11.11s,status | %-70.70s,summary
 
 =head1 DESCRIPTION
 
 This class represents the configuration of Prophet and the application built on
-top of it.
+top of it. It's just an instance of L<Config::GitLike|Config::GitLike> with
+a few small customizations and additions.
 
 =head1 METHODS
 
-=head2 new
-
-Takes no arguments. Automatically loads the config for you.
+=head2 new( confname => 'prophetrc', app_handle => $instance_of_prophet_app )
 
-=cut
+Initialize the configuration. Does NOT load the config for you! You need to
+call L<load|Config::GitLike/"load"> for that. The configuration will also
+load automatically the first time your prophet application tries to
+L<get|Config::GitLike/"get"> a config variable.
 
-=head2 app_config_file
+Both constructor arguments are required.
 
-The file which controls configuration for this application
-(the $PROPHET_APP_CONFIG environmental variable, C<$PROPHET_REPO/config>,
-or C<$HOME/.prophetrc>, in that order).
+=head2 replica_config_file
 
-=head2 load_from_files [files]
+The replica-specific configuration file, or the configuration file given
+by C<PROPHET_APP_CONFIG> if that environmental variable is set.
 
-Loads the given config files. If no files are passed in, it will use the
-default of L</app_config_file>.
+=head2 aliases
 
-=head2 load_from_file file
+A convenience method that gets you a hash (or a hashref, depending on context)
+of all currently defined aliases. (Basically, every entry in the 'alias'
+section of the config file.)
 
-Loads the given config file.
+=head2 sources
 
-=head2 get
+A convenience method that gets you a hash (or a hashref, depending on context)
+of all currently defined source replicas, in the format { 'name' =>
+{ url => 'URL', uuid => 'UUID } }. (Basically, every entry in the 'replica'
+section of the config file.)
 
-Gets a specific config setting.
-
-=head2 set
+=head2 display_name_for_uuid UUID
 
-Sets a specific config setting.
+Returns a "friendly" id for the given uuid.
 
-=head2 list
+TODO: regexp search for 'replica.(.*).UUID' and extract the section
 
-Lists all configuration options.
+=head1 CONFIG VARIABLES
 
-=head2 display_name_for_uuid UUID
+The following config variables are currently used in various places in
+Prophet:
 
-Returns a "friendly" id for the given uuid.
+<record-type>.summary-format
+record.summary-format
+user.email-address
+alias.<alias>
 
-=cut
+=head1 SEE ALSO
 
+Most of the useful methods for getting and setting configuration variables
+actually come from L<Config::GitLike|Config::GitLike>. See that module's
+documentation for details.

commit 3d578d36f53fa5bd4ed96cff62d91781518e2422
Merge: 19ee6b8... e7b9d4e...
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 10:31:10 2009 +0300

    Merge back master since sunnavy did some work on aliases and I want to make sure I'm not creating crazy conflicts

diff --cc lib/Prophet/CLI/Command/Aliases.pm
index 416e430,b5f3330..27d7b3d
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@@ -27,14 -14,7 +27,14 @@@ sub run 
          return;
      }
  
 +    if ($self->has_arg('global')) {
 +        $self->config_filename($config->global_file);
 +    }
 +    elsif ($self->has_arg('user')) {
 +        $self->config_filename($config->user_file);
 +    }
 +
-     # --add is the same as --set
+     # add is the same as set
      if ( $self->context->has_arg('add') ) {
          $self->context->set_arg('set', $self->arg('add') )
      }
diff --cc t/aliases.t
index a7ac708,44493b9..9aa3223
--- a/t/aliases.t
+++ b/t/aliases.t
@@@ -2,15 -2,10 +2,15 @@@
  #
  use warnings;
  use strict;
- use Prophet::Test 'no_plan';
- use File::Temp qw/tempdir tempfile/;
+ use Prophet::Test tests => 19;
+ use File::Temp qw/tempfile/;
  
 -$ENV{'PROPHET_APP_CONFIG'} = (tempfile(UNLINK => 1))[1];
 +$ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
 +$ENV{'PROPHET_APP_CONFIG'} = (tempfile(UNLINK => !$ENV{PROPHET_DEBUG}))[1];
 +diag("Using config file $ENV{PROPHET_APP_CONFIG}");
 +
 +# since we don't initialize the db for these tests, make the repo dir
 +mkdir $ENV{PROPHET_REPO};
  
  use_ok('Prophet::CLI');
  use_ok('Prophet::Config');
@@@ -51,12 -51,12 +57,12 @@@ my @cmds = 
          comment => q{delete an alias that doesn't exist any more},
      },
      {
-         cmd => [ '--add', 'pull -a=pull --all' ],
+         cmd => [ 'add', 'pull -a=pull --all' ],
          output  => qr/added alias 'pull -a = pull --all/,
 -        comment => 'readd a new alias',
 +        comment => 'read a new alias',
      },
      {
-         cmd => [ '--add', 'pull -l=pull --local' ],
+         cmd => [ 'add', 'pull -l=pull --local' ],
          output  => qr/added alias 'pull -l = pull --local/,
          comment => 'add a new alias',
      },
@@@ -89,10 -117,8 +126,16 @@@ open my $fh, '<', $ENV{'PROPHET_APP_CON
    or die "failed to open $ENV{'PROPHET_APP_CONFIG'}: $!";
  { local $/; $content = <$fh>; }
  is( $content, <<EOF, 'content in config' );
++<<<<<<< HEAD:t/aliases.t
 +
 +[alias]
 +	pull -a = pull --all
 +	pull -l = pull --local
++=======
+ alias pull -l = pull --local
+ alias foo bar = bar baz
+ alias pull -a = pull --all
++>>>>>>> master:t/aliases.t
  EOF
  
 +# TODO: need tests for interactive alias editing

commit d5faf258c2229b64b731f983c6723d1247645c25
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 10:40:17 2009 +0300

    More informative 'sd aliases --show'.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 27d7b3d..bb2e6ce 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -101,8 +101,9 @@ sub make_template {
 
     my $content = '';
    
-    $content .= "# Format: new_cmd = cmd\n"
-      unless $self->context->has_arg('show');
+    $content .= $self->context->has_arg('show') ?
+        "Active aliases for the current repository (including user-wide and global\naliases if not overridden):\n\n"
+        : "# Format: new_cmd = cmd\n";
 
     # get all settings records
     my $aliases = $self->app_handle->config->aliases;

commit 76646e0721480fdbb821b42c2805ca2e7a0c9e8d
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 11:21:03 2009 +0300

    sd aliases displays by default. sd aliases edit edits.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index bb2e6ce..e6748df 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -22,11 +22,6 @@ sub run {
 
     my $config = $self->app_handle->config;
 
-    if ( $self->context->has_arg('show') ) {
-        print $template. "\n";
-        return;
-    }
-
     if ($self->has_arg('global')) {
         $self->config_filename($config->global_file);
     }
@@ -87,13 +82,18 @@ sub run {
         }
 
     }
-    else {
+    elsif ( $self->has_arg('edit') ) {
         my $done = 0;
 
         while ( !$done ) {
             $done = $self->try_to_edit( template => \$template );
         }
     }
+    else {
+        print $template. "\n";
+        return;
+    }
+
 }
 
 sub make_template {
@@ -101,9 +101,9 @@ sub make_template {
 
     my $content = '';
    
-    $content .= $self->context->has_arg('show') ?
-        "Active aliases for the current repository (including user-wide and global\naliases if not overridden):\n\n"
-        : "# Format: new_cmd = cmd\n";
+    $content .= $self->context->has_arg('edit') ?
+        "# Format: new_cmd = cmd\n"
+        : "Active aliases for the current repository (including user-wide and global\naliases if not overridden):\n\n";
 
     # get all settings records
     my $aliases = $self->app_handle->config->aliases;
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index 9332d56..b868f95 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -47,6 +47,7 @@ on [ ['show', 'display'] ]       => run_command("Show");
 on [ ['update', 'edit'] ]        => run_command("Update");
 on [ ['delete', 'del', 'rm'] ]   => run_command("Delete");
 on [ ['search', 'list', 'ls' ] ] => run_command("Search");
+on [ ['aliases', 'alias'] ]      => run_command('Aliases');
 
 on version  => run_command("Version");
 on init     => run_command("Init");
@@ -62,7 +63,7 @@ on log      => run_command("Log");
 on shell    => run_command("Shell");
 on export   => run_command('Export');
 on info     => run_command('Info');
-on aliases     => run_command('Aliases');
+on history  => run_command('History');
 
 on push => sub {
     my $self = shift;
@@ -89,14 +90,15 @@ on qr/^alias(?:es)?\s*(.*)/ => sub {
     elsif ( $arg =~ /=/ ) {
         $self->context->set_arg(set => $arg);
     }
+    elsif ( $arg =~ /^edit\b/ ) {
+        $self->context->set_arg(edit => 1);
+    }
     else {
         die 'no idea what you mean, sorry';
     }
     run( 'aliases', $self, @_ );
 };
 
-on history => run_command('History');
-
 sub run_command {
     my $name = shift;
     return sub {

commit 723b5f782014599fa53fdbc35f724b79161a3cd5
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 13:16:23 2009 +0300

    Finish --user and --global flags for editing aliases.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index e6748df..f3dc249 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -18,7 +18,6 @@ sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  a => 'add', d => 'dele
 
 sub run {
     my $self     = shift;
-    my $template = $self->make_template;
 
     my $config = $self->app_handle->config;
 
@@ -29,6 +28,8 @@ sub run {
         $self->config_filename($config->user_file);
     }
 
+    my $template = $self->make_template;
+
     # add is the same as set
     if ( $self->context->has_arg('add') ) {
         $self->context->set_arg('set', $self->arg('add') )
@@ -102,11 +103,16 @@ sub make_template {
     my $content = '';
    
     $content .= $self->context->has_arg('edit') ?
-        "# Format: new_cmd = cmd\n"
-        : "Active aliases for the current repository (including user-wide and global\naliases if not overridden):\n\n";
+        "# Editing aliases in config file ".$self->config_filename."\n\n"
+        ."# Format: new_cmd = cmd\n"
+        : "Active aliases for the current repository (including user-wide and"
+        ." global\naliases if not overridden):\n\n";
 
-    # get all settings records
-    my $aliases = $self->app_handle->config->aliases;
+    # get aliases from the config file we're going to edit, or all of them if
+    # we're just displaying
+    my $aliases = $self->has_arg('edit') ?
+                  $self->app_handle->config->aliases( $self->config_filename )
+                : $self->app_handle->config->aliases;
 
     if ( $aliases ) {
         for my $key ( keys %$aliases ) {
@@ -138,7 +144,7 @@ sub process_template {
     my $updated = $args{edited};
     my ($config) = $self->parse_template($updated);
 
-    my $aliases = $self->app_handle->config->aliases;
+    my $aliases = $self->app_handle->config->aliases( $self->config_filename );
     my $c = $self->app_handle->config;
 
     my @added = grep { !$aliases->{$_} } sort keys %$config;
diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index a15d409..5dd7a68 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -47,14 +47,43 @@ override global_file => sub {
 # grab all values in the 'alias' section and strip away the section name
 sub aliases {
     my $self = shift;
-
-    my %aliases = $self->get_regexp( key => '^alias\.' );
-
-    my %new_aliases = map {
-        my $alias = $_;
-        $alias =~ s/^alias\.//;
-        ( $alias => $aliases{$_} );
-    } keys %aliases;
+    my $file = shift;
+
+    my %new_aliases;
+    if ( $file ) {
+        # parse the given config file with parse_content and use the
+        # callbacks to add to an array
+        my $content = Prophet::Util->slurp( $file );
+        $self->parse_content(
+            content => $content,
+            callback => sub {
+                my %args = @_;
+                return unless defined $args{name};
+                if ( $args{section} eq 'alias' ) {
+                    $new_aliases{$args{name}} = $args{value};
+                }
+            },
+            # Most of the time this error sub won't get triggered since
+            # Prophet loads the config file whenever it first tries to use
+            # a value from the config file, and errors are detected at that
+            # point. This always happens before this since every command
+            # triggers alias processing. So this should really only explode
+            # if we're running a shell and the config file has changed
+            # in a bad way since we started up.
+            error => sub {
+                Config::GitLike::error_callback( @_, filename => $file );
+            },
+        );
+    }
+    else {
+        my %aliases = $self->get_regexp( key => '^alias\.' );
+
+        %new_aliases = map {
+            my $alias = $_;
+            $alias =~ s/^alias\.//;
+            ( $alias => $aliases{$_} );
+        } keys %aliases;
+    }
 
     return wantarray ? %new_aliases : \%new_aliases;
 }
@@ -142,12 +171,15 @@ Both constructor arguments are required.
 The replica-specific configuration file, or the configuration file given
 by C<PROPHET_APP_CONFIG> if that environmental variable is set.
 
-=head2 aliases
+=head2 aliases( $config_filename )
 
 A convenience method that gets you a hash (or a hashref, depending on context)
 of all currently defined aliases. (Basically, every entry in the 'alias'
 section of the config file.)
 
+If a filename is passed in, this method will only return the aliases that
+are defined in that particular config file.
+
 =head2 sources
 
 A convenience method that gets you a hash (or a hashref, depending on context)

commit 032770b54b43a734cf4991d3663a642a383ed909
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 14:53:49 2009 +0300

    Don't interpret anything in a command alias as a regex metacharacter.

diff --git a/lib/Prophet/CLI.pm b/lib/Prophet/CLI.pm
index f06a0a2..cbf0e75 100644
--- a/lib/Prophet/CLI.pm
+++ b/lib/Prophet/CLI.pm
@@ -114,7 +114,7 @@ sub _command_matches_alias {
     my $cmd   = shift;
     my $alias = shift;
     my $dispatch_to = shift;;
-    if ( $cmd =~ /^$alias\s*(.*)$/ ) {
+    if ( $cmd =~ /^\Q$alias\E\s*(.*)$/ ) {
         no strict 'refs';
 
         my $rest = $1;

commit 849abbb6f4826e24e2157ac0b5d62a2a17eacd7b
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 15:11:17 2009 +0300

    Update aliases.t for new config API (again)

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index f3dc249..56a707b 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -114,11 +114,15 @@ sub make_template {
                   $self->app_handle->config->aliases( $self->config_filename )
                 : $self->app_handle->config->aliases;
 
-    if ( $aliases ) {
+    if ( %$aliases ) {
         for my $key ( keys %$aliases ) {
             $content .= "$key = $aliases->{$key}\n";
         }
     }
+    else {
+        $content = "No aliases for the current repository.\n";
+    }
+
 
     return $content;
 }
@@ -169,6 +173,10 @@ sub process_template {
         );
     };
     # if we fail, prompt the user to re-edit
+
+    # one of the few ways to trigger this is to try to set a variable
+    # that starts with a [ character
+
     # TODO: this doesn't really work correctly.
     # Also, handle_template_errors gives messages that are very
     # much tailored towards SD's ticket editing facility.
diff --git a/t/aliases.t b/t/aliases.t
index 9aa3223..57859b3 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -2,7 +2,7 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 19;
+use Prophet::Test tests => 18;
 use File::Temp qw/tempfile/;
 
 $ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
@@ -23,7 +23,7 @@ is_deeply( scalar $config->aliases, {}, 'initial alias is empty' );
 my @cmds = (
     {
         cmd => [ 'show' ],
-        output  => qr/^\s*$/,
+        output  => qr/^No aliases for the current repository.\n$/,
         comment => 'show empty aliases',
     },
 
@@ -68,12 +68,7 @@ my @cmds = (
     },
     {
         cmd => [ 'show' ],
-        output  => qr/alias pull -a = pull --all/s,
-        comment => 'show',
-    },
-    {
-        cmd => [ 'show' ],
-        output  => qr/alias pull -l = pull --local/s,
+        output  => qr/Active aliases for the current repository \(including user-wide and global\naliases if not overridden\):\n\npull -l = pull --local\npull -a = pull --all/s,
         comment => 'show',
     },
     {
@@ -84,7 +79,7 @@ my @cmds = (
     {
         cmd => [ 'foo', 'bar', '=', 'bar',  'baz' ],
         output  => qr/alias 'foo bar = bar baz' isn't changed, won't update/,
-        comment => 'readd alias foo bar',
+        comment => 'read alias foo bar',
     },
     {
         cmd => [ 'delete', 'foo', 'bar' ],
@@ -126,16 +121,11 @@ open my $fh, '<', $ENV{'PROPHET_APP_CONFIG'}
   or die "failed to open $ENV{'PROPHET_APP_CONFIG'}: $!";
 { local $/; $content = <$fh>; }
 is( $content, <<EOF, 'content in config' );
-<<<<<<< HEAD:t/aliases.t
 
 [alias]
 	pull -a = pull --all
 	pull -l = pull --local
-=======
-alias pull -l = pull --local
-alias foo bar = bar baz
-alias pull -a = pull --all
->>>>>>> master:t/aliases.t
+	foo bar = bar baz
 EOF
 
 # TODO: need tests for interactive alias editing

commit fd24abf70b083688a5f0349826541a8b80503c8a
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 16:43:50 2009 +0300

    Fix displaying of errors if set fails.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 56a707b..74df7f1 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -14,6 +14,12 @@ has config_filename => (
     },
 );
 
+has old_errors => (
+    is => 'rw',
+    isa => 'Str',
+    default => '',
+);
+
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  a => 'add', d => 'delete', s => 'show' };
 
 sub run {
@@ -172,23 +178,24 @@ sub process_template {
             \@to_set,
         );
     };
-    # if we fail, prompt the user to re-edit
 
-    # one of the few ways to trigger this is to try to set a variable
+    # if we fail, prompt the user to re-edit
+    #
+    # one of the few ways to trigger this is to try to create an alias
     # that starts with a [ character
-
-    # TODO: this doesn't really work correctly.
-    # Also, handle_template_errors gives messages that are very
-    # much tailored towards SD's ticket editing facility.
-    # Should genericise that.
     if ($@) {
-        warn $@;
-        return $self->handle_template_errors(
+        chomp $@;
+        my $error = "# Error: '$@'";
+        $self->handle_template_errors(
             rtype => 'aliases',
             template_ref => $args{template},
             bad_template => $args{edited},
-            error => "$@",
+            errors_pattern => '',
+            error => $error,
+            old_errors => $self->old_errors,
         );
+        $self->old_errors($error);
+        return 0;
     }
     # otherwise, print out what changed and return happily
     else {
diff --git a/lib/Prophet/CLI/TextEditorCommand.pm b/lib/Prophet/CLI/TextEditorCommand.pm
index 3068596..2dd4340 100644
--- a/lib/Prophet/CLI/TextEditorCommand.pm
+++ b/lib/Prophet/CLI/TextEditorCommand.pm
@@ -88,27 +88,42 @@ template (given by the arg C<bad_template> prefixed with the error messages
 given in the C<error> arg. If an errors section already exists in the
 template, it is replaced with an errors section containing the new errors.
 
+If the template you are editing is not section-based, you can override what
+will be prepended to the template by passing in the C<errors_pattern>
+argument, and passing in C<old_errors> if a template errors out repeatedly
+and there are old errors in the template that need to be replaced.
+
 Other arguments are: C<rtype>: the type of the record being edited. All
-arguments are required.
+arguments except overrides (C<errors_pattern> and C<old_errors> are
+required.
 
 =cut
 
 sub handle_template_errors {
     my $self = shift;
     my %args = validate( @_, { error => 1, template_ref => 1,
-                               bad_template => 1, rtype => 1 } );
-    my $errors_pattern = "=== errors in this $args{rtype} ===";
+                               bad_template => 1, rtype => 1,
+                               errors_pattern => 0, old_errors => 0 } );
+    my $errors_pattern = defined $args{errors_pattern}
+                       ? $args{errors_pattern}
+                       : "=== errors in this $args{rtype} ===";
 
     $self->prompt_Yn("Whoops, an error occurred processing your $args{rtype}.\nTry editing again? (Errors will be shown.)") || die "Aborted.\n";
 
-    # if the bad template already has an errors section in it, remove it
-    $args{bad_template} =~ s/$errors_pattern.*?\n(?==== .*? ===\n)//s;
+    # template is section-based
+    if ( !defined $args{old_errors} ) {
+        # if the bad template already has an errors section in it, remove it
+        $args{bad_template} =~ s/$errors_pattern.*?\n(?==== .*? ===\n)//s;
+    }
+    # template is not section-based: we allow passing in the old error to kill
+    else {
+        $args{bad_template} =~ s/\Q$args{old_errors}\E\n\n\n//;
+    }
 
     ${ $args{'template_ref'} }
-        = "$errors_pattern\n\n"
-        . $args{error} . "\n\n"
-        . 'You can bypass validation for a property by appending a ! to it.'
-        . "\n\n\n" . $args{bad_template};
+        = $errors_pattern ? "$errors_pattern\n\n" : ''
+        . $args{error} . "\n\n\n"
+        . $args{bad_template};
     return 0;
 }
 

commit 7fde2443ca389c60ab8cb294854df61efd90e175
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 22 23:09:58 2009 +0300

    Add a newline after getting output.

diff --git a/lib/Prophet/CLI/Command.pm b/lib/Prophet/CLI/Command.pm
index ee9a3f2..089f566 100644
--- a/lib/Prophet/CLI/Command.pm
+++ b/lib/Prophet/CLI/Command.pm
@@ -229,6 +229,7 @@ sub prompt_Yn {
 
     my $a = <STDIN>;
     chomp $a;
+    print "\n";
 
     return 1 if $a =~ /^(|y|yes)$/i;
     return 0;

commit 7c63419d11df4e39fcc278e82695e91c0893c0e7
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 23 10:23:19 2009 +0300

    prompt_Yn should end the pager before prompting

diff --git a/lib/Prophet/CLI/Command.pm b/lib/Prophet/CLI/Command.pm
index 089f566..18b5437 100644
--- a/lib/Prophet/CLI/Command.pm
+++ b/lib/Prophet/CLI/Command.pm
@@ -1,6 +1,7 @@
 package Prophet::CLI::Command;
 use Any::Moose;
 
+use Prophet::CLI;
 use Params::Validate qw(validate);
 
 has cli => (
@@ -225,11 +226,14 @@ otherwise. Default answer is 'Yes' (returns true).
 sub prompt_Yn {
     my $self = shift;
     my $msg = shift;
+
+    Prophet::CLI->end_pager();
     print "$msg [Y/n]: ";
 
     my $a = <STDIN>;
     chomp $a;
     print "\n";
+    Prophet::CLI->start_pager();
 
     return 1 if $a =~ /^(|y|yes)$/i;
     return 0;

commit 2d306e0d96a396254a2d0c3740de76dec0d89215
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jun 23 10:24:41 2009 +0300

    Fix a precedence bug resulting in an empty template

diff --git a/lib/Prophet/CLI/TextEditorCommand.pm b/lib/Prophet/CLI/TextEditorCommand.pm
index 2dd4340..3021ef7 100644
--- a/lib/Prophet/CLI/TextEditorCommand.pm
+++ b/lib/Prophet/CLI/TextEditorCommand.pm
@@ -121,7 +121,7 @@ sub handle_template_errors {
     }
 
     ${ $args{'template_ref'} }
-        = $errors_pattern ? "$errors_pattern\n\n" : ''
+        = ($errors_pattern ? "$errors_pattern\n\n" : '')
         . $args{error} . "\n\n\n"
         . $args{bad_template};
     return 0;

commit c25d49f1a372cc744814b3d7f6d29b0aabdd6c2a
Author: Christine Spang <spang at mit.edu>
Date:   Wed Jun 24 15:29:32 2009 +0300

    Quote elements of @ARGV that contain spaces before passing them
    off to Path::Dispatcher. Also, keep quoted text in a single
    element when parsing commands in the shell.

diff --git a/lib/Prophet/CLI.pm b/lib/Prophet/CLI.pm
index cbf0e75..5899261 100644
--- a/lib/Prophet/CLI.pm
+++ b/lib/Prophet/CLI.pm
@@ -103,7 +103,9 @@ sub run_one_command {
     $self->context( Prophet::CLIContext->new( app_handle => $self->app_handle ) );
     $self->context->setup_from_args(@args);
     my $dispatcher = $self->dispatcher_class->new( cli => $self );
-    my $dispatch = $dispatcher->dispatch( join ' ', @{ $self->context->primary_commands });
+    my $dispatch_command_string = join(' ', map { /\s/ ? qq{"$_"} : $_ }
+        @{ $self->context->primary_commands });
+    my $dispatch = $dispatcher->dispatch( $dispatch_command_string );
     $self->start_pager();
     $dispatch->run($dispatcher);
     $self->end_pager();
diff --git a/lib/Prophet/CLI/Command/Shell.pm b/lib/Prophet/CLI/Command/Shell.pm
index 852fca3..ff48e12 100644
--- a/lib/Prophet/CLI/Command/Shell.pm
+++ b/lib/Prophet/CLI/Command/Shell.pm
@@ -52,7 +52,17 @@ sub eval {
 
     eval {
         local $SIG{__DIE__} = 'DEFAULT';
-        $self->cli->run_one_command(split ' ', $line);
+        my @args;
+        while ($line) {
+            $line =~ s/^\s*//;
+            if ( $line =~ s/^["'](.+)["']// ) {
+                push @args, $1;
+            }
+            else {
+                push @args, $1 if $line =~ s/(\S+)//;
+            }
+        }
+        $self->cli->run_one_command(@args);
     };
     warn $@ if $@;
 }

commit a781ff5b1c9878aad5760d03dbd57bfb7c6b0a63
Author: Christine Spang <spang at mit.edu>
Date:   Wed Jun 24 15:32:01 2009 +0300

    Also support git-style 'alias "foo bar" "bar baz"' aliasing.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 74df7f1..4b1245f 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -46,11 +46,11 @@ sub run {
         if ( $self->has_arg('set') ) {
             my $value = $self->arg('set');
             if ( $value =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) {
-                my $old = $config->get( key => "alias.$1" );
+                my $old = $config->get( key => "alias.'$1'" );
                 if ( defined $old ) {
                     if ( $old ne $2 ) {
                         $config->set(
-                            key => "alias.$1",
+                            key => "alias.'$1'",
                             value => $2,
                             filename => $self->config_filename,
                         );
@@ -63,7 +63,7 @@ sub run {
                 }
                 else {
                     $config->set(
-                        key => "alias.$1",
+                        key => "alias.'$1'",
                         value => $2,
                         filename => $self->config_filename,
                     );
@@ -74,12 +74,12 @@ sub run {
         elsif ( $self->has_arg('delete') ) {
             my $key = $self->arg('delete');
 
-            if ( defined $config->get( key => "alias.$key" ) ) {
+            if ( defined $config->get( key => "alias.'$key'" ) ) {
                 print "deleted alias '$key = "
-                      .$config->get( key => "alias.$key" )."'\n";
+                      .$config->get( key => "alias.'$key'" )."'\n";
 
                 $config->set(
-                    key => "alias.$key",
+                    key => "alias.'$key'",
                     filename => $self->config_filename,
                 );
             }
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index b868f95..26e7760 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -75,23 +75,40 @@ on push => sub {
     run('merge', $self, @_);
 };
 
-on qr/^alias(?:es)?\s*(.*)/ => sub {
+on qr/^alias(?:es)?\s+(.*)/ => sub {
     my ( $self ) = @_;
     my $arg = $1;
+
     if ( $arg =~ /^show\b/ ) {
         $self->context->set_arg(show => 1);
     }
-    elsif ( $arg =~ /^delete\s+(.*)/ ) {
+    elsif ( $arg =~ /^edit\b/ ) {
+        $self->context->set_arg(edit => 1);
+    }
+    # arg *might* be quoted
+    elsif ( $arg =~ /^delete\s+"?([^"]+)"?/ ) {
         $self->context->set_arg(delete => $1);
     }
-    elsif ( $arg =~ /^(?:add|set)?\s+(.*)/ ) {
-        $self->context->set_arg(set => $1);
+    # prophet alias "foo bar" = "foo baz"
+    # prophet alias foo = bar
+    # prophet alias add foo bar = "bar baz"
+    # prophet alias add foo bar = bar baz
+    elsif ( $arg =~ 
+        /^(?:add |set )?\s*(?:(?:"([^"]+)"|([^"]+))\s+=\s+(?:"([^"]+)"|([^"]+)))$/ ) {
+        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+        $self->context->set_arg(set => "$orig=$new");
     }
-    elsif ( $arg =~ /=/ ) {
-        $self->context->set_arg(set => $arg);
+    # prophet alias "foo = bar"
+    # prophet alias "foo bar = foo baz"
+    elsif ( $arg =~ /^(?:add |set )?\s*"([^"]+=[^"]+)"$/ ) {
+        $self->context->set_arg(set => $1);
     }
-    elsif ( $arg =~ /^edit\b/ ) {
-        $self->context->set_arg(edit => 1);
+    # alternate syntax (preferred):
+    # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
+    # prophet alias foo bar, etc.
+    elsif ( $arg =~ /^(?:"([^"]+)"|([^"\s]+))\s+(?:"([^"]+)"|([^"\s]+))/ ) {
+        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+        $self->context->set_arg(set => "$orig=$new");
     }
     else {
         die 'no idea what you mean, sorry';
diff --git a/t/aliases.t b/t/aliases.t
index 57859b3..cbcb983 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -2,7 +2,7 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 18;
+use Prophet::Test tests => 22;
 use File::Temp qw/tempfile/;
 
 $ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
@@ -38,7 +38,6 @@ my @cmds = (
         comment => 'add the same alias will not change anything',
     },
     {
-
         # this alias is bad, please don't use it in real life
         cmd => [ 'set', 'pull -a=pull --local' ],
         output =>
@@ -87,10 +86,34 @@ my @cmds = (
         comment => 'deleted alias foo bar',
     },
     {
-        cmd => [ 'set', 'foo', 'bar', '=bar baz'],
+        cmd => [ 'set', 'foo', 'bar', '=', 'bar baz'],
         output => qr/added alias 'foo bar = bar baz'/,
-        comment => 'readd alias foo bar = bar baz',
+        comment => 'read alias foo bar = bar baz',
+    },
+    # tests for alternate syntax
+    {
+        cmd => [ 'foo bar', 'bar baz'],
+        output  => qr/alias 'foo bar = bar baz' isn't changed, won't update/,
+        comment => 'read alias foo bar = bar baz',
+    },
+    {
+        cmd => [ 'foo', 'bar baz'],
+        output  => qr/added alias 'foo = bar baz'/,
+        comment => 'added alias foo',
+    },
+    {
+        cmd => [ 'foo bar', 'bar'],
+        output =>
+          qr/changed alias 'foo bar' from 'bar baz' to 'bar'/,
+        comment => 'changed alias foo bar',
+    },
+    {
+        cmd => [ 'pull --from http://www.example.com/', 'pfe'],
+        output =>
+          qr|added alias 'pull --from http://www.example.com/ = pfe'|,
+        comment => 'added alias with weird characters',
     },
+,
 );
 
 for my $item ( @cmds ) {
@@ -110,7 +133,9 @@ is_deeply(
     {
         'pull -l' => 'pull --local',
         'pull -a' => 'pull --all',
-        'foo bar' => 'bar baz',
+        'foo bar' => 'bar',
+        'foo' => 'bar baz',
+        'pull --from http://www.example.com/' => 'pfe',
     },
     'non empty aliases',
 );
@@ -125,7 +150,9 @@ is( $content, <<EOF, 'content in config' );
 [alias]
 	pull -a = pull --all
 	pull -l = pull --local
-	foo bar = bar baz
+	foo bar = bar
+	foo = bar baz
+	pull --from http://www.example.com/ = pfe
 EOF
 
 # TODO: need tests for interactive alias editing

commit 3faa5b45b03e4b4dacd14269d1866fb35fa2477b
Author: Christine Spang <spang at mit.edu>
Date:   Wed Jun 24 20:42:39 2009 +0300

    Move the majority of the aliases code to config.
    
    The config command can now also be used to update
    the config file, à la git.

diff --git a/lib/Prophet/CLI.pm b/lib/Prophet/CLI.pm
index 5899261..b746a4d 100644
--- a/lib/Prophet/CLI.pm
+++ b/lib/Prophet/CLI.pm
@@ -103,7 +103,8 @@ sub run_one_command {
     $self->context( Prophet::CLIContext->new( app_handle => $self->app_handle ) );
     $self->context->setup_from_args(@args);
     my $dispatcher = $self->dispatcher_class->new( cli => $self );
-    my $dispatch_command_string = join(' ', map { /\s/ ? qq{"$_"} : $_ }
+    # XXX this means you can't have a literal " in CLI commands...
+    my $dispatch_command_string = join(' ', map { s/"/'/g; /\s/ ? qq{"$_"} : $_ }
         @{ $self->context->primary_commands });
     my $dispatch = $dispatcher->dispatch( $dispatch_command_string );
     $self->start_pager();
diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 4b1245f..43be982 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -2,105 +2,35 @@ package Prophet::CLI::Command::Aliases;
 use Any::Moose;
 use Params::Validate qw/validate/;
 
-extends 'Prophet::CLI::Command';
-with 'Prophet::CLI::TextEditorCommand';
-
-has config_filename => (
-    is => 'rw',
-    isa => 'Str',
-    lazy => 1,
-    default => sub {
-        $_[0]->app_handle->config->replica_config_file;
-    },
-);
-
-has old_errors => (
-    is => 'rw',
-    isa => 'Str',
-    default => '',
-);
-
-sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  a => 'add', d => 'delete', s => 'show' };
+extends 'Prophet::CLI::Command::Config';
+
+sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(), s => 'show' };
 
 sub run {
     my $self     = shift;
 
-    my $config = $self->app_handle->config;
-
-    if ($self->has_arg('global')) {
-        $self->config_filename($config->global_file);
-    }
-    elsif ($self->has_arg('user')) {
-        $self->config_filename($config->user_file);
-    }
+    my $config = $self->config;
 
     my $template = $self->make_template;
 
+    # alias.pull --from http://foo-bar.com/
     # add is the same as set
     if ( $self->context->has_arg('add') ) {
         $self->context->set_arg('set', $self->arg('add') )
     }
 
-    if ( $self->has_arg('set') || $self->has_arg('delete') ) {
-
-        if ( $self->has_arg('set') ) {
-            my $value = $self->arg('set');
-            if ( $value =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) {
-                my $old = $config->get( key => "alias.'$1'" );
-                if ( defined $old ) {
-                    if ( $old ne $2 ) {
-                        $config->set(
-                            key => "alias.'$1'",
-                            value => $2,
-                            filename => $self->config_filename,
-                        );
-                        print
-                          "changed alias '$1' from '$old' to '$2'\n";
-                    }
-                    else {
-                        print "alias '$1 = $2' isn't changed, won't update\n";
-                    }
-                }
-                else {
-                    $config->set(
-                        key => "alias.'$1'",
-                        value => $2,
-                        filename => $self->config_filename,
-                    );
-                    print "added alias '$1 = $2'\n";
-                }
-            }
-        }
-        elsif ( $self->has_arg('delete') ) {
-            my $key = $self->arg('delete');
-
-            if ( defined $config->get( key => "alias.'$key'" ) ) {
-                print "deleted alias '$key = "
-                      .$config->get( key => "alias.'$key'" )."'\n";
-
-                $config->set(
-                    key => "alias.'$key'",
-                    filename => $self->config_filename,
-                );
-            }
-            else {
-                print "didn't find alias '$key'\n";
-            }
-        }
-
-    }
-    elsif ( $self->has_arg('edit') ) {
-        my $done = 0;
-
-        while ( !$done ) {
-            $done = $self->try_to_edit( template => \$template );
-        }
-    }
-    else {
+    if ( ! ( $self->has_arg('set') ||
+             $self->has_arg('delete') || $self->has_arg('edit') ) ) {
         print $template. "\n";
         return;
     }
-
+    else {
+        $self->set_arg('set', 'alias.'.$self->arg('set'))
+            if $self->has_arg('set');
+        $self->set_arg('delete', 'alias.'.$self->arg('delete'))
+            if $self->has_arg('delete');
+        $self->SUPER::run(@_);
+    }
 }
 
 sub make_template {
@@ -125,7 +55,7 @@ sub make_template {
             $content .= "$key = $aliases->{$key}\n";
         }
     }
-    else {
+    elsif ( !$self->has_arg('edit') ) {
         $content = "No aliases for the current repository.\n";
     }
 
diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 2f69352..64072af 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -1,24 +1,136 @@
 package Prophet::CLI::Command::Config;
 use Any::Moose;
+use Params::Validate qw/validate/;
 extends 'Prophet::CLI::Command';
 
+with 'Prophet::CLI::TextEditorCommand';
+
+has config_filename => (
+    is => 'rw',
+    isa => 'Str',
+    lazy => 1,
+    default => sub {
+        $_[0]->app_handle->config->replica_config_file;
+    },
+);
+
+has old_errors => (
+    is => 'rw',
+    isa => 'Str',
+    default => '',
+);
+
+sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  a => 'add', d => 'delete', s => 'show' };
+
 sub run {
     my $self = shift;
 
     my $config = $self->config;
 
-    print "Configuration:\n\n";
-    my @files =@{$config->config_files};
-    if (!scalar @files) {
-        print $self->no_config_files;
-        return;
+    if ($self->has_arg('global')) {
+        $self->config_filename($config->global_file);
+    }
+    elsif ($self->has_arg('user')) {
+        $self->config_filename($config->user_file);
+    }
+
+    # add is the same as set
+    if ( $self->context->has_arg('add') ) {
+        $self->context->set_arg('set', $self->arg('add') )
+    }
+
+    if ( $self->has_arg('set') || $self->has_arg('delete') ) {
+
+        if ( $self->has_arg('set') ) {
+            my $value = $self->arg('set');
+            if ( $value =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) {
+                $config->set(
+                    key => $1,
+                    value => $2,
+                    filename => $self->config_filename,
+                );
+            }
+            # no value given, just print the current value
+            else {
+                print $config->get( key => $self->arg('set') ) . "\n";
+            }
+        }
+        elsif ( $self->has_arg('delete') ) {
+            my $key = $self->arg('delete');
+
+            $config->set(
+                key => $key,
+                filename => $self->config_filename,
+            );
+        }
+
     }
-    print "Config files:\n\n";
-    for my $file (@files) {
-        print "$file\n";
+    elsif ( $self->has_arg('edit') ) {
+        my $done = 0;
+
+        my $template = $self->make_template;
+
+        while ( !$done ) {
+            $done = $self->try_to_edit( template => \$template );
+        }
+    }
+    else {
+        print "Configuration:\n\n";
+        my @files =@{$config->config_files};
+        if (!scalar @files) {
+            print $self->no_config_files;
+            return;
+        }
+        print "Config files:\n\n";
+        for my $file (@files) {
+            print "$file\n";
+        }
+        print "\nYour configuration:\n\n";
+        $config->dump;
+    }
+}
+
+sub make_template {
+    my $self = shift;
+
+    return Prophet::Util->slurp($self->config_filename);
+}
+
+sub process_template {
+    my $self = shift;
+    my %args = validate( @_, { template => 1, edited => 1, record => 0 } );
+
+    # Attempt parsing the config. If we're good, remove any previous error
+    # sections, write to disk and load.
+    eval {
+        $self->config->parse_content(
+            content => $args{edited},
+            error => sub {
+                Config::GitLike::error_callback( @_, filename =>
+                    $self->config_filename );
+            },
+        );
+    };
+    if ($@) {
+        chomp $@;
+        my @error_lines = split "\n", $@;
+        my $error = join "\n", map { "# Error: '$_'" } @error_lines;
+        $self->handle_template_errors(
+            rtype => 'configuration',
+            template_ref => $args{template},
+            bad_template => $args{edited},
+            errors_pattern => '',
+            error => $error,
+            old_errors => $self->old_errors,
+        );
+        return 0;
     }
-    print "\nYour configuration:\n\n";
-    $config->dump;
+    my $old_errors = $self->old_errors;
+    Prophet::Util->write_file(
+        file => $self->config_filename,
+        content => $args{edited},
+    );
+    return 1;
 }
 
 sub no_config_files {
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index 26e7760..fedc6b3 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -75,9 +75,10 @@ on push => sub {
     run('merge', $self, @_);
 };
 
-on qr/^alias(?:es)?\s+(.*)/ => sub {
+on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
     my ( $self ) = @_;
-    my $arg = $1;
+    my $cmd = $1;
+    my $arg = $2;
 
     if ( $arg =~ /^show\b/ ) {
         $self->context->set_arg(show => 1);
@@ -96,6 +97,7 @@ on qr/^alias(?:es)?\s+(.*)/ => sub {
     elsif ( $arg =~ 
         /^(?:add |set )?\s*(?:(?:"([^"]+)"|([^"]+))\s+=\s+(?:"([^"]+)"|([^"]+)))$/ ) {
         my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
         $self->context->set_arg(set => "$orig=$new");
     }
     # prophet alias "foo = bar"
@@ -106,14 +108,20 @@ on qr/^alias(?:es)?\s+(.*)/ => sub {
     # alternate syntax (preferred):
     # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
     # prophet alias foo bar, etc.
-    elsif ( $arg =~ /^(?:"([^"]+)"|([^"\s]+))\s+(?:"([^"]+)"|([^"\s]+))/ ) {
+    elsif ( $arg =~ /^(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
         my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
-        $self->context->set_arg(set => "$orig=$new");
+        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
+        if ( $new ) {
+            $self->context->set_arg(set => "$orig=$new");
+        }
+        else {
+            $self->context->set_arg(set => $orig);
+        }
     }
     else {
         die 'no idea what you mean, sorry';
     }
-    run( 'aliases', $self, @_ );
+    run( $cmd, $self, @_ );
 };
 
 sub run_command {
diff --git a/t/aliases.t b/t/aliases.t
index cbcb983..f627413 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -2,7 +2,7 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 22;
+use Prophet::Test tests => 30;
 use File::Temp qw/tempfile/;
 
 $ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
@@ -20,6 +20,7 @@ $config->load;
 
 is_deeply( scalar $config->aliases, {}, 'initial alias is empty' );
 
+# no news is good news
 my @cmds = (
     {
         cmd => [ 'show' ],
@@ -29,90 +30,127 @@ my @cmds = (
 
     {
         cmd => [ 'add', 'pull -a=pull --all' ],
-        output  => qr/added alias 'pull -a = pull --all/,
+        output  => qr//,
         comment => 'add a new alias',
     },
     {
-        cmd => [ 'add', 'pull -a=pull --all' ],
-        output  => qr/alias 'pull -a = pull --all' isn't changed, won't update/,
-        comment => 'add the same alias will not change anything',
+        cmd => [ 'pull -a' ],
+        output  => qr/pull --all/,
+        comment => 'new alias set correctly',
     },
     {
         # this alias is bad, please don't use it in real life
         cmd => [ 'set', 'pull -a=pull --local' ],
-        output =>
-          qr/changed alias 'pull -a' from 'pull --all' to 'pull --local'/,
+        output => qr//,
         comment =>
           q{changed alias 'pull -a' from 'pull --all' to 'pull --local'},
     },
     {
+        cmd => [ 'pull -a' ],
+        output  => qr/pull --local/,
+        comment => 'alias changed correctly',
+    },
+    {
         cmd     => [ 'delete', 'pull -a' ],
-        output  => qr/deleted alias 'pull -a = pull --local'/,
+        output  => qr//,
         comment => q{deleted alias 'pull -a = pull --local'},
     },
     {
         cmd     => [ 'delete', 'pull -a' ],
-        output  => qr/didn't find alias 'pull -a'/,
+        output  => qr//,
         comment => q{delete an alias that doesn't exist any more},
     },
     {
         cmd => [ 'add', 'pull -a=pull --all' ],
-        output  => qr/added alias 'pull -a = pull --all/,
-        comment => 'read a new alias',
+        output  => qr//,
+        comment => 'add a new alias',
+    },
+    {
+        cmd => [ 'pull -a' ],
+        output  => qr/pull --all/,
+        comment => 'alias is set correctly',
     },
     {
         cmd => [ 'add', 'pull -l=pull --local' ],
-        output  => qr/added alias 'pull -l = pull --local/,
+        output  => qr//,
         comment => 'add a new alias',
     },
     {
+        cmd => [ 'pull -l' ],
+        output  => qr/pull --local/,
+        comment => 'alias is set correctly',
+    },
+    {
         cmd => [ 'show' ],
         output  => qr/Active aliases for the current repository \(including user-wide and global\naliases if not overridden\):\n\npull -l = pull --local\npull -a = pull --all/s,
         comment => 'show',
     },
     {
         cmd => [ 'add', 'foo', 'bar', '=', 'bar',  'baz' ],
-        output  => qr/added alias 'foo bar = bar baz'/,
+        output  => qr//,
         comment => 'added alias foo bar',
     },
     {
-        cmd => [ 'foo', 'bar', '=', 'bar',  'baz' ],
-        output  => qr/alias 'foo bar = bar baz' isn't changed, won't update/,
+        cmd => [ 'foo bar' ],
+        output  => qr/bar baz/,
+        comment => 'alias is set correctly',
+    },
+    {
+        cmd => [ 'foo', 'bar', '=bar',  'baz' ],
+        output  => qr//,
         comment => 'read alias foo bar',
     },
     {
+        cmd => [ 'foo bar' ],
+        output  => qr/bar baz/,
+        comment => 'alias foo bar still the same',
+    },
+    {
         cmd => [ 'delete', 'foo', 'bar' ],
-        output  => qr/deleted alias 'foo bar = bar baz'/,
+        output  => qr//,
         comment => 'deleted alias foo bar',
     },
     {
+        cmd => [ 'foo', 'bar' ],
+        output  => qr//,
+        comment => 'deleted alias no longer exists',
+    },
+    {
         cmd => [ 'set', 'foo', 'bar', '=', 'bar baz'],
-        output => qr/added alias 'foo bar = bar baz'/,
-        comment => 'read alias foo bar = bar baz',
+        output => qr//,
+        comment => 'set alias again with different syntax',
     },
     # tests for alternate syntax
     {
         cmd => [ 'foo bar', 'bar baz'],
-        output  => qr/alias 'foo bar = bar baz' isn't changed, won't update/,
-        comment => 'read alias foo bar = bar baz',
+        output  => qr//,
+        comment => 'alias foo bar = bar baz didn\'t change',
     },
     {
         cmd => [ 'foo', 'bar baz'],
-        output  => qr/added alias 'foo = bar baz'/,
+        output  => qr//,
         comment => 'added alias foo',
     },
     {
+        cmd => [ 'foo' ],
+        output  => qr/bar baz/,
+        comment => 'alias foo set correctly',
+    },
+    {
         cmd => [ 'foo bar', 'bar'],
-        output =>
-          qr/changed alias 'foo bar' from 'bar baz' to 'bar'/,
+        output => qr//,
         comment => 'changed alias foo bar',
     },
     {
         cmd => [ 'pull --from http://www.example.com/', 'pfe'],
-        output =>
-          qr|added alias 'pull --from http://www.example.com/ = pfe'|,
+        output => qr//,
         comment => 'added alias with weird characters',
     },
+    {
+        cmd => [ 'pull --from http://www.example.com/'],
+        output => qr/pfe/,
+        comment => 'alias with weird chars is correct',
+    },
 ,
 );
 
@@ -150,8 +188,8 @@ is( $content, <<EOF, 'content in config' );
 [alias]
 	pull -a = pull --all
 	pull -l = pull --local
-	foo bar = bar
 	foo = bar baz
+	foo bar = bar
 	pull --from http://www.example.com/ = pfe
 EOF
 

commit 28fa29a29c9fdf8ea8e300526d6223d4a97a1378
Author: Christine Spang <spang at mit.edu>
Date:   Wed Jun 24 21:59:31 2009 +0300

    Update recording replica pulls to new config format.

diff --git a/lib/Prophet/CLI/Command/Pull.pm b/lib/Prophet/CLI/Command/Pull.pm
index dec663e..cb3ed91 100644
--- a/lib/Prophet/CLI/Command/Pull.pm
+++ b/lib/Prophet/CLI/Command/Pull.pm
@@ -42,22 +42,36 @@ sub run {
 
 }
 
+# Create a new [replica] config file section for this replica if we haven't
+# pulled from it before.
 sub record_pull_from_source {
     my $self = shift;
     my $source = shift;
     my $from_uuid = shift;
-    my %previous_sources = $self->app_handle->config->sources;
-    my %sources_by_url = map {
-            my $name = $_;
-            my ($url, $uuid);
-            ($url, $uuid ) = split(qr/ \| /, $previous_sources{$name}, 2);
-         ($url => $uuid )
-     } keys %previous_sources;
-    if ( !exists $sources_by_url{$source}) {
-        $self->app_handle->config->set(
-            key => "source.'$source'",
-            value => "$source | $from_uuid",
-            filename => $self->app_handle->config->replica_config_file,
+
+    my %previous_sources
+        = $self->app_handle->config->get_regexp( key => "^replica\..+\.url" );
+
+    my $found_prev_replica;
+    for my $key (keys %previous_sources) {
+        $found_prev_replica = $previous_sources{$key}
+            if $previous_sources{$key} eq $source;
+    }
+
+    if ( !$found_prev_replica ) {
+        warn "setting new replica";
+        $self->app_handle->config->group_set(
+            $self->app_handle->config->replica_config_file,
+            [
+            {
+                key => "replica.$source.url",
+                value => $source,
+            },
+            {
+                key => "replica.$source.uuid",
+                value => $from_uuid,
+            },
+            ],
         );
     }
 }

commit f66404963e5310b5916c94b9c3b43a0d09a4cce9
Author: Christine Spang <spang at mit.edu>
Date:   Thu Jun 25 11:53:15 2009 +0300

    Friendly names and related changes.

diff --git a/lib/Prophet/CLI/Command/Clone.pm b/lib/Prophet/CLI/Command/Clone.pm
index fa87866..634aa2a 100644
--- a/lib/Prophet/CLI/Command/Clone.pm
+++ b/lib/Prophet/CLI/Command/Clone.pm
@@ -44,7 +44,7 @@ sub run {
     $target->initialize(%init_args);
 
     $self->app_handle->config->set(
-        key => 'source.'.$self->arg('from'),
+        key => 'replica.'.$self->arg('from').'.url',
         value => $self->arg('from'),
         filename => $self->app_handle->config->replica_config_file,
     );
diff --git a/lib/Prophet/CLI/Command/Pull.pm b/lib/Prophet/CLI/Command/Pull.pm
index cb3ed91..94017e0 100644
--- a/lib/Prophet/CLI/Command/Pull.pm
+++ b/lib/Prophet/CLI/Command/Pull.pm
@@ -10,19 +10,21 @@ sub run {
 
     Prophet::CLI->end_pager();
 
-    my $previous_sources = $self->app_handle->config->sources;
+    my %previous_sources_by_name = $self->app_handle->config->sources;
 
     my $explicit_from = '';
 
     if ($self->has_arg('from')) {
-        $explicit_from = $self->arg('from') ;
+        # substitute friendly name -> replica url if we can
+        $explicit_from
+            = exists $previous_sources_by_name{$self->arg('from')}
+            ? $previous_sources_by_name{$self->arg('from')}
+            : $self->arg('from');
         push @from, $explicit_from;
     }
     elsif ($self->has_arg('all')){
-        for my $source (values %$previous_sources) {
-            my ($url, $uuid ) = split(qr/ \| /,$source,2);
+        for my $url (values %previous_sources_by_name) {
             push @from, $url;
-
         }
     }
 
@@ -49,17 +51,12 @@ sub record_pull_from_source {
     my $source = shift;
     my $from_uuid = shift;
 
-    my %previous_sources
-        = $self->app_handle->config->get_regexp( key => "^replica\..+\.url" );
+    my %previous_sources_by_url
+        = $self->app_handle->config->sources( by_url => 1 );
 
-    my $found_prev_replica;
-    for my $key (keys %previous_sources) {
-        $found_prev_replica = $previous_sources{$key}
-            if $previous_sources{$key} eq $source;
-    }
+    my $found_prev_replica = $previous_sources_by_url{$source};
 
     if ( !$found_prev_replica ) {
-        warn "setting new replica";
         $self->app_handle->config->group_set(
             $self->app_handle->config->replica_config_file,
             [
diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index 5dd7a68..fbeff1b 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -44,7 +44,8 @@ override global_file => sub {
     return exists $ENV{PROPHET_APP_CONFIG} ? '' : $self->SUPER::global_file(@_);
 };
 
-# grab all values in the 'alias' section and strip away the section name
+# grab all values in the 'alias' section (of the file, if given) and strip
+# away the section name
 sub aliases {
     my $self = shift;
     my $file = shift;
@@ -88,16 +89,20 @@ sub aliases {
     return wantarray ? %new_aliases : \%new_aliases;
 }
 
-# grab all values in the 'source' section and strip away the section name
+# grab all the replicas we know of and return a hash of
+# name => url, or url => name if $args{by_url} is true
 sub sources {
     my $self = shift;
+    my %args = (
+        by_url => undef,
+        @_,
+    );
 
-    my %sources = $self->get_regexp( key => '^source\.' );
+    my %sources = $self->get_regexp( key => '^replica\..*\.url$' );
 
     my %new_sources = map {
-        my $source = $_;
-        $source =~ s/^source\.//;
-        ( $source => $sources{$_} );
+        $_ =~ /^replica\.(.*)\.url$/;
+        $args{by_url} ? ( $sources{$_} => $1 ) : ( $1 => $sources{$_} );
     } keys %sources;
 
     return wantarray ? %new_sources : \%new_sources;
@@ -112,13 +117,20 @@ sub replica_config_file {
     );
 }
 
-# friendly replica names go in the [display] section
+# friendly names are replica subsections
 sub display_name_for_uuid {
     my $self = shift;
     my $uuid = shift;
 
-    my $friendly = $self->get( key => "display.$uuid" );
-    return defined($friendly) ? $friendly : $uuid;
+    my %possibilities = $self->get_regexp( key => '^replica\..*\.uuid$' );
+    # form a hash of uuid -> name
+    my %sources_by_uuid = map {
+        my $uuid = $possibilities{$_};
+        $_ =~ /^replica\.(.*)\.uuid$/;
+        my $name = $1;
+        ( $uuid => $name );
+    } keys %possibilities;
+    return exists $sources_by_uuid{$uuid} ? $sources_by_uuid{$uuid} : $uuid;
 }
 
 __PACKAGE__->meta->make_immutable;
@@ -184,14 +196,12 @@ are defined in that particular config file.
 
 A convenience method that gets you a hash (or a hashref, depending on context)
 of all currently defined source replicas, in the format { 'name' =>
-{ url => 'URL', uuid => 'UUID } }. (Basically, every entry in the 'replica'
-section of the config file.)
+'URL' }, or { 'URL' => 'name' } if the argument C<by_url> is passed in.
 
 =head2 display_name_for_uuid UUID
 
-Returns a "friendly" id for the given uuid.
-
-TODO: regexp search for 'replica.(.*).UUID' and extract the section
+Returns a "friendly" id for the given uuid. UUIDs are for computers, friendly
+names are for people.
 
 =head1 CONFIG VARIABLES
 
diff --git a/t/config.t b/t/config.t
index a7ff1f4..88c47fd 100644
--- a/t/config.t
+++ b/t/config.t
@@ -2,7 +2,7 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 11;
+use Prophet::Test tests => 13;
 use File::Copy;
 use File::Temp qw'tempdir';
 
@@ -49,14 +49,28 @@ is( scalar @keys, 0, 'no config options are set' );
     is( $conf->aliases->{tlist}, 'ticket list', 'Got correct alias' );
     # test automatic reload after setting
     $conf->set(
-        key => 'source.sd',
+        key => 'replica.sd.url',
         value => 'http://fsck.com/sd/',
         filename => File::Spec->catfile($repo, 'test_app.conf'),
     );
-    is( $conf->get( key => 'source.sd' ), 'http://fsck.com/sd/',
+    is( $conf->get( key => 'replica.sd.url' ), 'http://fsck.com/sd/',
         'automatic reload after set' );
     # test the sources sub
     is( $conf->sources->{sd}, 'http://fsck.com/sd/', 'Got correct alias' );
+    is( $conf->sources( by_url => 1)->{'http://fsck.com/sd/'},
+        'sd',
+        'Got correct alias',
+    );
+    # test the display_name_for_uuid sub
+    $conf->set(
+        key => 'replica.sd.uuid',
+        value => '32b13934-910a-4792-b5ed-c9977b212245',
+        filename => File::Spec->catfile($repo, 'test_app.conf'),
+    );
+    is( $conf->display_name_for_uuid('32b13934-910a-4792-b5ed-c9977b212245'),
+        'sd',
+        'Got correct display name'
+    );
 
     # run the cli "config" command
     # make sure it matches with our file
@@ -71,7 +85,8 @@ $repo/test_app.conf
 Your configuration:
 
 alias.tlist=ticket list
-source.sd=http://fsck.com/sd/
+replica.sd.url=http://fsck.com/sd/
+replica.sd.uuid=32b13934-910a-4792-b5ed-c9977b212245
 test.foo=bar
 test.re=rawr
 EOF

commit 6b13137a8ae91d0d444a1e000f1a25149699321b
Author: Christine Spang <spang at mit.edu>
Date:   Thu Jun 25 14:35:15 2009 +0300

    Don't bring up editor if the user can't edit the given config file (us. happens with global files).

diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 64072af..2b57b62 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -68,6 +68,9 @@ sub run {
     elsif ( $self->has_arg('edit') ) {
         my $done = 0;
 
+        die "You don't have write permissions on "
+            .$self->config_filename.", can't edit!\n"
+            if ! -w $self->config_filename;
         my $template = $self->make_template;
 
         while ( !$done ) {

commit a2fc3bb4c194e6eedca05e870201173f0b7aebce
Author: Christine Spang <spang at mit.edu>
Date:   Thu Jun 25 14:37:51 2009 +0300

    Allow editing aliases/config files even if they don't already exist.

diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 2b57b62..d109840 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -96,7 +96,8 @@ sub run {
 sub make_template {
     my $self = shift;
 
-    return Prophet::Util->slurp($self->config_filename);
+    return -f $self->config_filename
+            ? Prophet::Util->slurp( $self->config_filename ) : '';
 }
 
 sub process_template {
diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index fbeff1b..2f6bfa3 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -54,7 +54,7 @@ sub aliases {
     if ( $file ) {
         # parse the given config file with parse_content and use the
         # callbacks to add to an array
-        my $content = Prophet::Util->slurp( $file );
+        my $content = -f $file ? Prophet::Util->slurp( $file ) : '';
         $self->parse_content(
             content => $content,
             callback => sub {

commit a1316914f448020702470436298912aebc683132
Author: Christine Spang <spang at mit.edu>
Date:   Thu Jun 25 14:38:10 2009 +0300

    Test one other syntax rather than the same syntax twice.

diff --git a/t/aliases.t b/t/aliases.t
index f627413..6328184 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -116,7 +116,7 @@ my @cmds = (
         comment => 'deleted alias no longer exists',
     },
     {
-        cmd => [ 'set', 'foo', 'bar', '=', 'bar baz'],
+        cmd => [ 'set', 'foo bar', '=', 'bar baz'],
         output => qr//,
         comment => 'set alias again with different syntax',
     },

commit 206525ef612f9e395c641cdc2f07d9a29e170a13
Author: Christine Spang <spang at mit.edu>
Date:   Fri Jun 26 12:40:37 2009 +0300

    Move set_editor_script over from SD

diff --git a/lib/Prophet/Test.pm b/lib/Prophet/Test.pm
index 6e7dbca..37df797 100644
--- a/lib/Prophet/Test.pm
+++ b/lib/Prophet/Test.pm
@@ -6,10 +6,11 @@ use base qw/Test::More Exporter/;
 use Test::Script::Run ':all';
 our @EXPORT = qw/as_alice as_bob as_charlie as_david as_user run_ok repo_uri_for run_script run_output_matches run_output_matches_unordered replica_last_rev replica_uuid_for ok_added_revisions replica_uuid database_uuid database_uuid_for
     serialize_conflict serialize_changeset in_gladiator diag is_script_output
-    run_command set_editor load_record last_script_stdout last_script_stderr
+    run_command set_editor set_editor_script load_record last_script_stdout last_script_stderr
     last_script_exit_code
     /;
 
+use Cwd qw/getcwd/;
 use File::Path 'rmtree';
 use File::Spec;
 use File::Temp qw/tempdir tempfile/;
@@ -55,6 +56,22 @@ sub set_editor {
     $EDIT_TEXT = shift;
 }
 
+=head2 set_editor_script SCRIPT
+
+Sets the editor that Proc::InvokeEditor uses.
+
+This should be a non-interactive script found in F<t/scripts>.
+
+=cut
+
+sub set_editor_script {
+    my ($self, $script) = @_;
+
+    delete $ENV{'VISUAL'};       # Proc::InvokeEditor checks this first
+    $ENV{'EDITOR'} = "$^X " . File::Spec->catfile(getcwd(), 't', 'scripts', $script);
+    Test::More::diag "export EDITOR=" . $ENV{'EDITOR'} . "\n";
+}
+
 =head2 import_extra($class, $args)
 
 =cut

commit 00486b0b4111839cb2deb88f7ca4fda9cf1e1275
Author: Christine Spang <spang at mit.edu>
Date:   Fri Jun 26 12:42:22 2009 +0300

    Move settings command tests from SD to Prophet

diff --git a/lib/Prophet/Test/Editor.pm b/lib/Prophet/Test/Editor.pm
new file mode 100644
index 0000000..bb5fe2b
--- /dev/null
+++ b/lib/Prophet/Test/Editor.pm
@@ -0,0 +1,97 @@
+package Prophet::Test::Editor;
+use strict;
+use warnings;
+
+use Prophet::Util;
+use Params::Validate;
+use File::Spec;
+
+=head2 edit( tmpl_files => $tmpl_files, edit_callback => sub {}, verify_callback => sub {} )
+
+Expects @ARGV to contain at least an option and a file to be edited. It
+can also contain a replica uuid, a ticket uuid, and a status file. The last
+item must always be the file to be edited. The others, if they appear, must
+be in that order after the option. The status file must contain the
+string 'status' in its filename.
+
+edit_callback is called on each line of the file being edited. It should make
+any edits to the lines it receives and then print what it wants to be saved to
+the file.
+
+verify_callback is called after editing is done. If you need to write
+whether the template was correct to a status file, for example, this
+should be done here.
+
+=cut
+
+sub edit {
+    my %args = @_;
+    validate( @_, { edit_callback => 1,
+                    verify_callback => 1,
+                    tmpl_files  => 1,
+                   }
+             );
+
+    my $option = shift @ARGV;
+    my $tmpl_file = $args{tmpl_files}->{$option};
+
+    my @valid_template = Prophet::Util->slurp("t/data/$tmpl_file");
+    chomp @valid_template;
+
+    my $status_file = $ARGV[-2] =~ /status/ ? delete $ARGV[-2] : undef;
+    # a bit of a hack to dermine whether the last arg is a filename
+    my $replica_uuid = File::Spec->file_name_is_absolute($ARGV[0]) ? undef : shift @ARGV;
+    my $ticket_uuid = File::Spec->file_name_is_absolute($ARGV[0]) ? undef : shift @ARGV;
+
+    my @template = ();
+    while (<>) {
+        chomp( my $line = $_ );
+        push @template, $line;
+
+        $args{edit_callback}(
+            option => $option, template => \@template,
+            valid_template => \@valid_template,
+            replica_uuid => $replica_uuid,
+            ticket_uuid => $ticket_uuid,
+        );
+    }
+
+    $args{verify_callback}( template => \@template,
+        valid_template => \@valid_template, status_file => $status_file );
+}
+
+=head2 check_template_by_line($template, $valid_template, $errors)
+
+$template is a reference to an array containing the template to check,
+split into lines. $valid_template is the same for the template to
+check against. Lines in these arrays should not have trailing newlines.
+$errors is a reference to an array where error messages will be stored.
+
+Lines in $valid_template should consist of either plain strings, or strings
+beginning with 'qr/' (to delimit a regexp object).
+
+Returns true if the templates match and false otherwise.
+
+=cut
+
+sub check_template_by_line {
+    my @template = @{ shift @_ };
+    my @valid_template = @{ shift @_ };
+    my $replica_uuid = shift;
+    my $ticket_uuid = shift;
+    my $errors = shift;
+
+    for my $valid_line (@valid_template) {
+        my $line = shift @template;
+
+        push @$errors, "got nothing, expected [$valid_line]" if !defined($line);
+
+        push @$errors, "[$line] doesn't match [$valid_line]"
+            if ($valid_line =~ /^qr\//) ? $line !~ eval($valid_line)
+            : $line eq $valid_line;
+    }
+
+    return !(@$errors == 0);
+}
+
+1;
diff --git a/t/Settings/t/database-settings-editor.t b/t/Settings/t/database-settings-editor.t
new file mode 100644
index 0000000..d0098df
--- /dev/null
+++ b/t/Settings/t/database-settings-editor.t
@@ -0,0 +1,97 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use Prophet::Test tests => 9;
+use Prophet::Util;
+use File::Temp qw(tempdir);
+use File::Spec;
+no warnings 'once';
+
+$ENV{'PERL5LIB'} .=  ':t/Settings/lib';
+
+# test the CLI and interactive UIs for showing and updating settings
+
+BEGIN {
+    require File::Temp;
+    $ENV{'PROPHET_REPO'} = File::Temp::tempdir( CLEANUP => 1 ) . '/_svb';
+    diag $ENV{'PROPHET_REPO'};
+}
+
+run_ok( 'settings', [ 'init' ] );
+
+my $replica_uuid = replica_uuid;
+
+# test noninteractive set
+run_output_matches( 'settings', [ 'settings', '--set', '--', 'statuses',
+    '["new","open","stalled"]' ],
+    [
+        'Trying to change statuses from ["new","open","stalled","closed"] to ["new","open","stalled"].',
+        ' -> Changed.',
+    ], [], "settings --set went ok",
+);
+
+# check with settings --show
+my @valid_settings_output = Prophet::Util->slurp('t/data/settings-first.tmpl');
+chomp (@valid_settings_output);
+
+run_output_matches(
+    'settings',
+    [ qw/settings --show/ ],
+    [ @valid_settings_output ], [], "changed settings output matches"
+);
+
+# test settings (interactive editing)
+
+my $filename = File::Temp->new(
+    TEMPLATE => File::Spec->catfile(File::Spec->tmpdir(), '/statusXXXXX') )->filename;
+diag ("interactive template status will be found in $filename");
+# first set the editor to an editor script
+Prophet::Test->set_editor_script("settings-editor.pl --first $filename");
+
+# then edit the settings
+run_output_matches( 'settings', [ 'settings' ],
+    [
+        'Changed default_status from ["new"] to ["open"].',
+        'Setting with uuid "6FBD84A1-4568-48E7-B90C-F1A5B7BD8ECD" does not exist.',
+    ], [], "interactive settings set went ok",);
+
+# check the tempfile to see if the template presented to the editor was correct
+chomp(my $template_ok = Prophet::Util->slurp($filename));
+is($template_ok, 'ok!', "interactive template was correct");
+
+# check the settings with settings --show
+ at valid_settings_output = Prophet::Util->slurp('t/data/settings-second.tmpl');
+chomp (@valid_settings_output);
+
+run_output_matches(
+    'settings',
+    [ qw/settings --show/ ],
+    [ @valid_settings_output ], [], "changed settings output matches"
+);
+
+# test setting to invalid json
+my $second_filename = File::Temp->new(
+    TEMPLATE => File::Spec->catfile(File::Spec->tmpdir(), '/statusXXXXX') )->filename;
+diag ("interactive template status will be found in $second_filename");
+Prophet::Test->set_editor_script("settings-editor.pl --second $second_filename");
+run_output_matches( 'settings', [ 'settings' ],
+    [
+        qr/^An error occured setting default_milestone to \["alpha":/,
+        'Changed default_component from ["core"] to ["ui"].',
+    ], [], "interactive settings set with JSON error went ok",
+);
+
+# check the tempfile to see if the template presented to the editor was correct
+chomp($template_ok = Prophet::Util->slurp($filename));
+is($template_ok, 'ok!', "interactive template was correct");
+
+# check the settings with settings --show
+ at valid_settings_output = Prophet::Util->slurp('t/data/settings-third.tmpl');
+chomp (@valid_settings_output);
+
+run_output_matches(
+    'settings',
+    [ qw/settings --show/ ],
+    [ @valid_settings_output ], [], "changed settings output matches"
+);
diff --git a/t/Settings/t/sync-database-settings.t b/t/Settings/t/sync-database-settings.t
index bed418f..5c1fbd1 100644
--- a/t/Settings/t/sync-database-settings.t
+++ b/t/Settings/t/sync-database-settings.t
@@ -37,6 +37,3 @@ as_alice {
     like($stdout, qr/default_status: \["stalled"\]/, "the original milestone list is there");
 
 };
-exit;
-
-
diff --git a/t/data/settings-first.tmpl b/t/data/settings-first.tmpl
new file mode 100644
index 0000000..57cc516
--- /dev/null
+++ b/t/data/settings-first.tmpl
@@ -0,0 +1,19 @@
+# uuid: BAB613BD-9E25-4612-8DE3-21E4572859EA
+default_milestone: ["alpha"]
+
+# uuid: 1AF5CF74-A6D4-417E-A738-CCE64A0A7F71
+milestones: ["alpha","beta","1.0"]
+
+# uuid: 0AEC922F-57B1-44BE-9588-816E5841BB18
+default_component: ["core"]
+
+# uuid: 6CBD84A1-4568-48E7-B90C-F1A5B7BD8ECD
+components: ["core","ui","docs","tests"]
+
+# uuid: 2F9E6509-4468-438A-A733-246B3061003E
+default_status: ["new"]
+
+# uuid: 24183C4D-EFD0-4B16-A207-ED7598E875E6
+statuses: ["new","open","stalled"]
+
+
diff --git a/t/data/settings-second.tmpl b/t/data/settings-second.tmpl
new file mode 100644
index 0000000..1cc5abc
--- /dev/null
+++ b/t/data/settings-second.tmpl
@@ -0,0 +1,19 @@
+# uuid: BAB613BD-9E25-4612-8DE3-21E4572859EA
+default_milestone: ["alpha"]
+
+# uuid: 1AF5CF74-A6D4-417E-A738-CCE64A0A7F71
+milestones: ["alpha","beta","1.0"]
+
+# uuid: 0AEC922F-57B1-44BE-9588-816E5841BB18
+default_component: ["core"]
+
+# uuid: 6CBD84A1-4568-48E7-B90C-F1A5B7BD8ECD
+components: ["core","ui","docs","tests"]
+
+# uuid: 2F9E6509-4468-438A-A733-246B3061003E
+default_status: ["open"]
+
+# uuid: 24183C4D-EFD0-4B16-A207-ED7598E875E6
+statuses: ["new","open","stalled"]
+
+
diff --git a/t/data/settings-third.tmpl b/t/data/settings-third.tmpl
new file mode 100644
index 0000000..74dd857
--- /dev/null
+++ b/t/data/settings-third.tmpl
@@ -0,0 +1,19 @@
+# uuid: BAB613BD-9E25-4612-8DE3-21E4572859EA
+default_milestone: ["alpha"]
+
+# uuid: 1AF5CF74-A6D4-417E-A738-CCE64A0A7F71
+milestones: ["alpha","beta","1.0"]
+
+# uuid: 0AEC922F-57B1-44BE-9588-816E5841BB18
+default_component: ["ui"]
+
+# uuid: 6CBD84A1-4568-48E7-B90C-F1A5B7BD8ECD
+components: ["core","ui","docs","tests"]
+
+# uuid: 2F9E6509-4468-438A-A733-246B3061003E
+default_status: ["open"]
+
+# uuid: 24183C4D-EFD0-4B16-A207-ED7598E875E6
+statuses: ["new","open","stalled"]
+
+
diff --git a/t/scripts/settings-editor.pl b/t/scripts/settings-editor.pl
new file mode 100755
index 0000000..6a6ad8a
--- /dev/null
+++ b/t/scripts/settings-editor.pl
@@ -0,0 +1,53 @@
+#!perl -i
+use strict;
+use warnings;
+use Prophet::Test::Editor;
+
+# perl script to trick Proc::InvokeEditor with for the settings command
+
+my %tmpl_files = ( '--first' => 'settings-first.tmpl',
+                   '--second' => 'settings-second.tmpl',
+);
+
+Prophet::Test::Editor::edit(
+    tmpl_files => { '--first' => 'settings-first.tmpl',
+                   '--second' => 'settings-second.tmpl',
+               },
+    edit_callback => sub {
+        my %args = @_;
+        my $option = $args{option};
+
+        if ($option eq '--first') {
+            s/(?<=^default_status: \[")new(?="\])/open/; # valid json change
+            s/^default_milestone(?=: \["alpha"\])$/invalid_setting/; # changes setting name
+            s/(?<=uuid: 6)C(?=BD84A1)/F/; # changes a UUID to an invalid one
+            s/^project_name//; # deletes setting
+        } elsif ($option eq '--second') {
+            s/(?<=^default_component: \[")core(?="\])/ui/; # valid json change
+            s/(?<=^default_milestone: \["alpha")]$//; # invalid json
+        }
+        print;
+    },
+    verify_callback => sub {
+        my %args = @_;
+
+        my $ok = 1;
+
+        my %seen;     # lookup table
+        my @vonly;    # answer
+
+        # build lookup table
+        @seen{@{$args{template}}} = ( );
+
+        for my $line (@{$args{valid_template}}) {
+            push(@vonly, $line) unless exists $seen{$line};
+        }
+
+        # if anything is only in the valid template, we don't match
+        $ok = 0 if scalar @vonly;
+
+        open STATUSFILE, '>', $args{status_file};
+        $ok ? print STATUSFILE "ok!" : print STATUSFILE "not ok!";
+        close STATUSFILE;
+    }
+);

commit eb41224ca6bc47e1447b4b2fea44f181ebacaac8
Author: Christine Spang <spang at mit.edu>
Date:   Fri Jun 26 13:19:18 2009 +0300

    Whoops, allow editing the config file if it doesn't yet, as long as it's contained in a directory writable by the user.

diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index d109840..47dd29d 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -70,7 +70,8 @@ sub run {
 
         die "You don't have write permissions on "
             .$self->config_filename.", can't edit!\n"
-            if ! -w $self->config_filename;
+            if (-e $self->config_filename && ! -w $self->config_filename)
+                || ! -W (File::Spec->splitpath($self->config_filename))[1];
         my $template = $self->make_template;
 
         while ( !$done ) {

commit 64cc2ffd1ed6b5a907e3ae8cb4ef0e3d8e8e29d7
Author: Christine Spang <spang at mit.edu>
Date:   Fri Jun 26 14:07:50 2009 +0300

    Test interactive alias editor.

diff --git a/t/aliases.t b/t/aliases.t
index 6328184..b8f5c8a 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -2,7 +2,7 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 30;
+use Prophet::Test tests => 32;
 use File::Temp qw/tempfile/;
 
 $ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
@@ -193,4 +193,26 @@ is( $content, <<EOF, 'content in config' );
 	pull --from http://www.example.com/ = pfe
 EOF
 
-# TODO: need tests for interactive alias editing
+# tests for interactive alias editing
+my $filename = File::Temp->new(
+    TEMPLATE => File::Spec->catfile(File::Spec->tmpdir(), '/statusXXXXX') )->filename;
+diag ("interactive template status will be found in $filename");
+Prophet::Test->set_editor_script("aliases-editor.pl --first $filename");
+
+run_output_matches( 'prophet', [ 'aliases', 'edit' ],
+    [
+        "Added alias 'something different' = 'pull --local'",
+        "Changed alias 'foo' from 'bar baz'to 'sigh'",
+        "Deleted alias 'pull -l'",
+    ], [], 'aliases edit went ok',
+);
+
+# check with alias show
+my @valid_settings_output = Prophet::Util->slurp('t/data/aliases.tmpl');
+chomp (@valid_settings_output);
+
+run_output_matches(
+    'prophet',
+    [ qw/alias show/ ],
+    [ @valid_settings_output ], [], "changed alias output matches"
+);
diff --git a/t/data/aliases.tmpl b/t/data/aliases.tmpl
new file mode 100644
index 0000000..0a20608
--- /dev/null
+++ b/t/data/aliases.tmpl
@@ -0,0 +1,9 @@
+Active aliases for the current repository (including user-wide and global
+aliases if not overridden):
+
+foo bar = bar
+foo = sigh
+something different = pull --local
+pull -a = pull --all
+pull --from http://www.example.com/ = pfe
+
diff --git a/t/scripts/aliases-editor.pl b/t/scripts/aliases-editor.pl
new file mode 100755
index 0000000..c7aaea5
--- /dev/null
+++ b/t/scripts/aliases-editor.pl
@@ -0,0 +1,24 @@
+#!perl -i
+use strict;
+use warnings;
+use Prophet::Test::Editor;
+
+# perl script to trick Proc::InvokeEditor with for the settings command
+
+my %tmpl_files = ( '--first' => 'aliases.tmpl',
+);
+
+Prophet::Test::Editor::edit(
+    tmpl_files => \%tmpl_files,
+    edit_callback => sub {
+        my %args = @_;
+        my $option = $args{option};
+
+        if ($option eq '--first') {
+            s/^pull -l/something different/; # both an add and a delete
+            s/(?<=foo = )bar baz/sigh/; # just change a value
+        }
+        print;
+    },
+    verify_callback => sub { },
+);
diff --git a/t/scripts/settings-editor.pl b/t/scripts/settings-editor.pl
index 6a6ad8a..ca5bd27 100755
--- a/t/scripts/settings-editor.pl
+++ b/t/scripts/settings-editor.pl
@@ -10,9 +10,7 @@ my %tmpl_files = ( '--first' => 'settings-first.tmpl',
 );
 
 Prophet::Test::Editor::edit(
-    tmpl_files => { '--first' => 'settings-first.tmpl',
-                   '--second' => 'settings-second.tmpl',
-               },
+    tmpl_files => \%tmpl_files,
     edit_callback => sub {
         my %args = @_;
         my $option = $args{option};

commit 4c7aba772f3f9d30ac6fe43569ceaed190aa709a
Author: Christine Spang <spang at mit.edu>
Date:   Fri Jun 26 14:25:50 2009 +0300

    -w, not -W

diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 47dd29d..253070d 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -71,7 +71,7 @@ sub run {
         die "You don't have write permissions on "
             .$self->config_filename.", can't edit!\n"
             if (-e $self->config_filename && ! -w $self->config_filename)
-                || ! -W (File::Spec->splitpath($self->config_filename))[1];
+                || ! -w (File::Spec->splitpath($self->config_filename))[1];
         my $template = $self->make_template;
 
         while ( !$done ) {

commit 8defa13a5d4162cb9f95bf9046b570f4659cab7c
Author: Christine Spang <spang at mit.edu>
Date:   Fri Jun 26 22:32:17 2009 +0300

    Only pull a record ID from the end of primary_commands for specific commands, not all of them.
    
    Otherwise, commands that sometimes want the last part of their command
    string to look like an id/uuid but don't actually want the
    turn-this-into-my-command-uuid magic will hate you.

diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index fedc6b3..e16cabf 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -1,19 +1,13 @@
 package Prophet::CLI::Dispatcher;
 use Path::Dispatcher::Declarative -base;
 use Any::Moose;
+require Prophet::CLIContext;
 
 with 'Prophet::CLI::Parameters';
 
 our @PREFIXES = qw(Prophet::CLI::Command);
 sub add_command_prefix { unshift @PREFIXES, @_ }
 
-# "ticket display $ID" -> "ticket display --id=$ID"
-on qr{^ (.*) \s+ ( \d+ | [0-9a-zA-Z\-\_]{22} | [A-Z0-9]{36} ) $ }x => sub {
-    my $self = shift;
-    $self->context->set_arg(id => $2);
-    run($1, $self, @_);
-};
-
 on '' => sub {
     my $self = shift;
     if ($self->context->has_arg('version')) { run_command("Version")->($self) }
@@ -36,16 +30,27 @@ on qr{^(clone|pull) (\S+)$} => sub {
 };
 
 # log range => log --range range
-on qr{log\s*([0-9LATEST.~]+)} => sub {
+on qr{log\s+([0-9LATEST.~]+)} => sub {
     my $self = shift;
     $self->context->set_arg(range => $1);
     run('log', $self);
 };
 
+on [ qr/^(update|edit|show|display|delete|del|rm|history)$/,
+     qr/^$Prophet::CLIContext::ID_REGEX$/ ] => sub {
+    my $self = shift;
+    $self->context->set_id_from_primary_commands;
+    run($1, $self, @_);
+};
+
+on [ [ 'update', 'edit' ] ]      => run_command("Update");
+on [ [ 'show', 'display' ] ]     => run_command("Show");
+on [ [ 'delete', 'del', 'rm' ] ] => run_command("Delete");
+on history                       => run_command("History");
+
 on [ ['create', 'new'] ]         => run_command("Create");
-on [ ['show', 'display'] ]       => run_command("Show");
-on [ ['update', 'edit'] ]        => run_command("Update");
-on [ ['delete', 'del', 'rm'] ]   => run_command("Delete");
+on [ ['search', 'list', 'ls' ] ] => run_command("Search");
+on [ ['aliases', 'alias'] ]      => run_command('Aliases');
 on [ ['search', 'list', 'ls' ] ] => run_command("Search");
 on [ ['aliases', 'alias'] ]      => run_command('Aliases');
 
@@ -63,7 +68,6 @@ on log      => run_command("Log");
 on shell    => run_command("Shell");
 on export   => run_command('Export');
 on info     => run_command('Info');
-on history  => run_command('History');
 
 on push => sub {
     my $self = shift;
@@ -108,7 +112,7 @@ on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
     # alternate syntax (preferred):
     # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
     # prophet alias foo bar, etc.
-    elsif ( $arg =~ /^(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
+    elsif ( $arg =~ /^(?:add |set )?\s*(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
         my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
         $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
         if ( $new ) {
diff --git a/lib/Prophet/CLIContext.pm b/lib/Prophet/CLIContext.pm
index 0b4d8d9..fc89d14 100644
--- a/lib/Prophet/CLIContext.pm
+++ b/lib/Prophet/CLIContext.pm
@@ -143,7 +143,7 @@ The regex to use for matching the id argument (luid / uuid).
 
 =cut
 
-our $ID_REGEX = '^(?:\d+|[A-Za-z0-9\-\_]{22}|[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})$';
+our $ID_REGEX = '(?:\d+|[A-Za-z0-9\-\_]{22}|[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})';
 
 =head2 setup_from_args
 
@@ -172,14 +172,10 @@ sub require_uuid {
     my $self    = shift;
 
     if (!$self->has_uuid) {
-        my $type = $self->type;
         die "This command requires a luid or uuid (use --id to specify).\n";
     }
 }
 
-
-
-
 =head2 parse_args @args
 
 This routine pulls arguments (specified by --key=value or --key
@@ -201,10 +197,6 @@ sub parse_args {
     my @primary;
     push @primary, shift @args while ( $args[0] && $args[0] !~ /^-/ );
 
-    # "ticket show 4" should DWIM and "ticket show --id=4"
-    my $id_re = $ID_REGEX;
-    $self->set_arg( id => pop @primary ) if @primary && $primary[-1] =~ /$id_re/i;
-
     my $collecting_props = 0;
 
     $self->primary_commands( \@primary );
@@ -274,29 +266,59 @@ on the command-line, if possible. Being unable to figure out a uuid is fatal.
 sub set_type_and_uuid {
     my $self = shift;
 
+    $self->set_uuid;
+    $self->set_type;
+}
+
+sub set_uuid {
+    my $self = shift;
+
     if ( my $id = $self->delete_arg('id') ) {
         if ( $id =~ /^(\d+)$/ ) {
             $self->set_arg( luid => $id );
-        } else {
+        }
+        else {
             $self->set_arg( uuid => $id );
         }
     }
 
     if ( my $uuid = $self->delete_arg('uuid') ) {
         $self->uuid($uuid);
-    } elsif ( my $luid = $self->delete_arg('luid') ) {
+    }
+    elsif ( my $luid = $self->delete_arg('luid') ) {
         my $uuid = $self->handle->find_uuid_by_luid( luid => $luid );
         die "I have no UUID mapped to the local id '$luid'\n"
             if !defined($uuid);
         $self->uuid($uuid);
     }
+}
+
+sub set_type {
+    my $self = shift;
+
     if ( my $type = $self->delete_arg('type') ) {
         $self->type($type);
-    } elsif ( $self->primary_commands->[-2] ) {
+    }
+    # allowance for things like ticket show 77, where 'ticket' is the type
+    elsif (
+        $self->primary_commands->[-1] =~ qr/^$Prophet::CLIContext::ID_REGEX$/
+            && $self->primary_commands->[-3] ) {
+        $self->type( $self->primary_commands->[-3] );
+    }
+    elsif ( $self->primary_commands->[-2] ) {
         $self->type( $self->primary_commands->[-2] );
     }
 }
 
+sub set_id_from_primary_commands {
+    my $self = shift;
+
+    if ( (my $id = pop @{$self->primary_commands}) =~ $ID_REGEX ) {
+        $self->set_arg( id => $id );
+        $self->set_uuid;
+    }
+}
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/t/cli-arg-parsing.t b/t/cli-arg-parsing.t
index fe7a735..29f8c1d 100644
--- a/t/cli-arg-parsing.t
+++ b/t/cli-arg-parsing.t
@@ -1,6 +1,6 @@
 use warnings;
 use strict;
-use Prophet::Test tests => 47;
+use Prophet::Test tests => 44;
 use Test::Exception;
 
 use File::Temp qw'tempdir';
@@ -80,13 +80,6 @@ is($context->arg_names, 0, 'no args were set');
 is($context->prop_names, 0, 'no props were set');
 reset_context($context);
 
-diag('primary commands only, grabbing uuid from the CLI');
-$context->parse_args(qw(show 10));
-is_deeply($context->primary_commands, [ 'show' ], 'primary commands are correct');
-is($context->arg('id'), '10', 'id was grabbed from primary commands');
-is($context->prop_names, 0, 'no props were set');
-reset_context($context);
-
 diag('primary commands + args with no values');
 $context->parse_args(qw(show --verbose --test));
 is_deeply($context->primary_commands, [ 'show' ], 'primary commands are correct');
@@ -166,7 +159,7 @@ reset_context($context);
 # XXX other errors?
 
 diag('put it all together with setup_from_args');
-$context->setup_from_args( 'bug', 'show', $luid );
+$context->setup_from_args( 'bug', 'show', '--id', $luid );
 is_deeply($context->primary_commands, [ 'bug', 'show' ],
     'primary commands are correct');
 is($context->uuid, $uuid, 'uuid is correct');
diff --git a/t/cli.t b/t/cli.t
index d13309c..493bfc6 100644
--- a/t/cli.t
+++ b/t/cli.t
@@ -1,11 +1,26 @@
 #!/usr/bin/perl
 use warnings;
 use strict;
-use Prophet::Test tests => 2;
+use Prophet::Test tests => 9;
 
 as_alice {
     run_command(qw(init));
     like(run_command(qw(create --type Bug -- --status new --from alice)), qr/Created Bug/, "Created a record as alice");
+
     like(run_command(qw(show 1 --type Bug --batch)), qr/id: 1/, "'show 1' dwims");
-};
+    like(run_command(qw(display 1 --type Bug --batch)), qr/id: 1/, "'display 1' dwims");
+
+    like(run_command(qw(update 1 --type Bug --batch -- status=open)),
+        qr/Bug 1 \(.+\) updated/, "'update 1' dwims");
+    like(run_command(qw(edit 1 --type Bug --batch -- status=new)),
+        qr/Bug 1 \(.+\) updated/, "'edit 1' dwims");
 
+    like(run_command(qw(history 1 --type Bug --batch)), qr/^ alice\@example.com/, "'history 1' dwims");
+
+    like(run_command(qw(delete 1 --type Bug --batch)), qr/Bug (.+) deleted/, "'delete 1' dwims");
+    run_command(qw(create --type Bug -- --status new --from alice));
+    like(run_command(qw(del 2 --type Bug --batch)), qr/Bug (.+) deleted/, "'del 2' dwims");
+    run_command(qw(create --type Bug -- --status new --from alice));
+    like(run_command(qw(rm 3 --type Bug --batch)), qr/Bug (.+) deleted/, "'rm 3' dwims");
+
+};

commit 51ae9fe731fc99cb9570e531737d269ed28a9264
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 29 12:07:46 2009 +0300

    sometimes UUIDs contain lowercase letters

diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index e16cabf..16679d1 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -37,7 +37,7 @@ on qr{log\s+([0-9LATEST.~]+)} => sub {
 };
 
 on [ qr/^(update|edit|show|display|delete|del|rm|history)$/,
-     qr/^$Prophet::CLIContext::ID_REGEX$/ ] => sub {
+     qr/^$Prophet::CLIContext::ID_REGEX$/i ] => sub {
     my $self = shift;
     $self->context->set_id_from_primary_commands;
     run($1, $self, @_);
diff --git a/lib/Prophet/CLIContext.pm b/lib/Prophet/CLIContext.pm
index fc89d14..98c12aa 100644
--- a/lib/Prophet/CLIContext.pm
+++ b/lib/Prophet/CLIContext.pm
@@ -301,7 +301,7 @@ sub set_type {
     }
     # allowance for things like ticket show 77, where 'ticket' is the type
     elsif (
-        $self->primary_commands->[-1] =~ qr/^$Prophet::CLIContext::ID_REGEX$/
+        $self->primary_commands->[-1] =~ qr/^$Prophet::CLIContext::ID_REGEX$/i
             && $self->primary_commands->[-3] ) {
         $self->type( $self->primary_commands->[-3] );
     }

commit 9ff21d8419a90cfa4a603319f9738ad849e97280
Merge: 51ae9fe... caef981...
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 29 12:08:17 2009 +0300

    Merge branch 'master' of fsck.com:/git/prophet into config-gitlike


commit fef857c0e3fa5010aa6e5ed75d44d1281b86e80f
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 29 15:36:06 2009 +0300

    Old config -> new config migration code.
    
    Will automatically convert the old config file that would have been
    loaded and any config files in locations that will be loaded under the
    new layout to the new config format. For example, if you have both a
    replica-specific config file for the replica you're using and a file in
    ~/.sdrc, both will be converted. If config files are found in deprecated
    locations (e.g. replica_root/prophetrc), the new config file will be
    written to the new preferred location (replica_root/config).
    
    Backups are made of the old config files; they're located at
    $old_config.bak.

diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index 2f6bfa3..778fafd 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -12,10 +12,12 @@ has app_handle => (
 );
 
 # reload config after setting values
-after set => sub  {
+override group_set => sub  {
     my $self = shift;
+    my ($filename, $args_ref, $override) = @_;
 
-    $self->load;
+    $self->SUPER::group_set($filename, $args_ref);
+    $self->load unless $override;
 };
 
 # per-replica config filename
@@ -133,6 +135,167 @@ sub display_name_for_uuid {
     return exists $sources_by_uuid{$uuid} ? $sources_by_uuid{$uuid} : $uuid;
 }
 
+### XXX BACKCOMPAT ONLY! We eventually want to kill this hash, modifier and
+### the following methods.
+
+# None of these need to have values mucked with at all, just the keys
+# migrated from old to new.
+our %KEYS_CONVERSION_TABLE = (
+    'email_address' => 'user.email-address',
+    'default_group_ticket_list' => 'ticket.list.default-group',
+    'default_sort_ticket_list' => 'ticket.list.default-sort',
+    'summary_format_ticket' => 'ticket.summary-format',
+    'default_summary_format' => 'record.summary-format',
+    'common_ticket_props' => 'ticket.common-props',
+    'disable_ticket_show_history_by_default' => 'ticket.show.disable-history',
+);
+
+override load => sub  {
+    my $self = shift;
+
+    Prophet::CLI->end_pager();
+
+    # Do backcompat stuff.
+    for my $file ( ($self->_old_app_config_file, $self->dir_file,
+            $self->user_file, $self->global_file) ) {
+        my $content = -f $file ? Prophet::Util->slurp($file) : '[';
+
+        # config file is old
+
+        # Also "converts" empty files but that's fine. If it ever
+        # does happen, we get the positive benefit of writing the
+        # config format to it.
+        if ( $content !~ /\[/ ) {
+            print "Detected old format config file $file. Converting to ".
+                  "new format... ";
+
+            # read in and parse old config
+            my $config = { _sources => {}, _aliases => {} };
+            $self->_load_old_config_from_file( $file, $config );
+            my $aliases = delete $config->{_aliases};
+            my $sources = delete $config->{_sources};
+
+            # new configuration will include a config format version #
+            my @config_to_set = ( {
+                    key => 'core.config-format-version',
+                    value => '0',
+            } );
+
+            # convert its keys to new-style keys by comparing to a conversion
+            # table
+            for my $key ( keys %$config ) {
+                die "Unknown key '$key' in old format config file '$file'."
+                    ." Remove it or ask\non irc.freenode.net #prophet if you"
+                    ." think this is a bug.\n"
+                        unless exists $KEYS_CONVERSION_TABLE{$key};
+                push @config_to_set, {
+                    key   => $KEYS_CONVERSION_TABLE{$key},
+                    value => $config->{$key},
+                };
+            }
+            # convert its aliases
+            for my $alias ( keys %$aliases ) {
+                push @config_to_set, {
+                    key   => "alias.'$alias'",
+                    value => $aliases->{$alias},
+                };
+            }
+            # convert its sources
+            for my $name ( keys %$sources ) {
+                my ($url, $uuid) = split(/ \| /, $sources->{$name}, 2);
+                push @config_to_set, {
+                    key   => "replica.'$name'.url",
+                    value => $url,
+                }, {
+                    key   => "replica.'$name'.uuid",
+                    value => $uuid,
+                };
+            }
+            # move the old config file to a backup
+            my $backup_file = $file;
+            unless ( $self->_deprecated_repo_config_names->{$file} ) {
+                $backup_file = "$file.bak";
+                rename $file, $backup_file;
+            }
+
+            # we want to write the new file to a supported filename if
+            # it's from a deprecated config name (replica/prophetrc)
+            $file = File::Spec->catfile( $self->app_handle->handle->fs_root, 'config' )
+                if $self->_deprecated_repo_config_names->{$file};
+
+            # write the new config file (with group_set)
+            $self->group_set( $file, \@config_to_set, 1);
+
+            # tell the user that we're done
+            print "done.\nOld config can be found at $backup_file; "
+                  ,"new config is $file.\n\n";
+
+            Prophet::CLI->start_pager();
+        }
+
+    }
+
+    # Do a regular load.
+    $self->SUPER::load;
+};
+
+sub _deprecated_repo_config_names {
+    my $self = shift;
+
+    my %filenames = ( File::Spec->catfile( $self->app_handle->handle->fs_root =>
+            'prophetrc' ) => 1 );
+
+    return wantarray ? %filenames : \%filenames;
+};
+
+sub _old_app_config_file {
+    my $self = shift;
+    my $config_env_var
+        = $_{config_env_var} ?  $_{config_env_var} : 'PROPHET_APP_CONFIG';
+
+    return $self->_file_if_exists($ENV{$config_env_var})
+        || $self->_file_if_exists( $self->_old_replica_config_file)
+        || $self->_file_if_exists( File::Spec->catfile( $ENV{'HOME'} => '.prophetrc' ))
+        || $self->_old_replica_config_file
+}
+
+sub _old_replica_config_file {
+    my $self = shift;
+     return
+     $self->_file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' )) ||
+     $self->_file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'prophetrc' )) ||
+      File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' );
+}
+
+sub _load_old_config_from_file {
+    my $self   = shift;
+    my $file   = shift;
+    my $config = shift || {};
+
+    for my $line (Prophet::Util->slurp($file) ) {
+        $line =~ s/\#.*$//; # strip comments
+        next unless ($line =~ /^(.*?)\s*=\s*(.*)$/);
+        my $key = $1;
+        my $val = $2;
+        if ($key =~ m!alias\s+(.+)!) {
+            $config->{_aliases}->{$1} = $val;
+        } elsif ($key =~ m!source\s+(.+)!) {
+            $config->{_sources}->{$1} = $val;
+        } else {
+            $config->{$key} = $val;
+        }
+    }
+    $config->{_aliases} ||= {}; # default aliases is null.
+    $config->{_sources} ||= {}; # default to no sources.
+}
+
+sub _file_if_exists {
+    my $self = shift;
+    my $file = shift || ''; # quiet warnings
+
+    return (-e $file) ? $file : '';
+}
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/t/aliases.t b/t/aliases.t
index b8f5c8a..90a67f8 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -2,7 +2,7 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 32;
+use Prophet::Test tests => 33;
 use File::Temp qw/tempfile/;
 
 $ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
@@ -13,9 +13,11 @@ diag("Using config file $ENV{PROPHET_APP_CONFIG}");
 mkdir $ENV{PROPHET_REPO};
 
 use_ok('Prophet::CLI');
-use_ok('Prophet::Config');
 
-my $config = Prophet::CLI->new()->config;
+my $a = Prophet::CLI->new();
+can_ok($a, 'app_handle');
+can_ok($a->app_handle, 'config');
+my $config = $a->config;
 $config->load;
 
 is_deeply( scalar $config->aliases, {}, 'initial alias is empty' );
@@ -184,7 +186,8 @@ open my $fh, '<', $ENV{'PROPHET_APP_CONFIG'}
   or die "failed to open $ENV{'PROPHET_APP_CONFIG'}: $!";
 { local $/; $content = <$fh>; }
 is( $content, <<EOF, 'content in config' );
-
+[core]
+	config-format-version = 0
 [alias]
 	pull -a = pull --all
 	pull -l = pull --local
diff --git a/t/cli-arg-translation.t b/t/cli-arg-translation.t
index 74ae7bc..4510933 100644
--- a/t/cli-arg-translation.t
+++ b/t/cli-arg-translation.t
@@ -1,6 +1,6 @@
 use warnings;
 use strict;
-use Prophet::Test tests => 7;
+use Prophet::Test tests => 9;
 use File::Temp qw'tempdir';
 
 # test coverage for Prophet::CLI::Command arg translation
@@ -9,6 +9,8 @@ use_ok('Prophet::CLI');
 $ENV{'PROPHET_REPO'} = tempdir( CLEANUP => ! $ENV{PROPHET_DEBUG}  ) . '/repo-' . $$;
 
 my $cli = Prophet::CLI->new();
+can_ok($cli, 'app_handle');
+can_ok($cli, 'handle');
 my $cxn = $cli->handle;
 isa_ok( $cxn, 'Prophet::Replica', "Got the cxn" );
 
diff --git a/t/config.t b/t/config.t
index 88c47fd..e96b4ba 100644
--- a/t/config.t
+++ b/t/config.t
@@ -34,8 +34,10 @@ is( scalar @keys, 0, 'no config options are set' );
     local $ENV{'PROPHET_APP_CONFIG'}
         = File::Spec->catfile($repo,'test_app.conf');
 
+    my $app_handle = Prophet::CLI->new->app_handle;
     my $conf = Prophet::Config->new(
-        app_handle => Prophet::CLI->new->app_handle,
+        app_handle => $app_handle,
+        handle => $app_handle->handle,
         confname => 'testrc',
     );
     $conf->load;

commit 2b3047d5941bcd1f39276b6e5bdb79838b607fc4
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 29 17:42:19 2009 +0300

    Quiet a warning when primary_commands is empty

diff --git a/lib/Prophet/CLIContext.pm b/lib/Prophet/CLIContext.pm
index 98c12aa..609e68d 100644
--- a/lib/Prophet/CLIContext.pm
+++ b/lib/Prophet/CLIContext.pm
@@ -300,7 +300,7 @@ sub set_type {
         $self->type($type);
     }
     # allowance for things like ticket show 77, where 'ticket' is the type
-    elsif (
+    elsif ( $self->primary_commands->[-1] &&
         $self->primary_commands->[-1] =~ qr/^$Prophet::CLIContext::ID_REGEX$/i
             && $self->primary_commands->[-3] ) {
         $self->type( $self->primary_commands->[-3] );

commit 9f8fc6ab5d1cd04894878d7a834918dd4f080fb0
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 29 18:10:24 2009 +0300

    Set core.config-format-version config var in any config file we set to that doesn't have it set in already.

diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index 778fafd..dfc79af 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -11,15 +11,31 @@ has app_handle => (
     required => 1
 );
 
+use constant FORMAT_VERSION => 0;
+
 # reload config after setting values
 override group_set => sub  {
     my $self = shift;
     my ($filename, $args_ref, $override) = @_;
 
+    # Set a config format version on this config file if
+    # it doesn't have one already.
+    push @$args_ref, {
+        key => 'core.config-format-version',
+        value => FORMAT_VERSION,
+    } unless _file_has_config_format_version( $filename );
+
     $self->SUPER::group_set($filename, $args_ref);
     $self->load unless $override;
 };
 
+sub _file_has_config_format_version {
+    my $filename = shift;
+    my $content = -f  $filename ? Prophet::Util->slurp($filename) : '';
+
+    return $content =~ 'core.config-format-version';
+}
+
 # per-replica config filename
 override dir_file => sub { 'config' };
 
@@ -178,7 +194,7 @@ override load => sub  {
             # new configuration will include a config format version #
             my @config_to_set = ( {
                     key => 'core.config-format-version',
-                    value => '0',
+                    value => FORMAT_VERSION,
             } );
 
             # convert its keys to new-style keys by comparing to a conversion
diff --git a/t/config.t b/t/config.t
index e96b4ba..3b353bd 100644
--- a/t/config.t
+++ b/t/config.t
@@ -87,6 +87,7 @@ $repo/test_app.conf
 Your configuration:
 
 alias.tlist=ticket list
+core.config-format-version=0
 replica.sd.url=http://fsck.com/sd/
 replica.sd.uuid=32b13934-910a-4792-b5ed-c9977b212245
 test.foo=bar

commit 9d08f73fc23894f580ad19a6132bdfcc93247183
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jun 29 19:20:01 2009 +0300

    Update MANIFEST

diff --git a/MANIFEST b/MANIFEST
index 6ea2838..a4d8b2d 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -95,8 +95,10 @@ lib/Prophet/Server/ViewHelpers/ParamFromFunction.pm
 lib/Prophet/Server/ViewHelpers/Widget.pm
 lib/Prophet/Test.pm
 lib/Prophet/Test/Arena.pm
+lib/Prophet/Test/Editor.pm
 lib/Prophet/Test/Participant.pm
 lib/Prophet/Util.pm
+lib/Prophet/UUIDGenerator.pm
 lib/Prophet/Web/Field.pm
 lib/Prophet/Web/FunctionResult.pm
 lib/Prophet/Web/Menu.pm
@@ -139,10 +141,13 @@ t/real-conflicting-merge.t
 t/references.t
 t/res-conflict-3.t
 t/resty-server.t
+t/scripts/aliases-editor.pl
+t/scripts/settings-editor.pl
 t/search.t
 t/Settings/lib/App/Settings.pm
 t/Settings/lib/App/Settings/Bug.pm
 t/Settings/lib/App/Settings/CLI.pm
+t/Settings/t/database-settings-editor.t
 t/Settings/t/sync-database-settings.t
 t/simple-conflicting-merge.t
 t/simple-push.t
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
index 5b5496b..1a9b124 100644
--- a/MANIFEST.SKIP
+++ b/MANIFEST.SKIP
@@ -8,3 +8,4 @@
 pm_to_blib
 .gitignore
 ^Makefile(?:\.old)?$
+.prove

commit 1285e73dd8ea3e9c3afd6daeaf1b89054227a76c
Author: Christine Spang <spang at mit.edu>
Date:   Thu Jul 2 11:38:46 2009 -0400

    Move aliases cli parsing logic into the Aliases command class

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 43be982..51f922a 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -144,6 +144,53 @@ sub process_template {
     }
 }
 
+sub parse_cli_arg {
+    my $self = shift;
+    my ($cmd, $arg) = @_;
+
+    if ( $arg =~ /^show\b/ ) {
+        $self->context->set_arg(show => 1);
+    }
+    elsif ( $arg =~ /^edit\b/ ) {
+        $self->context->set_arg(edit => 1);
+    }
+    # arg *might* be quoted
+    elsif ( $arg =~ /^delete\s+"?([^"]+)"?/ ) {
+        $self->context->set_arg(delete => $1);
+    }
+    # prophet alias "foo bar" = "foo baz"
+    # prophet alias foo = bar
+    # prophet alias add foo bar = "bar baz"
+    # prophet alias add foo bar = bar baz
+    elsif ( $arg =~ 
+        /^(?:add |set )?\s*(?:(?:"([^"]+)"|([^"]+))\s+=\s+(?:"([^"]+)"|([^"]+)))$/ ) {
+        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
+        $self->context->set_arg(set => "$orig=$new");
+    }
+    # prophet alias "foo = bar"
+    # prophet alias "foo bar = foo baz"
+    elsif ( $arg =~ /^(?:add |set )?\s*"([^"]+=[^"]+)"$/ ) {
+        $self->context->set_arg(set => $1);
+    }
+    # alternate syntax (preferred):
+    # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
+    # prophet alias foo bar, etc.
+    elsif ( $arg =~ /^(?:add |set )?\s*(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
+        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
+        if ( $new ) {
+            $self->context->set_arg(set => "$orig=$new");
+        }
+        else {
+            $self->context->set_arg(set => $orig);
+        }
+    }
+    else {
+        die 'no idea what you mean, sorry';
+    }
+}
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index 16679d1..2e05e45 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -84,48 +84,17 @@ on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
     my $cmd = $1;
     my $arg = $2;
 
-    if ( $arg =~ /^show\b/ ) {
-        $self->context->set_arg(show => 1);
-    }
-    elsif ( $arg =~ /^edit\b/ ) {
-        $self->context->set_arg(edit => 1);
-    }
-    # arg *might* be quoted
-    elsif ( $arg =~ /^delete\s+"?([^"]+)"?/ ) {
-        $self->context->set_arg(delete => $1);
-    }
-    # prophet alias "foo bar" = "foo baz"
-    # prophet alias foo = bar
-    # prophet alias add foo bar = "bar baz"
-    # prophet alias add foo bar = bar baz
-    elsif ( $arg =~ 
-        /^(?:add |set )?\s*(?:(?:"([^"]+)"|([^"]+))\s+=\s+(?:"([^"]+)"|([^"]+)))$/ ) {
-        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
-        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
-        $self->context->set_arg(set => "$orig=$new");
-    }
-    # prophet alias "foo = bar"
-    # prophet alias "foo bar = foo baz"
-    elsif ( $arg =~ /^(?:add |set )?\s*"([^"]+=[^"]+)"$/ ) {
-        $self->context->set_arg(set => $1);
-    }
-    # alternate syntax (preferred):
-    # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
-    # prophet alias foo bar, etc.
-    elsif ( $arg =~ /^(?:add |set )?\s*(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
-        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
-        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
-        if ( $new ) {
-            $self->context->set_arg(set => "$orig=$new");
-        }
-        else {
-            $self->context->set_arg(set => $orig);
-        }
-    }
-    else {
-        die 'no idea what you mean, sorry';
+    my @classes = $self->class_names('Aliases');
+    for my $class (@classes) {
+        Prophet::App->try_to_require($class) or next;
+        my $aliases_cmd = $class->new(
+            context => $self->context,
+        );
+        $aliases_cmd->parse_cli_arg($cmd, $arg);
+        return run( $cmd, $self, @_ );
     }
-    run( $cmd, $self, @_ );
+
+    die "Could not find 'Aliases' command class";
 };
 
 sub run_command {

commit a7251c33620357a9be0efb748983363287ec6d9e
Merge: 1285e73... 64485be...
Author: Christine Spang <spang at mit.edu>
Date:   Sun Jul 5 20:27:23 2009 -0400

    Merge branch 'master' into config-gitlike


commit 6e48c78ac98acf607203a2fac0f3b5067ddc13db
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Mon Jul 6 16:16:17 2009 -0400

    Extract a method from what was otherwise kind of a giant ball of goo

diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index dfc79af..292d9a7 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -182,6 +182,22 @@ override load => sub  {
         # does happen, we get the positive benefit of writing the
         # config format to it.
         if ( $content !~ /\[/ ) {
+
+            $self->convert_ancient_config_file($file);
+        }
+
+    }
+
+    Prophet::CLI->start_pager();
+
+    # Do a regular load.
+    $self->SUPER::load;
+};
+
+
+sub convert_ancient_config_file {
+            my $self = shift;
+            my $file = shift;
             print "Detected old format config file $file. Converting to ".
                   "new format... ";
 
@@ -246,20 +262,13 @@ override load => sub  {
             print "done.\nOld config can be found at $backup_file; "
                   ,"new config is $file.\n\n";
 
-            Prophet::CLI->start_pager();
-        }
-
-    }
+}
 
-    # Do a regular load.
-    $self->SUPER::load;
-};
 
 sub _deprecated_repo_config_names {
     my $self = shift;
 
-    my %filenames = ( File::Spec->catfile( $self->app_handle->handle->fs_root =>
-            'prophetrc' ) => 1 );
+    my %filenames = ( File::Spec->catfile( $self->app_handle->handle->fs_root => 'prophetrc' ) => 1 );
 
     return wantarray ? %filenames : \%filenames;
 };

commit 35ff302990eeee5a6a7099b770e27fda9894ba03
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Mon Jul 6 17:43:28 2009 -0400

    looser uuid regex, factor out a set_id method

diff --git a/lib/Prophet/CLIContext.pm b/lib/Prophet/CLIContext.pm
index 609e68d..244e02c 100644
--- a/lib/Prophet/CLIContext.pm
+++ b/lib/Prophet/CLIContext.pm
@@ -143,7 +143,7 @@ The regex to use for matching the id argument (luid / uuid).
 
 =cut
 
-our $ID_REGEX = '(?:\d+|[A-Za-z0-9\-\_]{22}|[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})';
+our $ID_REGEX = qr'(?:\d+|[A-Za-z0-9\-\_]{22}|[0-9a-fA-F\-]{32,36})';
 
 =head2 setup_from_args
 
@@ -312,12 +312,20 @@ sub set_type {
 
 sub set_id_from_primary_commands {
     my $self = shift;
-
+    
     if ( (my $id = pop @{$self->primary_commands}) =~ $ID_REGEX ) {
-        $self->set_arg( id => $id );
-        $self->set_uuid;
+        $self->set_id($id);
     }
+
 }
+sub set_id {
+    my $self = shift;
+    my $id = shift;
+    $self->set_arg( id => $id );
+    $self->set_uuid;
+}
+
+
 
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;

commit 0b8bf4fee509f4bffcfe086e4239499d647b83e3
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Tue Jul 7 16:01:00 2009 -0400

    moved a whole bunch of sd-specific config upgrade stuff out of the prophet core

diff --git a/lib/Prophet/Config.pm b/lib/Prophet/Config.pm
index 292d9a7..654b978 100644
--- a/lib/Prophet/Config.pm
+++ b/lib/Prophet/Config.pm
@@ -20,9 +20,9 @@ override group_set => sub  {
 
     # Set a config format version on this config file if
     # it doesn't have one already.
-    push @$args_ref, {
+    unshift @$args_ref, {
         key => 'core.config-format-version',
-        value => FORMAT_VERSION,
+        value => $self->FORMAT_VERSION,
     } unless _file_has_config_format_version( $filename );
 
     $self->SUPER::group_set($filename, $args_ref);
@@ -151,168 +151,7 @@ sub display_name_for_uuid {
     return exists $sources_by_uuid{$uuid} ? $sources_by_uuid{$uuid} : $uuid;
 }
 
-### XXX BACKCOMPAT ONLY! We eventually want to kill this hash, modifier and
-### the following methods.
-
-# None of these need to have values mucked with at all, just the keys
-# migrated from old to new.
-our %KEYS_CONVERSION_TABLE = (
-    'email_address' => 'user.email-address',
-    'default_group_ticket_list' => 'ticket.list.default-group',
-    'default_sort_ticket_list' => 'ticket.list.default-sort',
-    'summary_format_ticket' => 'ticket.summary-format',
-    'default_summary_format' => 'record.summary-format',
-    'common_ticket_props' => 'ticket.common-props',
-    'disable_ticket_show_history_by_default' => 'ticket.show.disable-history',
-);
-
-override load => sub  {
-    my $self = shift;
-
-    Prophet::CLI->end_pager();
-
-    # Do backcompat stuff.
-    for my $file ( ($self->_old_app_config_file, $self->dir_file,
-            $self->user_file, $self->global_file) ) {
-        my $content = -f $file ? Prophet::Util->slurp($file) : '[';
 
-        # config file is old
-
-        # Also "converts" empty files but that's fine. If it ever
-        # does happen, we get the positive benefit of writing the
-        # config format to it.
-        if ( $content !~ /\[/ ) {
-
-            $self->convert_ancient_config_file($file);
-        }
-
-    }
-
-    Prophet::CLI->start_pager();
-
-    # Do a regular load.
-    $self->SUPER::load;
-};
-
-
-sub convert_ancient_config_file {
-            my $self = shift;
-            my $file = shift;
-            print "Detected old format config file $file. Converting to ".
-                  "new format... ";
-
-            # read in and parse old config
-            my $config = { _sources => {}, _aliases => {} };
-            $self->_load_old_config_from_file( $file, $config );
-            my $aliases = delete $config->{_aliases};
-            my $sources = delete $config->{_sources};
-
-            # new configuration will include a config format version #
-            my @config_to_set = ( {
-                    key => 'core.config-format-version',
-                    value => FORMAT_VERSION,
-            } );
-
-            # convert its keys to new-style keys by comparing to a conversion
-            # table
-            for my $key ( keys %$config ) {
-                die "Unknown key '$key' in old format config file '$file'."
-                    ." Remove it or ask\non irc.freenode.net #prophet if you"
-                    ." think this is a bug.\n"
-                        unless exists $KEYS_CONVERSION_TABLE{$key};
-                push @config_to_set, {
-                    key   => $KEYS_CONVERSION_TABLE{$key},
-                    value => $config->{$key},
-                };
-            }
-            # convert its aliases
-            for my $alias ( keys %$aliases ) {
-                push @config_to_set, {
-                    key   => "alias.'$alias'",
-                    value => $aliases->{$alias},
-                };
-            }
-            # convert its sources
-            for my $name ( keys %$sources ) {
-                my ($url, $uuid) = split(/ \| /, $sources->{$name}, 2);
-                push @config_to_set, {
-                    key   => "replica.'$name'.url",
-                    value => $url,
-                }, {
-                    key   => "replica.'$name'.uuid",
-                    value => $uuid,
-                };
-            }
-            # move the old config file to a backup
-            my $backup_file = $file;
-            unless ( $self->_deprecated_repo_config_names->{$file} ) {
-                $backup_file = "$file.bak";
-                rename $file, $backup_file;
-            }
-
-            # we want to write the new file to a supported filename if
-            # it's from a deprecated config name (replica/prophetrc)
-            $file = File::Spec->catfile( $self->app_handle->handle->fs_root, 'config' )
-                if $self->_deprecated_repo_config_names->{$file};
-
-            # write the new config file (with group_set)
-            $self->group_set( $file, \@config_to_set, 1);
-
-            # tell the user that we're done
-            print "done.\nOld config can be found at $backup_file; "
-                  ,"new config is $file.\n\n";
-
-}
-
-
-sub _deprecated_repo_config_names {
-    my $self = shift;
-
-    my %filenames = ( File::Spec->catfile( $self->app_handle->handle->fs_root => 'prophetrc' ) => 1 );
-
-    return wantarray ? %filenames : \%filenames;
-};
-
-sub _old_app_config_file {
-    my $self = shift;
-    my $config_env_var
-        = $_{config_env_var} ?  $_{config_env_var} : 'PROPHET_APP_CONFIG';
-
-    return $self->_file_if_exists($ENV{$config_env_var})
-        || $self->_file_if_exists( $self->_old_replica_config_file)
-        || $self->_file_if_exists( File::Spec->catfile( $ENV{'HOME'} => '.prophetrc' ))
-        || $self->_old_replica_config_file
-}
-
-sub _old_replica_config_file {
-    my $self = shift;
-     return
-     $self->_file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' )) ||
-     $self->_file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'prophetrc' )) ||
-      File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' );
-}
-
-sub _load_old_config_from_file {
-    my $self   = shift;
-    my $file   = shift;
-    my $config = shift || {};
-
-    for my $line (Prophet::Util->slurp($file) ) {
-        $line =~ s/\#.*$//; # strip comments
-        next unless ($line =~ /^(.*?)\s*=\s*(.*)$/);
-        my $key = $1;
-        my $val = $2;
-        if ($key =~ m!alias\s+(.+)!) {
-            $config->{_aliases}->{$1} = $val;
-        } elsif ($key =~ m!source\s+(.+)!) {
-            $config->{_sources}->{$1} = $val;
-        } else {
-            $config->{$key} = $val;
-        }
-    }
-    $config->{_aliases} ||= {}; # default aliases is null.
-    $config->{_sources} ||= {}; # default to no sources.
-}
 
 sub _file_if_exists {
     my $self = shift;
diff --git a/t/aliases.t b/t/aliases.t
index 90a67f8..d15edf4 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -5,12 +5,10 @@ use strict;
 use Prophet::Test tests => 33;
 use File::Temp qw/tempfile/;
 
-$ENV{'PROPHET_REPO'} = $Prophet::Test::REPO_BASE . '/repo-' . $$;
 $ENV{'PROPHET_APP_CONFIG'} = (tempfile(UNLINK => !$ENV{PROPHET_DEBUG}))[1];
 diag("Using config file $ENV{PROPHET_APP_CONFIG}");
 
 # since we don't initialize the db for these tests, make the repo dir
-mkdir $ENV{PROPHET_REPO};
 
 use_ok('Prophet::CLI');
 
@@ -182,10 +180,10 @@ is_deeply(
 
 # check content in config
 my $content;
-open my $fh, '<', $ENV{'PROPHET_APP_CONFIG'}
-  or die "failed to open $ENV{'PROPHET_APP_CONFIG'}: $!";
+open my $fh, '<', $ENV{'PROPHET_APP_CONFIG'} or die "failed to open $ENV{'PROPHET_APP_CONFIG'}: $!";
 { local $/; $content = <$fh>; }
 is( $content, <<EOF, 'content in config' );
+
 [core]
 	config-format-version = 0
 [alias]

commit cb4889de413f5497394c14ab7cfe893c9875ba63
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Tue Jul 7 16:12:30 2009 -0400

    Removed duplicated dispatcher stanzas

diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index 2e05e45..98bd653 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -51,8 +51,6 @@ on history                       => run_command("History");
 on [ ['create', 'new'] ]         => run_command("Create");
 on [ ['search', 'list', 'ls' ] ] => run_command("Search");
 on [ ['aliases', 'alias'] ]      => run_command('Aliases');
-on [ ['search', 'list', 'ls' ] ] => run_command("Search");
-on [ ['aliases', 'alias'] ]      => run_command('Aliases');
 
 on version  => run_command("Version");
 on init     => run_command("Init");

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list