package Statocles::Deploy::Git;
# ABSTRACT: Deploy a site to a Git repository
$Statocles::Deploy::Git::VERSION = '0.040';
use Statocles::Base 'Class';
extends 'Statocles::Deploy::File';

use Git::Repository;


has branch => (
    is => 'ro',
    isa => Str,
    default => sub { 'master' },
);


has remote => (
    is => 'ro',
    isa => Str,
    default => sub { 'origin' },
);


around 'deploy' => sub {
    my ( $orig, $self, $from_store, $message ) = @_;

    my $deploy_dir = $self->path;
    my $git = Git::Repository->new( work_tree => "$deploy_dir" );

    # Switch to the right branch
    my $current_branch = _current_branch( $git );
    if ( !_has_branch( $git, $self->branch ) ) {
        # Create a new, orphan branch
        # Orphan branches were introduced in git 1.7.2
        _git_run( $git, checkout => '--orphan', $self->branch );
        _git_run( $git, 'rm', '-r', '-f', '.' );
    }
    else {
        _git_run( $git, checkout => $self->branch );
    }

    # Copy the files
    my @files = $self->$orig( $from_store, $message );

    # Check to see which files were changed
    # --porcelain was added in 1.7.0
    my @status_lines = $git->run(
        status => '--porcelain', '--ignore-submodules', '--untracked-files',
    );

    my %in_status;
    for my $line ( @status_lines ) {
        my ( $status, $path ) = $line =~ /^\s*(\S+)\s+(.+)$/;
        $in_status{ $path } = $status;
    }

    # Commit the files
    _git_run( $git, add => grep { $in_status{ $_ } } @files );
    _git_run( $git, commit => -m => $message || "Site update" );
    if ( _has_remote( $git, $self->remote ) ) {
        _git_run( $git, push => $self->remote => $self->branch );
    }

    # Tidy up
    _git_run( $git, checkout => $current_branch );

    return @files;
};

sub _git_run {
    my ( $git, @args ) = @_;
    my $cmdline = join " ", 'git', @args;
    my $cmd = $git->command( @args );
    my $stdout = join( "\n", readline( $cmd->stdout ) ) // '';
    my $stderr = join( "\n", readline( $cmd->stderr ) ) // '';
    $cmd->close;
    my $exit = $cmd->exit;

    if ( $exit ) {
        die "git $args[0] exited with $exit\n\n-- CMD --\n$cmdline\n\n-- STDOUT --\n$stdout\n\n-- STDERR --\n$stderr\n";
    }

    return $cmd->exit;
}

sub _current_branch {
    my ( $git ) = @_;
    my @branches = map { s/^\*\s+//; $_ } grep { /^\*/ } $git->run( 'branch' );
    return $branches[0];
}

sub _has_branch {
    my ( $git, $branch ) = @_;
    return !!grep { $_ eq $branch } map { s/^[\*\s]\s+//; $_ } $git->run( 'branch' );
}

sub _has_remote {
    my ( $git, $remote ) = @_;
    return !!grep { $_ eq $remote } map { s/^[\*\s]\s+//; $_ } $git->run( 'remote' );
}

sub _git_version {
    my $git_version = ( split ' ', `git --version` )[-1];
    return unless $git_version;
    my $v = sprintf '%i.%03i%03i', split /[.]/, $git_version;
    return $v;
}

1;

__END__

=pod

=head1 NAME

Statocles::Deploy::Git - Deploy a site to a Git repository

=head1 VERSION

version 0.040

=head1 DESCRIPTION

This class allows a site to be deployed to a Git repository.

This class extends L<Statocles::Store::File|Statocles::Store::File>.

=head1 ATTRIBUTES

=head2 path

The path to the Git work tree, the root of the repository.

=head2 branch

The Git branch to deploy to. Defaults to "master". If you're building a Github Pages
site for a project, you probably want to use the "gh-pages" branch.

=head2 remote

The name of the remote to deploy to. Defaults to 'origin'.

=head1 METHODS

=head2 deploy( FROM_STORE, MESSAGE )

Deploy the site, copying from the given store. Returns the files deployed.

=head1 SEE ALSO

=over 4

=item L<Statocles::Store::File>

=item L<Statocles::Deploy>

=back

=head1 AUTHOR

Doug Bell <preaction@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Doug Bell.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
