The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Git::FastExport;
$Git::FastExport::VERSION = '0.107';
use strict;
use warnings;
use Carp;
use Scalar::Util qw( blessed );

use Git::Repository;
use Git::FastExport::Block;

my $LF = "\012";

sub new {
    my ( $class, $handle ) = @_;
    return bless { stream => $handle }, $class;
}

sub next_block {
    my ($self) = @_;
    my $fh = $self->{stream};
    return if !defined $fh;

    my $block = bless {}, 'Git::FastExport::Block';

    # pick up the header from the previous round, or read it (first time)
    $self->{header} ||= <$fh>;
    $block->{header} = delete $self->{header};

    # nothing left to process
    return if !defined $block->{header};

    chomp $block->{header};
    ( $block->{type} ) = $block->{header} =~ /^(\w+)/g;

    local $_;
    while (<$fh>) {

        # we've reached the beginning of the next block
        if (/^(commit|tag|reset|blob|checkpoint|progress|feature|option|done)\b/) {
            s/^progress /progress [$self->{source}] / if exists $self->{source};
            $self->{header} = $_;
            last;
        }

        chomp;

        # special case of data block
        if (/^data (.*)/) {
            if ( substr( $1, 0, 2 ) eq '<<' ) {    # Delimited format

                # 'data' SP '<<' <delim> LF
                # <raw> LF
                # <delim> LF
                # LF?
                my $delim = substr( $1, 2 );
                local $/ = "$LF$delim$LF";
                chomp( $block->{data} = <$fh> );
            }
            else {                                 # Exact byte count format

                # 'data' SP <count> LF
                # <raw> LF?
                my $bytes = $1;
                if ($bytes) {
                    local $/ = \$bytes;
                    $block->{data} = <$fh>;
                }
                else {
                    $block->{data} = "";
                }
            }
        }
        elsif (/^(?:[MDRC] |deleteall)/) {
            push @{ $block->{files} }, $_;
        }
        elsif (/^(\w+)/) {
            push @{ $block->{$1} }, $_;
        }
        else {

            # ignore empty lines, but choke on others
            die "Unexpected line:\n$_\n" if !/^$/;
            $block->{footer} .= "\012";
        }
    }

    # post-processing
    if ( $block->{type} eq 'commit' ) {
        ( $block->{committer_date} )
            = $block->{committer}[0] =~ /^committer [^>]*> (\d+) [-+]\d+$/g;
        ( $block->{author_date} )
            = $block->{author}[0] =~ /^author [^>]*> (\d+) [-+]\d+$/g;
    }

    return $block;
}

'progress 1 objects';

__END__

=head1 NAME

Git::FastExport - A parser for git fast-export streams

=head1 SYNOPSIS

    use Git::Repository;
    use Git::FastExport;

    # get the fast-export stream from a Git::Repository
    my $r  = Git::Repository->new();
    my $fh = $r->command(qw( fast-export --progress=1 --date-order --all ))->stdout;

    # create the parser object
    my $export = Git::FastExport->new($fh);

    # extract blocks from the fast-import stream
    while ( my $block = $export->next_block() ) {

        # do something with $block

    }

=head1 DESCRIPTION

Git::FastExport is a module that parses the output of
B<git-fast-export> and returns L<Git::FastExport::Block> objects that
can be inspected or modified before being eventually passed on as the
input to B<git-fast-import>.

=head1 METHODS

This class provides the following methods:

=head2 new

    my $export = Git::FastExport->new($fh);

The constructor takes an open filehandle and returns a
C<Git::FastExport> object attached to it.

=head2 next_block

    my $block = $export->next_block();

Returns the next block in the B<git-fast-export> stream as a
L<Git::FastExport::Block> object.

Return nothing at the end of stream.

This methods reads from the filehandle of the Git::FastExport object.

=head1 ACKNOWLEDGEMENTS

The original version of this module was created as part of my work
for BOOKING.COM, which authorized its publication/distribution
under the same terms as Perl itself.

=head1 AUTHOR

Philippe Bruhat (BooK) <book@cpan.org>

=head1 COPYRIGHT

Copyright 2008-2016 Philippe Bruhat (BooK), All Rights Reserved.

=head1 LICENSE

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

=cut