contrib/long-running-filter: add long running filter example
Add a simple pass-thru filter as example implementation for the Git filter protocol version 2. See Documentation/gitattributes.txt, section "Filter Protocol" for more info. Signed-off-by: Lars Schneider <larsxschneider@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Lars Schneider committed
Oct 16, 2016 at 16:20 UTC
0f71fa273fc6c352a266894f1f757c5950230d9b
2 files changed
+131
-1
Documentation/gitattributes.txt
+3
-1
@@ -516,7 +516,9 @@ the command pipe on exit. The filter is expected to detect EOF
516
and exit gracefully on its own. Git will wait until the filter
517
process has stopped.
518
519
-If you develop your own long running filter
519
+A long running filter demo implementation can be found in
520
+`contrib/long-running-filter/example.pl` located in the Git
521
+core repository. If you develop your own long running filter
522
process then the `GIT_TRACE_PACKET` environment variables can be
523
very helpful for debugging (see linkgit:git[1]).
524
contrib/long-running-filter/example.pl
new
+128
@@ -0,0 +1,128 @@
1
+#!/usr/bin/perl
2
+#
3
+# Example implementation for the Git filter protocol version 2
4
+# See Documentation/gitattributes.txt, section "Filter Protocol"
5
+#
6
+# Please note, this pass-thru filter is a minimal skeleton. No proper
7
+# error handling was implemented.
8
+#
9
+
10
+use strict;
11
+use warnings;
12
+
13
+my $MAX_PACKET_CONTENT_SIZE = 65516;
14
+
15
+sub packet_bin_read {
16
+ my $buffer;
17
+ my $bytes_read = read STDIN, $buffer, 4;
18
+ if ( $bytes_read == 0 ) {
19
+
20
+ # EOF - Git stopped talking to us!
21
+ exit();
22
+ }
23
+ elsif ( $bytes_read != 4 ) {
24
+ die "invalid packet: '$buffer'";
25
+ }
26
+ my $pkt_size = hex($buffer);
27
+ if ( $pkt_size == 0 ) {
28
+ return ( 1, "" );
29
+ }
30
+ elsif ( $pkt_size > 4 ) {
31
+ my $content_size = $pkt_size - 4;
32
+ $bytes_read = read STDIN, $buffer, $content_size;
33
+ if ( $bytes_read != $content_size ) {
34
+ die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
35
+ }
36
+ return ( 0, $buffer );
37
+ }
38
+ else {
39
+ die "invalid packet size: $pkt_size";
40
+ }
41
+}
42
+
43
+sub packet_txt_read {
44
+ my ( $res, $buf ) = packet_bin_read();
45
+ unless ( $buf =~ s/\n$// ) {
46
+ die "A non-binary line MUST be terminated by an LF.";
47
+ }
48
+ return ( $res, $buf );
49
+}
50
+
51
+sub packet_bin_write {
52
+ my $buf = shift;
53
+ print STDOUT sprintf( "%04x", length($buf) + 4 );
54
+ print STDOUT $buf;
55
+ STDOUT->flush();
56
+}
57
+
58
+sub packet_txt_write {
59
+ packet_bin_write( $_[0] . "\n" );
60
+}
61
+
62
+sub packet_flush {
63
+ print STDOUT sprintf( "%04x", 0 );
64
+ STDOUT->flush();
65
+}
66
+
67
+( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize";
68
+( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version";
69
+( packet_bin_read() eq ( 1, "" ) ) || die "bad version end";
70
+
71
+packet_txt_write("git-filter-server");
72
+packet_txt_write("version=2");
73
+packet_flush();
74
+
75
+( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability";
76
+( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
77
+( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end";
78
+
79
+packet_txt_write("capability=clean");
80
+packet_txt_write("capability=smudge");
81
+packet_flush();
82
+
83
+while (1) {
84
+ my ($command) = packet_txt_read() =~ /^command=([^=]+)$/;
85
+ my ($pathname) = packet_txt_read() =~ /^pathname=([^=]+)$/;
86
+
87
+ packet_bin_read();
88
+
89
+ my $input = "";
90
+ {
91
+ binmode(STDIN);
92
+ my $buffer;
93
+ my $done = 0;
94
+ while ( !$done ) {
95
+ ( $done, $buffer ) = packet_bin_read();
96
+ $input .= $buffer;
97
+ }
98
+ }
99
+
100
+ my $output;
101
+ if ( $command eq "clean" ) {
102
+ ### Perform clean here ###
103
+ $output = $input;
104
+ }
105
+ elsif ( $command eq "smudge" ) {
106
+ ### Perform smudge here ###
107
+ $output = $input;
108
+ }
109
+ else {
110
+ die "bad command '$command'";
111
+ }
112
+
113
+ packet_txt_write("status=success");
114
+ packet_flush();
115
+ while ( length($output) > 0 ) {
116
+ my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
117
+ packet_bin_write($packet);
118
+ if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
119
+ $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
120
+ }
121
+ else {
122
+ $output = "";
123
+ }
124
+ }
125
+ packet_flush(); # flush content!
126
+ packet_flush(); # empty list, keep "status=success" unchanged!
127
+
128
+}