{"thread":{"id":"47154","subject":"[PATCH v3 0/8] Create Git/Packet.pm","startedAt":"2017-11-10T13:22:26Z","lastAt":"2017-11-10T19:03:36Z","messageCount":10,"participants":["Christian Couder","Junio C Hamano"],"isPatch":true,"patchVersion":3,"patchTotal":8},"messages":[{"id":"332172","messageId":"20171110132200.7871-1-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":null,"subject":"[PATCH v3 0/8] Create Git/Packet.pm","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:52Z","receivedAt":"2017-11-10T13:22:26Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Goal\n~~~~\n\nPacket related functions in Perl can be useful to write new filters or\nto debug or test existing filters. They might also in the future be\nused by other software using the same packet line protocol. So instead\nof having them in t0021/rot13-filter.pl, let's extract them into a new\nGit/Packet.pm module.\n\nChanges since the previous version\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are only a few small changes compared to v2:\n\nPatch 2/8 has the following change:\n\n  - packet_required_key_val_read() is renamed packet_key_val_read()\n    and a comment is added before the function,\n\nPatch 2/8 and 6/8 have the following change:\n\n  - \"if\" is used instead of \"unless\" to make the logic easier to\n    understand\n\nThe diff with the previous version is:\n\ndiff --git a/perl/Git/Packet.pm b/perl/Git/Packet.pm\nindex 255b28c098..1682403ffc 100644\n--- a/perl/Git/Packet.pm\n+++ b/perl/Git/Packet.pm\n@@ -17,7 +17,7 @@ our @EXPORT = qw(\n                        packet_compare_lists\n                        packet_bin_read\n                        packet_txt_read\n-                       packet_required_key_val_read\n+                       packet_key_val_read\n                        packet_bin_write\n                        packet_txt_write\n                        packet_flush\n@@ -68,28 +68,29 @@ sub packet_bin_read {\n \n sub remove_final_lf_or_die {\n        my $buf = shift;\n-       unless ( $buf =~ s/\\n$// ) {\n-               die \"A non-binary line MUST be terminated by an LF.\\n\"\n-                   . \"Received: '$buf'\";\n+       if ( $buf =~ s/\\n$// ) {\n+               return $buf;\n        }\n-       return $buf;\n+       die \"A non-binary line MUST be terminated by an LF.\\n\"\n+           . \"Received: '$buf'\";\n }\n\n sub packet_txt_read {\n        my ( $res, $buf ) = packet_bin_read();\n-       unless ( $res == -1 or $buf eq '' ) {\n+       if ( $res != -1 and $buf ne '' ) {\n                $buf = remove_final_lf_or_die($buf);\n        }\n        return ( $res, $buf );\n }\n \n-sub packet_required_key_val_read {\n+# Read a text line and check that it is in the form \"key=value\"\n+sub packet_key_val_read {\n        my ( $key ) = @_;\n        my ( $res, $buf ) = packet_txt_read();\n-       unless ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {\n-               die \"bad $key: '$buf'\";\n+       if ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {\n+               return ( $res, $buf );\n        }\n-       return ( $res, $buf );\n+       die \"bad $key: '$buf'\";\n }\n \n sub packet_bin_write {\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex 6fd7fa476b..f1678851de 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -70,7 +70,7 @@ print $debug \"init handshake complete\\n\";\n $debug->flush();\n \n while (1) {\n-       my ( $res, $command ) = packet_required_key_val_read(\"command\");\n+       my ( $res, $command ) = packet_key_val_read(\"command\");\n        if ( $res == -1 ) {\n                print $debug \"STOP\\n\";\n                exit();\n@@ -106,7 +106,7 @@ while (1) {\n                packet_txt_write(\"status=success\");\n                packet_flush();\n        } else {\n-               my ( $res, $pathname ) = packet_required_key_val_read(\"pathname\");\n+               my ( $res, $pathname ) = packet_key_val_read(\"pathname\");\n                if ( $res == -1 ) {\n                        die \"unexpected EOF while expecting pathname\";\n                }\n\nLinks\n~~~~~\n\nThis patch series is on the following branch:\n\nhttps://github.com/chriscool/git/commits/gl-prep-external-odb\n\nVersion 1 and 2 of this patch series are on the mailing list here:\n\nhttps://public-inbox.org/git/20171019123030.17338-1-chriscool@tuxfamily.org/\nhttps://public-inbox.org/git/20171105213836.11717-1-chriscool@tuxfamily.org/\n\nThey are also available in the following branch:\n\nhttps://github.com/chriscool/git/commits/gl-prep-external-odb1\nhttps://github.com/chriscool/git/commits/gl-prep-external-odb14\n\nThis patch series was extracted from previous \"Add initial\nexperimental external ODB support\" patch series.\n\nVersion 1, 2, 3, 4, 5 and 6 of this previous series are on the mailing\nlist here:\n\nhttps://public-inbox.org/git/20160613085546.11784-1-chriscool@tuxfamily.org/\nhttps://public-inbox.org/git/20160628181933.24620-1-chriscool@tuxfamily.org/\nhttps://public-inbox.org/git/20161130210420.15982-1-chriscool@tuxfamily.org/\nhttps://public-inbox.org/git/20170620075523.26961-1-chriscool@tuxfamily.org/\nhttps://public-inbox.org/git/20170803091926.1755-1-chriscool@tuxfamily.org/\nhttps://public-inbox.org/git/20170916080731.13925-1-chriscool@tuxfamily.org/\n\nThey are also available in the following branches:\n\nhttps://github.com/chriscool/git/commits/gl-external-odb12\nhttps://github.com/chriscool/git/commits/gl-external-odb22\nhttps://github.com/chriscool/git/commits/gl-external-odb61\nhttps://github.com/chriscool/git/commits/gl-external-odb239\nhttps://github.com/chriscool/git/commits/gl-external-odb373\nhttps://github.com/chriscool/git/commits/gl-external-odb411\n\n\nChristian Couder (8):\n  t0021/rot13-filter: fix list comparison\n  t0021/rot13-filter: refactor packet reading functions\n  t0021/rot13-filter: improve 'if .. elsif .. else' style\n  t0021/rot13-filter: improve error message\n  t0021/rot13-filter: add packet_initialize()\n  t0021/rot13-filter: refactor checking final lf\n  t0021/rot13-filter: add capability functions\n  Add Git/Packet.pm from parts of t0021/rot13-filter.pl\n\n perl/Git/Packet.pm      | 169 ++++++++++++++++++++++++++++++++++++++++++++++++\n perl/Makefile           |   1 +\n t/t0021/rot13-filter.pl | 127 ++++++++++--------------------------\n 3 files changed, 203 insertions(+), 94 deletions(-)\n create mode 100644 perl/Git/Packet.pm\n\n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332173","messageId":"20171110132200.7871-2-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 1/8] t0021/rot13-filter: fix list comparison","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:53Z","receivedAt":"2017-11-10T13:22:31Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Since edcc8581 (\"convert: add filter.<driver>.process\noption\", 2016-10-16) when t0021/rot13-filter.pl was created, list\ncomparison in this perl script have been quite broken.\n\npacket_txt_read() returns a 2-element list, and the right hand\nside of \"eq\" also has a list with (two, elements), but \"eq\" takes\nthe last element of the list on each side, and compares them. The\nfirst elements (0 or 1) on the right hand side lists do not matter,\nwhich means we do not require to see a flush at the end of the\nversion -- a simple empty string or an EOF would do, which is\ndefinitely not what we want.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t0021/rot13-filter.pl | 35 ++++++++++++++++++++++++++++-------\n 1 file changed, 28 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex ad685d92f8..4b2d9087d4 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -55,6 +55,20 @@ sub rot13 {\n \treturn $str;\n }\n \n+sub packet_compare_lists {\n+\tmy ($expect, @result) = @_;\n+\tmy $ix;\n+\tif (scalar @$expect != scalar @result) {\n+\t\treturn undef;\n+\t}\n+\tfor ($ix = 0; $ix < $#result; $ix++) {\n+\t\tif ($expect->[$ix] ne $result[$ix]) {\n+\t\t\treturn undef;\n+\t\t}\n+\t}\n+\treturn 1;\n+}\n+\n sub packet_bin_read {\n \tmy $buffer;\n \tmy $bytes_read = read STDIN, $buffer, 4;\n@@ -110,18 +124,25 @@ sub packet_flush {\n print $debug \"START\\n\";\n $debug->flush();\n \n-( packet_txt_read() eq ( 0, \"git-filter-client\" ) ) || die \"bad initialize\";\n-( packet_txt_read() eq ( 0, \"version=2\" ) )         || die \"bad version\";\n-( packet_bin_read() eq ( 1, \"\" ) )                  || die \"bad version end\";\n+packet_compare_lists([0, \"git-filter-client\"], packet_txt_read()) ||\n+\tdie \"bad initialize\";\n+packet_compare_lists([0, \"version=2\"], packet_txt_read()) ||\n+\tdie \"bad version\";\n+packet_compare_lists([1, \"\"], packet_bin_read()) ||\n+\tdie \"bad version end\";\n \n packet_txt_write(\"git-filter-server\");\n packet_txt_write(\"version=2\");\n packet_flush();\n \n-( packet_txt_read() eq ( 0, \"capability=clean\" ) )  || die \"bad capability\";\n-( packet_txt_read() eq ( 0, \"capability=smudge\" ) ) || die \"bad capability\";\n-( packet_txt_read() eq ( 0, \"capability=delay\" ) )  || die \"bad capability\";\n-( packet_bin_read() eq ( 1, \"\" ) )                  || die \"bad capability end\";\n+packet_compare_lists([0, \"capability=clean\"], packet_txt_read()) ||\n+\tdie \"bad capability\";\n+packet_compare_lists([0, \"capability=smudge\"], packet_txt_read()) ||\n+\tdie \"bad capability\";\n+packet_compare_lists([0, \"capability=delay\"], packet_txt_read()) ||\n+\tdie \"bad capability\";\n+packet_compare_lists([1, \"\"], packet_bin_read()) ||\n+\tdie \"bad capability end\";\n \n foreach (@capabilities) {\n \tpacket_txt_write( \"capability=\" . $_ );\n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332174","messageId":"20171110132200.7871-3-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 2/8] t0021/rot13-filter: refactor packet reading functions","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:54Z","receivedAt":"2017-11-10T13:22:37Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"To make it possible in a following commit to move packet\nreading and writing functions into a Packet.pm module,\nlet's refactor these functions, so they don't handle\nprinting debug output and exiting.\n\nWhile at it let's create packet_key_val_read()\nto still handle erroring out in a common case.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t0021/rot13-filter.pl | 43 +++++++++++++++++++++++++++++++------------\n 1 file changed, 31 insertions(+), 12 deletions(-)\n\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex 4b2d9087d4..b1909699f4 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -74,8 +74,7 @@ sub packet_bin_read {\n \tmy $bytes_read = read STDIN, $buffer, 4;\n \tif ( $bytes_read == 0 ) {\n \t\t# EOF - Git stopped talking to us!\n-\t\tprint $debug \"STOP\\n\";\n-\t\texit();\n+\t\treturn ( -1, \"\" );\n \t}\n \telsif ( $bytes_read != 4 ) {\n \t\tdie \"invalid packet: '$buffer'\";\n@@ -99,10 +98,20 @@ sub packet_bin_read {\n \n sub packet_txt_read {\n \tmy ( $res, $buf ) = packet_bin_read();\n-\tunless ( $buf eq '' or $buf =~ s/\\n$// ) {\n-\t\tdie \"A non-binary line MUST be terminated by an LF.\";\n+\tif ( $res == -1 or $buf eq '' or $buf =~ s/\\n$// ) {\n+\t\treturn ( $res, $buf );\n+\t}\n+\tdie \"A non-binary line MUST be terminated by an LF.\";\n+}\n+\n+# Read a text line and check that it is in the form \"key=value\"\n+sub packet_key_val_read {\n+\tmy ( $key ) = @_;\n+\tmy ( $res, $buf ) = packet_txt_read();\n+\tif ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {\n+\t\treturn ( $res, $buf );\n \t}\n-\treturn ( $res, $buf );\n+\tdie \"bad $key: '$buf'\";\n }\n \n sub packet_bin_write {\n@@ -152,13 +161,18 @@ print $debug \"init handshake complete\\n\";\n $debug->flush();\n \n while (1) {\n-\tmy ( $command ) = packet_txt_read() =~ /^command=(.+)$/;\n+\tmy ( $res, $command ) = packet_key_val_read(\"command\");\n+\tif ( $res == -1 ) {\n+\t\tprint $debug \"STOP\\n\";\n+\t\texit();\n+\t}\n \tprint $debug \"IN: $command\";\n \t$debug->flush();\n \n \tif ( $command eq \"list_available_blobs\" ) {\n \t\t# Flush\n-\t\tpacket_bin_read();\n+\t\tpacket_compare_lists([1, \"\"], packet_bin_read()) ||\n+\t\t\tdie \"bad list_available_blobs end\";\n \n \t\tforeach my $pathname ( sort keys %DELAY ) {\n \t\t\tif ( $DELAY{$pathname}{\"requested\"} >= 1 ) {\n@@ -184,14 +198,13 @@ while (1) {\n \t\tpacket_flush();\n \t}\n \telse {\n-\t\tmy ( $pathname ) = packet_txt_read() =~ /^pathname=(.+)$/;\n+\t\tmy ( $res, $pathname ) = packet_key_val_read(\"pathname\");\n+\t\tif ( $res == -1 ) {\n+\t\t\tdie \"unexpected EOF while expecting pathname\";\n+\t\t}\n \t\tprint $debug \" $pathname\";\n \t\t$debug->flush();\n \n-\t\tif ( $pathname eq \"\" ) {\n-\t\t\tdie \"bad pathname '$pathname'\";\n-\t\t}\n-\n \t\t# Read until flush\n \t\tmy ( $done, $buffer ) = packet_txt_read();\n \t\twhile ( $buffer ne '' ) {\n@@ -205,6 +218,9 @@ while (1) {\n \n \t\t\t( $done, $buffer ) = packet_txt_read();\n \t\t}\n+\t\tif ( $done == -1 ) {\n+\t\t\tdie \"unexpected EOF after pathname '$pathname'\";\n+\t\t}\n \n \t\tmy $input = \"\";\n \t\t{\n@@ -215,6 +231,9 @@ while (1) {\n \t\t\t\t( $done, $buffer ) = packet_bin_read();\n \t\t\t\t$input .= $buffer;\n \t\t\t}\n+\t\t\tif ( $done == -1 ) {\n+\t\t\t\tdie \"unexpected EOF while reading input for '$pathname'\";\n+\t\t\t}\t\t\t\n \t\t\tprint $debug \" \" . length($input) . \" [OK] -- \";\n \t\t\t$debug->flush();\n \t\t}\n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332175","messageId":"20171110132200.7871-4-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 3/8] t0021/rot13-filter: improve 'if .. elsif .. else' style","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:55Z","receivedAt":"2017-11-10T13:22:44Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Before further refactoring the \"t0021/rot13-filter.pl\" script,\nlet's modernize the style of its 'if .. elsif .. else' clauses\nto improve its readability by making it more similar to our\nother perl scripts.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t0021/rot13-filter.pl | 39 +++++++++++++--------------------------\n 1 file changed, 13 insertions(+), 26 deletions(-)\n\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex b1909699f4..8bba97af1a 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -75,23 +75,20 @@ sub packet_bin_read {\n \tif ( $bytes_read == 0 ) {\n \t\t# EOF - Git stopped talking to us!\n \t\treturn ( -1, \"\" );\n-\t}\n-\telsif ( $bytes_read != 4 ) {\n+\t} elsif ( $bytes_read != 4 ) {\n \t\tdie \"invalid packet: '$buffer'\";\n \t}\n \tmy $pkt_size = hex($buffer);\n \tif ( $pkt_size == 0 ) {\n \t\treturn ( 1, \"\" );\n-\t}\n-\telsif ( $pkt_size > 4 ) {\n+\t} elsif ( $pkt_size > 4 ) {\n \t\tmy $content_size = $pkt_size - 4;\n \t\t$bytes_read = read STDIN, $buffer, $content_size;\n \t\tif ( $bytes_read != $content_size ) {\n \t\t\tdie \"invalid packet ($content_size bytes expected; $bytes_read bytes read)\";\n \t\t}\n \t\treturn ( 0, $buffer );\n-\t}\n-\telse {\n+\t} else {\n \t\tdie \"invalid packet size: $pkt_size\";\n \t}\n }\n@@ -196,8 +193,7 @@ while (1) {\n \t\t$debug->flush();\n \t\tpacket_txt_write(\"status=success\");\n \t\tpacket_flush();\n-\t}\n-\telse {\n+\t} else {\n \t\tmy ( $res, $pathname ) = packet_key_val_read(\"pathname\");\n \t\tif ( $res == -1 ) {\n \t\t\tdie \"unexpected EOF while expecting pathname\";\n@@ -241,17 +237,13 @@ while (1) {\n \t\tmy $output;\n \t\tif ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{\"output\"} ) {\n \t\t\t$output = $DELAY{$pathname}{\"output\"}\n-\t\t}\n-\t\telsif ( $pathname eq \"error.r\" or $pathname eq \"abort.r\" ) {\n+\t\t} elsif ( $pathname eq \"error.r\" or $pathname eq \"abort.r\" ) {\n \t\t\t$output = \"\";\n-\t\t}\n-\t\telsif ( $command eq \"clean\" and grep( /^clean$/, @capabilities ) ) {\n+\t\t} elsif ( $command eq \"clean\" and grep( /^clean$/, @capabilities ) ) {\n \t\t\t$output = rot13($input);\n-\t\t}\n-\t\telsif ( $command eq \"smudge\" and grep( /^smudge$/, @capabilities ) ) {\n+\t\t} elsif ( $command eq \"smudge\" and grep( /^smudge$/, @capabilities ) ) {\n \t\t\t$output = rot13($input);\n-\t\t}\n-\t\telse {\n+\t\t} else {\n \t\t\tdie \"bad command '$command'\";\n \t\t}\n \n@@ -260,25 +252,21 @@ while (1) {\n \t\t\t$debug->flush();\n \t\t\tpacket_txt_write(\"status=error\");\n \t\t\tpacket_flush();\n-\t\t}\n-\t\telsif ( $pathname eq \"abort.r\" ) {\n+\t\t} elsif ( $pathname eq \"abort.r\" ) {\n \t\t\tprint $debug \"[ABORT]\\n\";\n \t\t\t$debug->flush();\n \t\t\tpacket_txt_write(\"status=abort\");\n \t\t\tpacket_flush();\n-\t\t}\n-\t\telsif ( $command eq \"smudge\" and\n+\t\t} elsif ( $command eq \"smudge\" and\n \t\t\texists $DELAY{$pathname} and\n-\t\t\t$DELAY{$pathname}{\"requested\"} == 1\n-\t\t) {\n+\t\t\t$DELAY{$pathname}{\"requested\"} == 1 ) {\n \t\t\tprint $debug \"[DELAYED]\\n\";\n \t\t\t$debug->flush();\n \t\t\tpacket_txt_write(\"status=delayed\");\n \t\t\tpacket_flush();\n \t\t\t$DELAY{$pathname}{\"requested\"} = 2;\n \t\t\t$DELAY{$pathname}{\"output\"} = $output;\n-\t\t}\n-\t\telse {\n+\t\t} else {\n \t\t\tpacket_txt_write(\"status=success\");\n \t\t\tpacket_flush();\n \n@@ -298,8 +286,7 @@ while (1) {\n \t\t\t\tprint $debug \".\";\n \t\t\t\tif ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {\n \t\t\t\t\t$output = substr( $output, $MAX_PACKET_CONTENT_SIZE );\n-\t\t\t\t}\n-\t\t\t\telse {\n+\t\t\t\t} else {\n \t\t\t\t\t$output = \"\";\n \t\t\t\t}\n \t\t\t}\n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332176","messageId":"20171110132200.7871-5-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 4/8] t0021/rot13-filter: improve error message","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:56Z","receivedAt":"2017-11-10T13:22:53Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"If there is no new line at the end of something it receives,\nthe packet_txt_read() function die()s, but it's difficult to\ndebug without much context.\n\nLet's give a bit more information when that happens.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t0021/rot13-filter.pl | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex 8bba97af1a..55b6e17034 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -98,7 +98,8 @@ sub packet_txt_read {\n \tif ( $res == -1 or $buf eq '' or $buf =~ s/\\n$// ) {\n \t\treturn ( $res, $buf );\n \t}\n-\tdie \"A non-binary line MUST be terminated by an LF.\";\n+\tdie \"A non-binary line MUST be terminated by an LF.\\n\"\n+\t    . \"Received: '$buf'\";\n }\n \n # Read a text line and check that it is in the form \"key=value\"\n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332177","messageId":"20171110132200.7871-6-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 5/8] t0021/rot13-filter: add packet_initialize()","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:57Z","receivedAt":"2017-11-10T13:23:03Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Let's refactor the code to initialize communication into its own\npacket_initialize() function, so that we can reuse this\nfunctionality in following patches.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t0021/rot13-filter.pl | 26 ++++++++++++++++----------\n 1 file changed, 16 insertions(+), 10 deletions(-)\n\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex 55b6e17034..9e18be66b6 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -128,19 +128,25 @@ sub packet_flush {\n \tSTDOUT->flush();\n }\n \n+sub packet_initialize {\n+\tmy ($name, $version) = @_;\n+\n+\tpacket_compare_lists([0, $name . \"-client\"], packet_txt_read()) ||\n+\t\tdie \"bad initialize\";\n+\tpacket_compare_lists([0, \"version=\" . $version], packet_txt_read()) ||\n+\t\tdie \"bad version\";\n+\tpacket_compare_lists([1, \"\"], packet_bin_read()) ||\n+\t\tdie \"bad version end\";\n+\n+\tpacket_txt_write( $name . \"-server\" );\n+\tpacket_txt_write( \"version=\" . $version );\n+\tpacket_flush();\n+}\n+\n print $debug \"START\\n\";\n $debug->flush();\n \n-packet_compare_lists([0, \"git-filter-client\"], packet_txt_read()) ||\n-\tdie \"bad initialize\";\n-packet_compare_lists([0, \"version=2\"], packet_txt_read()) ||\n-\tdie \"bad version\";\n-packet_compare_lists([1, \"\"], packet_bin_read()) ||\n-\tdie \"bad version end\";\n-\n-packet_txt_write(\"git-filter-server\");\n-packet_txt_write(\"version=2\");\n-packet_flush();\n+packet_initialize(\"git-filter\", 2);\n \n packet_compare_lists([0, \"capability=clean\"], packet_txt_read()) ||\n \tdie \"bad capability\";\n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332178","messageId":"20171110132200.7871-7-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 6/8] t0021/rot13-filter: refactor checking final lf","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:58Z","receivedAt":"2017-11-10T13:23:22Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"As checking for a lf character at the end of a buffer\nwill be useful in another function, let's refactor this\nfunctionality into a small remove_final_lf_or_die()\nhelper function.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t0021/rot13-filter.pl | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex 9e18be66b6..8f255b6131 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -93,15 +93,23 @@ sub packet_bin_read {\n \t}\n }\n \n-sub packet_txt_read {\n-\tmy ( $res, $buf ) = packet_bin_read();\n-\tif ( $res == -1 or $buf eq '' or $buf =~ s/\\n$// ) {\n-\t\treturn ( $res, $buf );\n+sub remove_final_lf_or_die {\n+\tmy $buf = shift;\n+\tif ( $buf =~ s/\\n$// ) {\n+\t\treturn $buf;\n \t}\n \tdie \"A non-binary line MUST be terminated by an LF.\\n\"\n \t    . \"Received: '$buf'\";\n }\n \n+sub packet_txt_read {\n+\tmy ( $res, $buf ) = packet_bin_read();\n+\tif ( $res != -1 and $buf ne '' ) {\n+\t\t$buf = remove_final_lf_or_die($buf);\n+\t}\n+\treturn ( $res, $buf );\n+}\n+\n # Read a text line and check that it is in the form \"key=value\"\n sub packet_key_val_read {\n \tmy ( $key ) = @_;\n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332179","messageId":"20171110132200.7871-8-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 7/8] t0021/rot13-filter: add capability functions","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:21:59Z","receivedAt":"2017-11-10T13:23:30Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"These function help read and write capabilities.\n\nTo make them more generic and make it easy to reuse them,\nthe following changes are made:\n\n- we don't require capabilities to come in a fixed order,\n- we allow duplicates,\n- we check that the remote supports the capabilities we\n  advertise,\n- we don't check if the remote declares any capability we\n  don't know about.\n\nThe reason behind the last change is that the protocol\nshould work using only the capabilities that both ends\nsupport, and it should not stop working if one end starts\nto advertise a new capability.\n\nDespite those changes, we can still require a set of\ncapabilities, and die if one of them is not supported.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t0021/rot13-filter.pl | 58 ++++++++++++++++++++++++++++++++++++++-----------\n 1 file changed, 45 insertions(+), 13 deletions(-)\n\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex 8f255b6131..6cd9ecffee 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -151,24 +151,56 @@ sub packet_initialize {\n \tpacket_flush();\n }\n \n+sub packet_read_capabilities {\n+\tmy @cap;\n+\twhile (1) {\n+\t\tmy ( $res, $buf ) = packet_bin_read();\n+\t\tif ( $res == -1 ) {\n+\t\t\tdie \"unexpected EOF when reading capabilities\";\n+\t\t}\n+\t\treturn ( $res, @cap ) if ( $res != 0 );\n+\t\t$buf = remove_final_lf_or_die($buf);\n+\t\tunless ( $buf =~ s/capability=// ) {\n+\t\t\tdie \"bad capability buf: '$buf'\";\n+\t\t}\n+\t\tpush @cap, $buf;\n+\t}\n+}\n+\n+# Read remote capabilities and check them against capabilities we require\n+sub packet_read_and_check_capabilities {\n+\tmy @required_caps = @_;\n+\tmy ($res, @remote_caps) = packet_read_capabilities();\n+\tmy %remote_caps = map { $_ => 1 } @remote_caps;\n+\tforeach (@required_caps) {\n+\t\tunless (exists($remote_caps{$_})) {\n+\t\t\tdie \"required '$_' capability not available from remote\" ;\n+\t\t}\n+\t}\n+\treturn %remote_caps;\n+}\n+\n+# Check our capabilities we want to advertise against the remote ones\n+# and then advertise our capabilities\n+sub packet_check_and_write_capabilities {\n+\tmy ($remote_caps, @our_caps) = @_;\n+\tforeach (@our_caps) {\n+\t\tunless (exists($remote_caps->{$_})) {\n+\t\t\tdie \"our capability '$_' is not available from remote\"\n+\t\t}\n+\t\tpacket_txt_write( \"capability=\" . $_ );\n+\t}\n+\tpacket_flush();\n+}\n+\n print $debug \"START\\n\";\n $debug->flush();\n \n packet_initialize(\"git-filter\", 2);\n \n-packet_compare_lists([0, \"capability=clean\"], packet_txt_read()) ||\n-\tdie \"bad capability\";\n-packet_compare_lists([0, \"capability=smudge\"], packet_txt_read()) ||\n-\tdie \"bad capability\";\n-packet_compare_lists([0, \"capability=delay\"], packet_txt_read()) ||\n-\tdie \"bad capability\";\n-packet_compare_lists([1, \"\"], packet_bin_read()) ||\n-\tdie \"bad capability end\";\n-\n-foreach (@capabilities) {\n-\tpacket_txt_write( \"capability=\" . $_ );\n-}\n-packet_flush();\n+my %remote_caps = packet_read_and_check_capabilities(\"clean\", \"smudge\", \"delay\");\n+packet_check_and_write_capabilities(\\%remote_caps, @capabilities);\n+\n print $debug \"init handshake complete\\n\";\n $debug->flush();\n \n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332180","messageId":"20171110132200.7871-9-chriscool@tuxfamily.org","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"[PATCH v3 8/8] Add Git/Packet.pm from parts of t0021/rot13-filter.pl","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2017-11-10T13:22:00Z","receivedAt":"2017-11-10T13:23:41Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"And while at it let's simplify t0021/rot13-filter.pl by\nusing Git/Packet.pm.\n\nThis will make it possible to reuse packet related\nfunctions in other test scripts.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n perl/Git/Packet.pm      | 169 ++++++++++++++++++++++++++++++++++++++++++++++++\n perl/Makefile           |   1 +\n t/t0021/rot13-filter.pl | 141 +---------------------------------------\n 3 files changed, 173 insertions(+), 138 deletions(-)\n create mode 100644 perl/Git/Packet.pm\n\ndiff --git a/perl/Git/Packet.pm b/perl/Git/Packet.pm\nnew file mode 100644\nindex 0000000000..1682403ffc\n--- /dev/null\n+++ b/perl/Git/Packet.pm\n@@ -0,0 +1,169 @@\n+package Git::Packet;\n+use 5.008;\n+use strict;\n+use warnings;\n+BEGIN {\n+\trequire Exporter;\n+\tif ($] < 5.008003) {\n+\t\t*import = \\&Exporter::import;\n+\t} else {\n+\t\t# Exporter 5.57 which supports this invocation was\n+\t\t# released with perl 5.8.3\n+\t\tExporter->import('import');\n+\t}\n+}\n+\n+our @EXPORT = qw(\n+\t\t\tpacket_compare_lists\n+\t\t\tpacket_bin_read\n+\t\t\tpacket_txt_read\n+\t\t\tpacket_key_val_read\n+\t\t\tpacket_bin_write\n+\t\t\tpacket_txt_write\n+\t\t\tpacket_flush\n+\t\t\tpacket_initialize\n+\t\t\tpacket_read_capabilities\n+\t\t\tpacket_read_and_check_capabilities\n+\t\t\tpacket_check_and_write_capabilities\n+\t\t);\n+our @EXPORT_OK = @EXPORT;\n+\n+sub packet_compare_lists {\n+\tmy ($expect, @result) = @_;\n+\tmy $ix;\n+\tif (scalar @$expect != scalar @result) {\n+\t\treturn undef;\n+\t}\n+\tfor ($ix = 0; $ix < $#result; $ix++) {\n+\t\tif ($expect->[$ix] ne $result[$ix]) {\n+\t\t\treturn undef;\n+\t\t}\n+\t}\n+\treturn 1;\n+}\n+\n+sub packet_bin_read {\n+\tmy $buffer;\n+\tmy $bytes_read = read STDIN, $buffer, 4;\n+\tif ( $bytes_read == 0 ) {\n+\t\t# EOF - Git stopped talking to us!\n+\t\treturn ( -1, \"\" );\n+\t} elsif ( $bytes_read != 4 ) {\n+\t\tdie \"invalid packet: '$buffer'\";\n+\t}\n+\tmy $pkt_size = hex($buffer);\n+\tif ( $pkt_size == 0 ) {\n+\t\treturn ( 1, \"\" );\n+\t} elsif ( $pkt_size > 4 ) {\n+\t\tmy $content_size = $pkt_size - 4;\n+\t\t$bytes_read = read STDIN, $buffer, $content_size;\n+\t\tif ( $bytes_read != $content_size ) {\n+\t\t\tdie \"invalid packet ($content_size bytes expected; $bytes_read bytes read)\";\n+\t\t}\n+\t\treturn ( 0, $buffer );\n+\t} else {\n+\t\tdie \"invalid packet size: $pkt_size\";\n+\t}\n+}\n+\n+sub remove_final_lf_or_die {\n+\tmy $buf = shift;\n+\tif ( $buf =~ s/\\n$// ) {\n+\t\treturn $buf;\n+\t}\n+\tdie \"A non-binary line MUST be terminated by an LF.\\n\"\n+\t    . \"Received: '$buf'\";\n+}\n+\n+sub packet_txt_read {\n+\tmy ( $res, $buf ) = packet_bin_read();\n+\tif ( $res != -1 and $buf ne '' ) {\n+\t\t$buf = remove_final_lf_or_die($buf);\n+\t}\n+\treturn ( $res, $buf );\n+}\n+\n+# Read a text line and check that it is in the form \"key=value\"\n+sub packet_key_val_read {\n+\tmy ( $key ) = @_;\n+\tmy ( $res, $buf ) = packet_txt_read();\n+\tif ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {\n+\t\treturn ( $res, $buf );\n+\t}\n+\tdie \"bad $key: '$buf'\";\n+}\n+\n+sub packet_bin_write {\n+\tmy $buf = shift;\n+\tprint STDOUT sprintf( \"%04x\", length($buf) + 4 );\n+\tprint STDOUT $buf;\n+\tSTDOUT->flush();\n+}\n+\n+sub packet_txt_write {\n+\tpacket_bin_write( $_[0] . \"\\n\" );\n+}\n+\n+sub packet_flush {\n+\tprint STDOUT sprintf( \"%04x\", 0 );\n+\tSTDOUT->flush();\n+}\n+\n+sub packet_initialize {\n+\tmy ($name, $version) = @_;\n+\n+\tpacket_compare_lists([0, $name . \"-client\"], packet_txt_read()) ||\n+\t\tdie \"bad initialize\";\n+\tpacket_compare_lists([0, \"version=\" . $version], packet_txt_read()) ||\n+\t\tdie \"bad version\";\n+\tpacket_compare_lists([1, \"\"], packet_bin_read()) ||\n+\t\tdie \"bad version end\";\n+\n+\tpacket_txt_write( $name . \"-server\" );\n+\tpacket_txt_write( \"version=\" . $version );\n+\tpacket_flush();\n+}\n+\n+sub packet_read_capabilities {\n+\tmy @cap;\n+\twhile (1) {\n+\t\tmy ( $res, $buf ) = packet_bin_read();\n+\t\tif ( $res == -1 ) {\n+\t\t\tdie \"unexpected EOF when reading capabilities\";\n+\t\t}\n+\t\treturn ( $res, @cap ) if ( $res != 0 );\n+\t\t$buf = remove_final_lf_or_die($buf);\n+\t\tunless ( $buf =~ s/capability=// ) {\n+\t\t\tdie \"bad capability buf: '$buf'\";\n+\t\t}\n+\t\tpush @cap, $buf;\n+\t}\n+}\n+\n+# Read remote capabilities and check them against capabilities we require\n+sub packet_read_and_check_capabilities {\n+\tmy @required_caps = @_;\n+\tmy ($res, @remote_caps) = packet_read_capabilities();\n+\tmy %remote_caps = map { $_ => 1 } @remote_caps;\n+\tforeach (@required_caps) {\n+\t\tunless (exists($remote_caps{$_})) {\n+\t\t\tdie \"required '$_' capability not available from remote\" ;\n+\t\t}\n+\t}\n+\treturn %remote_caps;\n+}\n+\n+# Check our capabilities we want to advertise against the remote ones\n+# and then advertise our capabilities\n+sub packet_check_and_write_capabilities {\n+\tmy ($remote_caps, @our_caps) = @_;\n+\tforeach (@our_caps) {\n+\t\tunless (exists($remote_caps->{$_})) {\n+\t\t\tdie \"our capability '$_' is not available from remote\"\n+\t\t}\n+\t\tpacket_txt_write( \"capability=\" . $_ );\n+\t}\n+\tpacket_flush();\n+}\n+\n+1;\ndiff --git a/perl/Makefile b/perl/Makefile\nindex 15d96fcc7a..f657de20e3 100644\n--- a/perl/Makefile\n+++ b/perl/Makefile\n@@ -30,6 +30,7 @@ instdir_SQ = $(subst ','\\'',$(prefix)/lib)\n modules += Git\n modules += Git/I18N\n modules += Git/IndexInfo\n+modules += Git/Packet\n modules += Git/SVN\n modules += Git/SVN/Memoize/YAML\n modules += Git/SVN/Fetcher\ndiff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl\nindex 6cd9ecffee..f1678851de 100644\n--- a/t/t0021/rot13-filter.pl\n+++ b/t/t0021/rot13-filter.pl\n@@ -30,9 +30,12 @@\n #     to the \"list_available_blobs\" response.\n #\n \n+use 5.008;\n+use lib (split(/:/, $ENV{GITPERLLIB}));\n use strict;\n use warnings;\n use IO::File;\n+use Git::Packet;\n \n my $MAX_PACKET_CONTENT_SIZE = 65516;\n my $log_file                = shift @ARGV;\n@@ -55,144 +58,6 @@ sub rot13 {\n \treturn $str;\n }\n \n-sub packet_compare_lists {\n-\tmy ($expect, @result) = @_;\n-\tmy $ix;\n-\tif (scalar @$expect != scalar @result) {\n-\t\treturn undef;\n-\t}\n-\tfor ($ix = 0; $ix < $#result; $ix++) {\n-\t\tif ($expect->[$ix] ne $result[$ix]) {\n-\t\t\treturn undef;\n-\t\t}\n-\t}\n-\treturn 1;\n-}\n-\n-sub packet_bin_read {\n-\tmy $buffer;\n-\tmy $bytes_read = read STDIN, $buffer, 4;\n-\tif ( $bytes_read == 0 ) {\n-\t\t# EOF - Git stopped talking to us!\n-\t\treturn ( -1, \"\" );\n-\t} elsif ( $bytes_read != 4 ) {\n-\t\tdie \"invalid packet: '$buffer'\";\n-\t}\n-\tmy $pkt_size = hex($buffer);\n-\tif ( $pkt_size == 0 ) {\n-\t\treturn ( 1, \"\" );\n-\t} elsif ( $pkt_size > 4 ) {\n-\t\tmy $content_size = $pkt_size - 4;\n-\t\t$bytes_read = read STDIN, $buffer, $content_size;\n-\t\tif ( $bytes_read != $content_size ) {\n-\t\t\tdie \"invalid packet ($content_size bytes expected; $bytes_read bytes read)\";\n-\t\t}\n-\t\treturn ( 0, $buffer );\n-\t} else {\n-\t\tdie \"invalid packet size: $pkt_size\";\n-\t}\n-}\n-\n-sub remove_final_lf_or_die {\n-\tmy $buf = shift;\n-\tif ( $buf =~ s/\\n$// ) {\n-\t\treturn $buf;\n-\t}\n-\tdie \"A non-binary line MUST be terminated by an LF.\\n\"\n-\t    . \"Received: '$buf'\";\n-}\n-\n-sub packet_txt_read {\n-\tmy ( $res, $buf ) = packet_bin_read();\n-\tif ( $res != -1 and $buf ne '' ) {\n-\t\t$buf = remove_final_lf_or_die($buf);\n-\t}\n-\treturn ( $res, $buf );\n-}\n-\n-# Read a text line and check that it is in the form \"key=value\"\n-sub packet_key_val_read {\n-\tmy ( $key ) = @_;\n-\tmy ( $res, $buf ) = packet_txt_read();\n-\tif ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {\n-\t\treturn ( $res, $buf );\n-\t}\n-\tdie \"bad $key: '$buf'\";\n-}\n-\n-sub packet_bin_write {\n-\tmy $buf = shift;\n-\tprint STDOUT sprintf( \"%04x\", length($buf) + 4 );\n-\tprint STDOUT $buf;\n-\tSTDOUT->flush();\n-}\n-\n-sub packet_txt_write {\n-\tpacket_bin_write( $_[0] . \"\\n\" );\n-}\n-\n-sub packet_flush {\n-\tprint STDOUT sprintf( \"%04x\", 0 );\n-\tSTDOUT->flush();\n-}\n-\n-sub packet_initialize {\n-\tmy ($name, $version) = @_;\n-\n-\tpacket_compare_lists([0, $name . \"-client\"], packet_txt_read()) ||\n-\t\tdie \"bad initialize\";\n-\tpacket_compare_lists([0, \"version=\" . $version], packet_txt_read()) ||\n-\t\tdie \"bad version\";\n-\tpacket_compare_lists([1, \"\"], packet_bin_read()) ||\n-\t\tdie \"bad version end\";\n-\n-\tpacket_txt_write( $name . \"-server\" );\n-\tpacket_txt_write( \"version=\" . $version );\n-\tpacket_flush();\n-}\n-\n-sub packet_read_capabilities {\n-\tmy @cap;\n-\twhile (1) {\n-\t\tmy ( $res, $buf ) = packet_bin_read();\n-\t\tif ( $res == -1 ) {\n-\t\t\tdie \"unexpected EOF when reading capabilities\";\n-\t\t}\n-\t\treturn ( $res, @cap ) if ( $res != 0 );\n-\t\t$buf = remove_final_lf_or_die($buf);\n-\t\tunless ( $buf =~ s/capability=// ) {\n-\t\t\tdie \"bad capability buf: '$buf'\";\n-\t\t}\n-\t\tpush @cap, $buf;\n-\t}\n-}\n-\n-# Read remote capabilities and check them against capabilities we require\n-sub packet_read_and_check_capabilities {\n-\tmy @required_caps = @_;\n-\tmy ($res, @remote_caps) = packet_read_capabilities();\n-\tmy %remote_caps = map { $_ => 1 } @remote_caps;\n-\tforeach (@required_caps) {\n-\t\tunless (exists($remote_caps{$_})) {\n-\t\t\tdie \"required '$_' capability not available from remote\" ;\n-\t\t}\n-\t}\n-\treturn %remote_caps;\n-}\n-\n-# Check our capabilities we want to advertise against the remote ones\n-# and then advertise our capabilities\n-sub packet_check_and_write_capabilities {\n-\tmy ($remote_caps, @our_caps) = @_;\n-\tforeach (@our_caps) {\n-\t\tunless (exists($remote_caps->{$_})) {\n-\t\t\tdie \"our capability '$_' is not available from remote\"\n-\t\t}\n-\t\tpacket_txt_write( \"capability=\" . $_ );\n-\t}\n-\tpacket_flush();\n-}\n-\n print $debug \"START\\n\";\n $debug->flush();\n \n-- \n2.15.0.132.g7ad97d78be\n\n"},{"id":"332212","messageId":"xmqqvaiidkhv.fsf@gitster.mtv.corp.google.com","threadId":"47154","inReplyTo":"20171110132200.7871-1-chriscool@tuxfamily.org","subject":"Re: [PATCH v3 0/8] Create Git/Packet.pm","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-10T19:03:24Z","receivedAt":"2017-11-10T19:03:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <christian.couder@gmail.com> writes:\n\n> There are only a few small changes compared to v2:\n\nPlease go incremental as v2 is already in 'next', and small changes\nare easier to reiew and understand when given as follow-up \"polish\nthis minor glitch in the initial effort\" patches.\n\nThanks.\n"}]}