The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
package Git::FastExport;
$Git::FastExport::VERSION = '0.105';
use strict;
use warnings;
use Carp;
use Scalar::Util qw( blessed );

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

sub new {
    my ( $class, $repo ) = @_;
    my $self = bless { source => '' }, $class;

    $self->{git} = blessed $repo && $repo->isa('Git::Repository')
        ? $repo    # below, use "$repo" for Path::Class paths
        : Git::Repository->new( defined $repo ? ( { cwd => "$repo" } ) : () );

    # git fast-export appeared in git 1.5.4
    if ( $self->{git}->version_lt('1.5.4') ) {
        my $opt = $self->{git}->options->{git};
        my ($git) = $opt
            ? ref $opt ? "@$opt" : $opt
            : $self->{git}->command("version")->cmdline;
        croak "Git version 1.5.4 required for git fast-export. '$git' is only version ${\$self->{git}->version}";
    }

    return $self;
}

sub fast_export {
    my ( $self, @args ) = @_;
    my $repo = $self->{git};
    $self->{source} = $repo->work_tree || $repo->git_dir;

    # call the fast-export command (no default arguments)
    $self->{command} = $repo->command( 'fast-export', @args );
    $self->{export_fh} = $self->{command}->stdout;
}

sub next_block {
    my ($self) = @_;

    my $fh = $self->{export_fh};
    die "fast_export() must be called before next_block()" if !$fh;

    # are we done?
    if ( eof $fh ) {
        if ( $self->{command} ) {
            $self->{command}->close;
            delete $self->{command};
        }
        delete $self->{export_fh};
        return;
    }

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

    # use the header from last time, or read it (first time)
    $block->{header} = $self->{header} ||= <$fh>;
    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)\b/) {
            s/^progress /progress [$self->{source}] /;
            $self->{header} = $_;
            last;
        }

        chomp;

        # special case of data block
        if (/^data (\d+)/) {
            my $bytes= 0 + $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';



=pod

=head1 NAME

Git::FastExport - A module to parse the output of git-fast-export

=head1 VERSION

version 0.105

=head1 SYNOPSIS

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

    # get the object from a Git::Repository
    my $repo = Git::Repository->new( work_tree => $path );
    my $export = Git::FastExport->new($repo);

    # or simply from a path specification
    my $export = Git::FastExport->new($path);

    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($repo);

The constructor takes an optional L<Git::Repository> object,
or a path (to a C<GIT_DIR> or C<GIT_WORK_TREE>), and returns a
Git::FastExport object attached to it.

=head2 fast_export

    # example @args: qw< --progress=1 --all --date-order >
    $export->fast_export(@args);

Initialize a B<git-fast-export> command on the repository, using the
arguments given in C<@args>.

=head2 next_block

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

Return 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 C<export_fh> filehandle of the Git::FastExport
object. It is normally setup via the C<fast_export()> method, but it is
possible to make it read directly from C<STDIN> (or another filehandle) by doing:

    $export->{export_fh} = \*STDIN;
    while ( my $block = $export->next_block() ) {
        ...
    }

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website
http://rt.cpan.org/NoAuth/Bugs.html?Dist=Git-FastExport or by email to
bug-git-fastexport@rt.cpan.org.

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

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

=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 COPYRIGHT

Copyright 2008-2014 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


__END__

# ABSTRACT: A module to parse the output of git-fast-export