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 )