AnyEvent-Git-Wrapper

 view release on metacpan or  search on metacpan

lib/AnyEvent/Git/Wrapper.pm  view on Meta::CPAN


# ABSTRACT: Wrap git command-line interface without blocking
our $VERSION = '0.10'; # VERSION


sub new
{
  my $class = shift;
  
  my $args;
  if(scalar @_ == 1)
  {
    my $arg = shift;
    if(ref $arg eq 'HASH') { $args = $arg }
    elsif(blessed $arg)    { $args = { dir => "$arg" } }
    elsif(! ref $arg)      { $args = { dir => $arg } }
    else { die "Singlearg must be hashref, scalar or stringify-able object" }
  }
  else
  {
    my($dir, %opts) = @_;
    $dir = "$dir" if blessed $dir;
    $args = { dir => $dir, %opts };
  }
  my $cache_version = delete $args->{cache_version};
  my $self = $class->SUPER::new($args);
  $self->{ae_cache_version} = $cache_version;
  $self;
}


sub RUN
{
  my($self) = shift;
  my $cv;
  if(ref($_[-1]) eq 'CODE')
  {
    $cv = AE::cv;
    $cv->cb(pop);
  }
  elsif(eval { $_[-1]->isa('AnyEvent::CondVar') })
  {
    $cv = pop;
  }
  else
  {
    return $self->SUPER::RUN(@_);
  }

  my $cmd = shift;

  my $customize;
  $customize = pop if ref($_[-1]) eq 'CODE';
  
  my ($parts, $in) = Git::Wrapper::_parse_args( $cmd, @_ );
  my @out;
  my @err;

  my $ipc = AnyEvent::Open3::Simple->new(
    on_stdout => \@out,
    on_stderr => \@err,
    on_error  => sub {
      #my($error) = @_;
      $cv->croak(
        Git::Wrapper::Exception->new(
          output => \@out,
          error  => \@err,
          status => -1,
        )
      );
    },
    on_exit   => sub {
      my(undef, $exit, $signal) = @_;
      
      # borrowed from superclass, see comment there
      my $stupid_status = $cmd eq 'status' && @out && ! @err;
      
      if(($exit || $signal) && ! $stupid_status)
      {
        $cv->croak(
          Git::Wrapper::Exception->new(
            output => \@out,
            error  => \@err,
            status => $exit,
          )
        );
      }
      else
      {
        $self->{err} = \@err;
        $self->{out} = \@out;
        $cv->send(\@out, \@err);
      }
    },
    $customize ? $customize->() : ()
  );
  
  do {
    my $d = pushd $self->dir unless $cmd eq 'clone';
    
    my @cmd = ( $self->git, @$parts );
    
    local $ENV{GIT_EDITOR} = $^O eq 'MSWin32' ? 'cmd /c "exit 2"' : '';
    $ipc->run(@cmd, \$in);
    
    undef $d;
  };
  
  $cv;
}


my %STATUS_CONFLICTS = map { $_ => 1 } qw<DD AU UD UA DU AA UU>;

sub status
{
  my($self) = shift;
  my $cv;
  if(ref($_[-1]) eq 'CODE')
  {
    $cv = AE::cv;

lib/AnyEvent/Git/Wrapper.pm  view on Meta::CPAN

    $self->RUN('version', sub {
      my $out = eval { shift->recv };
      if($@)
      {
        $cv->croak($@);
      }
      else
      {
        $self->{ae_version} = $out->[0];
        $self->{ae_version} =~ s/^git version //;
        $cv->send($self->{ae_version});
      }
    });
  }
  
  $cv;
}


1;

__END__

=pod

=encoding UTF-8

=head1 NAME

AnyEvent::Git::Wrapper - Wrap git command-line interface without blocking

=head1 VERSION

version 0.10

=head1 SYNOPSIS

 use AnyEvent::Git::Wrapper;
 
 # add all files and make a commit...
 my $git = AnyEvent::Git::Wrapper->new($dir);
 $git->add('.', sub {
   $git->commit({ message => 'initial commit' }, sub {
     say "made initial commit";
   });
 });

=head1 DESCRIPTION

B<DEPRECATED>: May go away at some point.

This module provides a non-blocking and blocking API for git in the style and using the data 
structures of L<Git::Wrapper>.  For methods that execute the git binary, if the last argument is 
either a code reference or an L<AnyEvent> condition variable, then the command is run in 
non-blocking mode and the result will be sent to the condition variable when the command completes.  
For most commands (all those but C<status>, C<log> and C<version>), the result comes back via the 
C<recv> method on the condition variable as two array references, one representing the standard out 
and the other being the standard error.  Because C<recv> will return just the first value if 
called in scalar context, you can retrieve just the output by calling C<recv> in scalar context.

 # ignoring stderr
 $git->branch(sub {
   my $out = shift->recv;
   foreach my $line (@$out)
   {
     ...
   }
 });
 
 # same thing, but saving stderr
 $git->branch(sub {
   my($out, $err) = shit->recv;
   foreach my $line(@$out)
   {
     ...
   }
 });

Like L<Git::Wrapper>, you can also access the standard output and error via the C<OUT> and C<ERR>, but care
needs to be taken that you either save the values immediately if other commands are being run at the same
time.

 $git->branch(sub {
   my $out = $git->OUT;
   foreach my $line (@$out)
   {
     ...
   }
 });

If git signals an error condition the condition variable will croak, so you will need to wrap your call
to C<recv> in an eval if you want to handle it:

 $git->branch(sub {
   my $out = eval { shift->recv };
   if($@)
   {
     warn "error: $@";
     return;
   }
   ...
 });

=head1 CONSTRUCTOR

=head2 new

 my $git = AnyEvent::Git::Wrapper->new('.');

The constructor takes all the same arguments as L<Git::Wrapper>, in addition to 
these options:

=over 4

=item cache_version

The first time the C<version> command is executed the value will be cached so
that C<git version> doesn't need to be executed again (via the C<version> method
only, this doesn't include if you call C<git version> using the C<RUN> method).
The default is false (no cache).

=back

=head1 METHODS

=head2 RUN

Run the given git command with the given arguments (see L<Git::Wrapper>).  If the last argument is
either a code reference or a condition variable then the command will be run in non-blocking mode
and a condition variable will be returned immediately.  Otherwise the command will be run in 
normal blocking mode, exactly like L<Git::Wrapper>.

If you provide this method with a condition variable it will use that to send the results of the
command.  If you provide a code reference it will create its own condition variable and attach
the code reference  to its callback.  Either way it will return the condition variable.

 # blocking
 $git->RUN($command, @arguments);
 
 # non-blocking callback
 $git->RUN($command, @arguments, sub {
   # $out is a list ref of stdout
   # $err is a list ref of stderr
   my($out, $err) = shift->recv;
 });
 
 # non-blocking cv
 my $cv = $git->RUN($command, @arguments, AE::cv);
 $cv->cb(sub {
   my($out, $err) = shift->recv;
 });

=head2 status

If called in blocking mode (without a code reference or condition variable as the last argument),
this method works exactly as with L<Git::Wrapper>.  If run in non blocking mode, the L<Git::Wrapper::Statuses>
object will be passed back via the C<recv> method on the condition variable.

 # blocking
 # $statuses isa Git::Wrapper::Statuses
 my $statuses = $git->status;

 # with a code ref
 $git->status(sub {
   # $statuses isa Git::Wrapper::Statuses 
   my $statuses = shift->recv;
   ...
 });
 
 # with a condition variable
 my $cv = $git->status(AE::cv)
 $cv->cb(sub {
   # $statuses isa Git::Wrapper::Statuses
   my $statuses = shift->recv;
   ...   
 });

=head2 log

This method has three different calling modes, blocking, non-blocking as commits arrive and non-blocking
processed at completion.

=over 4

=item blocking mode

 $git->log(@args);

Works exactly like L<Git::Wrapper>

=item as commits arrive

 # without a condition variable
 $git->log(@args, sub {
   # $commit isa Git::Wrapper::Log
   my $commit;
   ...
 }, sub {
   # called when complete
   ...
 });
 
 # with a condition variable



( run in 1.806 second using v1.01-cache-2.11-cpan-39bf76dae61 )