#!/usr/bin/perl # # Example implementation for the Git filter protocol version 2 # See Documentation/gitattributes.txt, section "Filter Protocol" # # Please note, this pass-thru filter is a minimal skeleton. No proper # error handling was implemented. # use strict; use warnings; my $MAX_PACKET_CONTENT_SIZE = 65516; sub packet_bin_read { my $buffer; my $bytes_read = read STDIN, $buffer, 4; if ( $bytes_read == 0 ) { # EOF - Git stopped talking to us! exit(); } elsif ( $bytes_read != 4 ) { die "invalid packet: '$buffer'"; } my $pkt_size = hex($buffer); if ( $pkt_size == 0 ) { return ( 1, "" ); } elsif ( $pkt_size > 4 ) { my $content_size = $pkt_size - 4; $bytes_read = read STDIN, $buffer, $content_size; if ( $bytes_read != $content_size ) { die "invalid packet ($content_size bytes expected; $bytes_read bytes read)"; } return ( 0, $buffer ); } else { die "invalid packet size: $pkt_size"; } } sub packet_txt_read { my ( $res, $buf ) = packet_bin_read(); unless ( $buf =~ s/\n$// ) { die "A non-binary line MUST be terminated by an LF."; } return ( $res, $buf ); } sub packet_bin_write { my $buf = shift; print STDOUT sprintf( "%04x", length($buf) + 4 ); print STDOUT $buf; STDOUT->flush(); } sub packet_txt_write { packet_bin_write( $_[0] . "\n" ); } sub packet_flush { print STDOUT sprintf( "%04x", 0 ); STDOUT->flush(); } ( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize"; ( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version"; ( packet_bin_read() eq ( 1, "" ) ) || die "bad version end"; packet_txt_write("git-filter-server"); packet_txt_write("version=2"); packet_flush(); ( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability"; ( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability"; ( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end"; packet_txt_write("capability=clean"); packet_txt_write("capability=smudge"); packet_flush(); while (1) { my ($command) = packet_txt_read() =~ /^command=(.+)$/; my ($pathname) = packet_txt_read() =~ /^pathname=(.+)$/; if ( $pathname eq "" ) { die "bad pathname '$pathname'"; } packet_bin_read(); my $input = ""; { binmode(STDIN); my $buffer; my $done = 0; while ( !$done ) { ( $done, $buffer ) = packet_bin_read(); $input .= $buffer; } } my $output; if ( $command eq "clean" ) { ### Perform clean here ### $output = $input; } elsif ( $command eq "smudge" ) { ### Perform smudge here ### $output = $input; } else { die "bad command '$command'"; } packet_txt_write("status=success"); packet_flush(); while ( length($output) > 0 ) { my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE ); packet_bin_write($packet); if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) { $output = substr( $output, $MAX_PACKET_CONTENT_SIZE ); } else { $output = ""; } } packet_flush(); # flush content! packet_flush(); # empty list, keep "status=success" unchanged! }