{"thread":{"id":"10438","subject":"[PATCH 3/9] git-cat-file: Make option parsing a little more flexible","startedAt":"2007-10-23T05:46:28Z","lastAt":"2007-10-24T06:48:37Z","messageCount":24,"participants":["Adam Roben","Shawn O. Pearce","Mike Hommey","Johannes Sixt","Sam Vilain","Brian Downing","Eric Wong"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"56967","messageId":"1193118397-4696-1-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":null,"subject":"[PATCH 0/9] Make git-svn fetch ~1.7x faster","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:28Z","receivedAt":"2007-10-23T05:46:28Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"\nThis patch series makes git-svn fetch about 1.7x faster by reducing the number\nof forks/execs that occur for each file retrieved from Subversion. To do so, a\nfew new options are added to git-cat-file and git-hash-object to allow\ncontinuous input on stdin and continuous output on stdout, so that one instance\nof each of these commands can be kept running for the duration of the fetch.\n\nThe series is based on top of next. I considered basing it on top of the\nparse_options work since I touch the option parsing in these two commands, but\nI didn't know how wise it would be to base a patch series on something in pu.\n\nI tried to add some new tests for cat-file and hash-object to ensure that I\ndidn't break old behavior, but I'm not very experienced with the git test suite\nand I'm sure my tests could use some improvement. This is the most invasive\nchange I've yet made to git, so comments are more than welcome.\n\n-Adam\n\n--\n Documentation/git-cat-file.txt    |   11 +++-\n Documentation/git-hash-object.txt |    5 +-\n builtin-cat-file.c                |   96 +++++++++++++++++++++----\n git-svn.perl                      |   94 +++++++++++++++++++------\n hash-object.c                     |   29 ++++++++-\n perl/Git.pm                       |   56 +++++++++++++++\n t/t1005-cat-file.sh               |  139 +++++++++++++++++++++++++++++++++++++\n t/t1006-hash-object.sh            |   49 +++++++++++++\n 8 files changed, 438 insertions(+), 41 deletions(-)\n--\n"},{"id":"56968","messageId":"1193118397-4696-2-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-1-git-send-email-aroben@apple.com","subject":"[PATCH 1/9] Add tests for git cat-file","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:29Z","receivedAt":"2007-10-23T05:46:29Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n t/t1005-cat-file.sh |   91 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 91 insertions(+), 0 deletions(-)\n create mode 100755 t/t1005-cat-file.sh\n\ndiff --git a/t/t1005-cat-file.sh b/t/t1005-cat-file.sh\nnew file mode 100755\nindex 0000000..2fdc446\n--- /dev/null\n+++ b/t/t1005-cat-file.sh\n@@ -0,0 +1,91 @@\n+#!/bin/sh\n+\n+test_description='git cat-file'\n+\n+. ./test-lib.sh\n+\n+function maybe_remove_timestamp()\n+{\n+    if test -z \"$2\"; then\n+        echo \"$1\"\n+    else\n+        echo \"$1\" | sed -e 's/ [0-9]\\{10\\} [+-][0-9]\\{4\\}$//'\n+    fi\n+}\n+\n+function run_tests()\n+{\n+    type=$1\n+    sha1=$2\n+    size=$3\n+    content=$4\n+    pretty_content=$5\n+    no_timestamp=$6\n+\n+    test_expect_success \\\n+        \"$type exists\" \\\n+        \"git cat-file -e $hello_sha1\"\n+    test_expect_success \\\n+        \"Type of $type is correct\" \\\n+        \"test $type = \\\"$(git cat-file -t $sha1)\\\"\"\n+    test_expect_success \\\n+        \"Size of $type is correct\" \\\n+        \"test $size = \\\"$(git cat-file -s $sha1)\\\"\"\n+    test -z \"$content\" || test_expect_success \\\n+        \"Content of $type is correct\" \\\n+        \"test \\\"$(maybe_remove_timestamp \"$content\" $no_timestamp)\\\" = \\\"$(maybe_remove_timestamp \"$(git cat-file $type $sha1)\" $no_timestamp)\\\"\"\n+    test_expect_success \\\n+        \"Pretty content of $type is correct\" \\\n+        \"test \\\"$(maybe_remove_timestamp \"$pretty_content\" $no_timestamp)\\\" = \\\"$(maybe_remove_timestamp \"$(git cat-file -p $sha1)\" $no_timestamp)\\\"\"\n+}\n+\n+hello_content=\"Hello World\"\n+hello_size=$(echo \"$hello_content\" | wc -c)\n+hello_sha1=557db03de997c86a4a028e1ebd3a1ceb225be238\n+\n+echo \"$hello_content\" > hello\n+\n+git update-index --add hello\n+\n+run_tests 'blob' $hello_sha1 $hello_size \"$hello_content\" \"$hello_content\"\n+\n+tree_sha1=$(git write-tree)\n+tree_size=33\n+tree_pretty_content=\"100644 blob $hello_sha1\thello\"\n+\n+run_tests 'tree' $tree_sha1 $tree_size \"\" \"$tree_pretty_content\"\n+\n+commit_message=\"Intial commit\"\n+commit_sha1=$(echo \"$commit_message\" | git commit-tree $tree_sha1)\n+commit_size=177\n+commit_content=\"tree $tree_sha1\n+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000\n+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000\n+\n+$commit_message\"\n+\n+run_tests 'commit' $commit_sha1 $commit_size \"$commit_content\" \"$commit_content\" 1\n+\n+tag_header=\"object $hello_sha1\n+type blob\n+tag hellotag\n+tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\"\n+tag_description=\"This is a tag\"\n+tag_content=\"$tag_header\n+\n+$tag_description\"\n+tag_pretty_content=\"$tag_header\n+Thu Jan 1 00:00:00 1970 +0000\n+\n+$tag_description\"\n+\n+tag_sha1=$(echo \"$tag_content\" | git mktag)\n+tag_size=$(echo \"$tag_content\" | wc -c)\n+\n+run_tests 'tag' $tag_sha1 $tag_size \"$tag_content\" \"$tag_pretty_content\"\n+\n+test_expect_success \\\n+    \"Reach a blob from a tag pointing to it\" \\\n+    \"test \\\"$hello_content\\\" = \\\"$(git cat-file blob $tag_sha1)\\\"\"\n+\n+test_done\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56966","messageId":"1193118397-4696-3-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-2-git-send-email-aroben@apple.com","subject":"[PATCH 2/9] git-cat-file: Small refactor of cmd_cat_file","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:30Z","receivedAt":"2007-10-23T05:46:30Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"I separated the logic of parsing the arguments from the logic of fetching and\noutputting the data. cat_one_file now does the latter.\n\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n builtin-cat-file.c |   38 ++++++++++++++++++++++----------------\n 1 files changed, 22 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin-cat-file.c b/builtin-cat-file.c\nindex f132d58..34a63d1 100644\n--- a/builtin-cat-file.c\n+++ b/builtin-cat-file.c\n@@ -76,31 +76,16 @@ static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long\n \t\twrite_or_die(1, cp, endp - cp);\n }\n \n-int cmd_cat_file(int argc, const char **argv, const char *prefix)\n+static int cat_one_file(int opt, const char *exp_type, const char *obj_name)\n {\n \tunsigned char sha1[20];\n \tenum object_type type;\n \tvoid *buf;\n \tunsigned long size;\n-\tint opt;\n-\tconst char *exp_type, *obj_name;\n-\n-\tgit_config(git_default_config);\n-\tif (argc != 3)\n-\t\tusage(\"git-cat-file [-t|-s|-e|-p|<type>] <sha1>\");\n-\texp_type = argv[1];\n-\tobj_name = argv[2];\n \n \tif (get_sha1(obj_name, sha1))\n \t\tdie(\"Not a valid object name %s\", obj_name);\n \n-\topt = 0;\n-\tif ( exp_type[0] == '-' ) {\n-\t\topt = exp_type[1];\n-\t\tif ( !opt || exp_type[2] )\n-\t\t\topt = -1; /* Not a single character option */\n-\t}\n-\n \tbuf = NULL;\n \tswitch (opt) {\n \tcase 't':\n@@ -157,3 +142,24 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)\n \twrite_or_die(1, buf, size);\n \treturn 0;\n }\n+\n+int cmd_cat_file(int argc, const char **argv, const char *prefix)\n+{\n+\tint opt;\n+\tconst char *exp_type, *obj_name;\n+\n+\tgit_config(git_default_config);\n+\tif (argc != 3)\n+\t\tusage(\"git-cat-file [-t|-s|-e|-p|<type>] <sha1>\");\n+\texp_type = argv[1];\n+\tobj_name = argv[2];\n+\n+\topt = 0;\n+\tif ( exp_type[0] == '-' ) {\n+\t\topt = exp_type[1];\n+\t\tif ( !opt || exp_type[2] )\n+\t\t\topt = -1; /* Not a single character option */\n+\t}\n+\n+\treturn cat_one_file(opt, exp_type, obj_name);\n+}\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56965","messageId":"1193118397-4696-4-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-3-git-send-email-aroben@apple.com","subject":"[PATCH 3/9] git-cat-file: Make option parsing a little more flexible","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:31Z","receivedAt":"2007-10-23T05:46:31Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"This will make it easier to add newer options later.\n\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n builtin-cat-file.c |   42 ++++++++++++++++++++++++++++++------------\n 1 files changed, 30 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin-cat-file.c b/builtin-cat-file.c\nindex 34a63d1..3a0be4a 100644\n--- a/builtin-cat-file.c\n+++ b/builtin-cat-file.c\n@@ -143,23 +143,41 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)\n \treturn 0;\n }\n \n+static const char cat_file_usage[] = \"git-cat-file [-t|-s|-e|-p|<type>] <sha1>\";\n+\n int cmd_cat_file(int argc, const char **argv, const char *prefix)\n {\n-\tint opt;\n-\tconst char *exp_type, *obj_name;\n+\tint i, opt = 0;\n+\tconst char *exp_type = 0, *obj_name = 0;\n \n \tgit_config(git_default_config);\n-\tif (argc != 3)\n-\t\tusage(\"git-cat-file [-t|-s|-e|-p|<type>] <sha1>\");\n-\texp_type = argv[1];\n-\tobj_name = argv[2];\n-\n-\topt = 0;\n-\tif ( exp_type[0] == '-' ) {\n-\t\topt = exp_type[1];\n-\t\tif ( !opt || exp_type[2] )\n-\t\t\topt = -1; /* Not a single character option */\n+\n+\tfor (i = 1; i < argc; ++i) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (!strcmp(arg, \"-t\") || !strcmp(arg, \"-s\") || !strcmp(arg, \"-e\") || !strcmp(arg, \"-p\")) {\n+\t\t\texp_type = arg;\n+\t\t\topt = exp_type[1];\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (arg[0] == '-')\n+\t\t\tusage(cat_file_usage);\n+\n+\t\tif (!exp_type) {\n+\t\t\texp_type = arg;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (obj_name)\n+\t\t\tusage(cat_file_usage);\n+\n+\t\tobj_name = arg;\n+\t\tbreak;\n \t}\n \n+\tif (!exp_type || !obj_name)\n+\t\tusage(cat_file_usage);\n+\n \treturn cat_one_file(opt, exp_type, obj_name);\n }\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56969","messageId":"1193118397-4696-5-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-4-git-send-email-aroben@apple.com","subject":"[PATCH 4/9] git-cat-file: Add --stdin option","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:32Z","receivedAt":"2007-10-23T05:46:32Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"This lets you specify object names on stdin instead of on the command line.\n\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n Documentation/git-cat-file.txt |    6 +++++-\n builtin-cat-file.c             |   26 ++++++++++++++++++++++----\n t/t1005-cat-file.sh            |   14 ++++++++++++++\n 3 files changed, 41 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt\nindex afa095c..588d71a 100644\n--- a/Documentation/git-cat-file.txt\n+++ b/Documentation/git-cat-file.txt\n@@ -8,7 +8,7 @@ git-cat-file - Provide content or type/size information for repository objects\n \n SYNOPSIS\n --------\n-'git-cat-file' [-t | -s | -e | -p | <type>] <object>\n+'git-cat-file' [-t | -s | -e | -p | <type>] [--stdin | <object>]\n \n DESCRIPTION\n -----------\n@@ -23,6 +23,10 @@ OPTIONS\n \tFor a more complete list of ways to spell object names, see\n \t\"SPECIFYING REVISIONS\" section in gitlink:git-rev-parse[1].\n \n+--stdin::\n+\tRead object names from stdin instead of specifying one on the\n+\tcommand line.\n+\n -t::\n \tInstead of the content, show the object type identified by\n \t<object>.\ndiff --git a/builtin-cat-file.c b/builtin-cat-file.c\nindex 3a0be4a..0f1ffe5 100644\n--- a/builtin-cat-file.c\n+++ b/builtin-cat-file.c\n@@ -143,12 +143,14 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)\n \treturn 0;\n }\n \n-static const char cat_file_usage[] = \"git-cat-file [-t|-s|-e|-p|<type>] <sha1>\";\n+static const char cat_file_usage[] = \"git-cat-file [-t|-s|-e|-p|<type>] [--stdin | <sha1>]\";\n \n int cmd_cat_file(int argc, const char **argv, const char *prefix)\n {\n \tint i, opt = 0;\n+\tint read_stdin = 0;\n \tconst char *exp_type = 0, *obj_name = 0;\n+\tstruct strbuf buf;\n \n \tgit_config(git_default_config);\n \n@@ -161,6 +163,11 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\t}\n \n+\t\tif (!strcmp(arg, \"--stdin\")) {\n+\t\t    read_stdin = 1;\n+\t\t    continue;\n+\t\t}\n+\n \t\tif (arg[0] == '-')\n \t\t\tusage(cat_file_usage);\n \n@@ -169,15 +176,26 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (obj_name)\n+\t\tif (obj_name || read_stdin)\n \t\t\tusage(cat_file_usage);\n \n \t\tobj_name = arg;\n \t\tbreak;\n \t}\n \n-\tif (!exp_type || !obj_name)\n+\tif (!read_stdin) {\n+\t    if (!exp_type || !obj_name)\n \t\tusage(cat_file_usage);\n+\t    return cat_one_file(opt, exp_type, obj_name);\n+\t}\n \n-\treturn cat_one_file(opt, exp_type, obj_name);\n+\tstrbuf_init(&buf, 0);\n+\twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n+\t\tint error = cat_one_file(opt, exp_type, buf.buf);\n+\t\tif (error)\n+\t\t\treturn error;\n+\t}\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n }\ndiff --git a/t/t1005-cat-file.sh b/t/t1005-cat-file.sh\nindex 2fdc446..49eb89d 100755\n--- a/t/t1005-cat-file.sh\n+++ b/t/t1005-cat-file.sh\n@@ -88,4 +88,18 @@ test_expect_success \\\n     \"Reach a blob from a tag pointing to it\" \\\n     \"test \\\"$hello_content\\\" = \\\"$(git cat-file blob $tag_sha1)\\\"\"\n \n+sha1s=\"$hello_sha1\n+$tree_sha1\n+$commit_sha1\n+$tag_sha1\"\n+\n+sizes=\"$hello_size\n+$tree_size\n+$commit_size\n+$tag_size\"\n+\n+test_expect_success \\\n+    \"Pass object hashes on stdin\" \\\n+    \"test \\\"$sizes\\\" = \\\"$(echo \"$sha1s\" | git cat-file -s --stdin)\\\"\"\n+\n test_done\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56970","messageId":"1193118397-4696-6-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-5-git-send-email-aroben@apple.com","subject":"[PATCH 5/9] git-cat-file: Add --separator option","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:33Z","receivedAt":"2007-10-23T05:46:33Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"This lets the user specify a string to be printed in between the output from\neach object passed on stdin.\n\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n Documentation/git-cat-file.txt |    7 ++++++-\n builtin-cat-file.c             |   28 +++++++++++++++++++++++++---\n t/t1005-cat-file.sh            |   36 +++++++++++++++++++++++++++++++++++-\n 3 files changed, 66 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt\nindex 588d71a..7a59a5e 100644\n--- a/Documentation/git-cat-file.txt\n+++ b/Documentation/git-cat-file.txt\n@@ -8,7 +8,7 @@ git-cat-file - Provide content or type/size information for repository objects\n \n SYNOPSIS\n --------\n-'git-cat-file' [-t | -s | -e | -p | <type>] [--stdin | <object>]\n+'git-cat-file' [-t | -s | -e | -p | <type>] [--stdin [--separator <string>] | <object>]\n \n DESCRIPTION\n -----------\n@@ -27,6 +27,11 @@ OPTIONS\n \tRead object names from stdin instead of specifying one on the\n \tcommand line.\n \n+--separator::\n+\tA string to print in between the output for each object passed on\n+\tstdin. A newline will be appended to the separator each time it is\n+\tprinted.\n+\n -t::\n \tInstead of the content, show the object type identified by\n \t<object>.\ndiff --git a/builtin-cat-file.c b/builtin-cat-file.c\nindex 0f1ffe5..9ae3184 100644\n--- a/builtin-cat-file.c\n+++ b/builtin-cat-file.c\n@@ -92,6 +92,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)\n \t\ttype = sha1_object_info(sha1, NULL);\n \t\tif (type > 0) {\n \t\t\tprintf(\"%s\\n\", typename(type));\n+\t\t\tfflush(stdout);\n \t\t\treturn 0;\n \t\t}\n \t\tbreak;\n@@ -100,6 +101,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)\n \t\ttype = sha1_object_info(sha1, &size);\n \t\tif (type > 0) {\n \t\t\tprintf(\"%lu\\n\", size);\n+\t\t\tfflush(stdout);\n \t\t\treturn 0;\n \t\t}\n \t\tbreak;\n@@ -143,14 +145,16 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)\n \treturn 0;\n }\n \n-static const char cat_file_usage[] = \"git-cat-file [-t|-s|-e|-p|<type>] [--stdin | <sha1>]\";\n+static const char cat_file_usage[] = \"git-cat-file [-t|-s|-e|-p|<type>] [--stdin [--separator <string>] | <sha1>]\";\n+\n+static const char *separator;\n \n int cmd_cat_file(int argc, const char **argv, const char *prefix)\n {\n \tint i, opt = 0;\n \tint read_stdin = 0;\n \tconst char *exp_type = 0, *obj_name = 0;\n-\tstruct strbuf buf;\n+\tstruct strbuf buf, sbuf;\n \n \tgit_config(git_default_config);\n \n@@ -168,6 +172,13 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)\n \t\t    continue;\n \t\t}\n \n+\t\tif (!strcmp(arg, \"--separator\")) {\n+\t\t\tif (++i == argc)\n+\t\t\t\tusage(cat_file_usage);\n+\t\t\tseparator = argv[i];\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (arg[0] == '-')\n \t\t\tusage(cat_file_usage);\n \n@@ -184,18 +195,29 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (!read_stdin) {\n-\t    if (!exp_type || !obj_name)\n+\t    if (!exp_type || !obj_name || separator)\n \t\tusage(cat_file_usage);\n \t    return cat_one_file(opt, exp_type, obj_name);\n \t}\n \n+\tif (separator) {\n+\t\tstrbuf_init(&sbuf, 0);\n+\t\tstrbuf_addstr(&sbuf, separator);\n+\t\tstrbuf_addch(&sbuf, '\\n');\n+\t}\n+\n \tstrbuf_init(&buf, 0);\n \twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n \t\tint error = cat_one_file(opt, exp_type, buf.buf);\n \t\tif (error)\n \t\t\treturn error;\n+\t\tif (separator)\n+\t\t\twrite_or_die(1, sbuf.buf, sbuf.len);\n \t}\n \tstrbuf_release(&buf);\n \n+\tif (separator)\n+\t\tstrbuf_release(&sbuf);\n+\n \treturn 0;\n }\ndiff --git a/t/t1005-cat-file.sh b/t/t1005-cat-file.sh\nindex 49eb89d..52a3efd 100755\n--- a/t/t1005-cat-file.sh\n+++ b/t/t1005-cat-file.sh\n@@ -99,7 +99,41 @@ $commit_size\n $tag_size\"\n \n test_expect_success \\\n-    \"Pass object hashes on stdin\" \\\n+    \"Print sizes for object hashes on stdin\" \\\n     \"test \\\"$sizes\\\" = \\\"$(echo \"$sha1s\" | git cat-file -s --stdin)\\\"\"\n \n+separator=\"TESTSEPARATOR\"\n+\n+separated_sizes=\"$hello_size\n+$separator\n+$tree_size\n+$separator\n+$commit_size\n+$separator\n+$tag_size\n+$separator\"\n+\n+test_expect_success \\\n+    \"Print sizes for object hashes on stdin with --separator\" \\\n+    \"test \\\"$separated_sizes\\\" = \\\"$(echo \"$sha1s\" | git cat-file -s --stdin --separator $separator)\\\"\"\n+\n+sha1s=\"$hello_sha1\n+$hello_sha1\"\n+\n+contents=\"$hello_content\n+$hello_content\"\n+\n+separated_contents=\"$hello_content\n+$separator\n+$hello_content\n+$separator\"\n+\n+test_expect_success \\\n+    \"Print objects for object hashes on stdin\" \\\n+    \"test \\\"$contents\\\" = \\\"$(echo \"$sha1s\" | git cat-file blob --stdin)\\\"\"\n+\n+test_expect_success \\\n+    \"Print objects for object hashes on stdin with --separator\" \\\n+    \"test \\\"$separated_contents\\\" = \\\"$(echo \"$sha1s\" | git cat-file blob --stdin --separator $separator)\\\"\"\n+\n test_done\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56971","messageId":"1193118397-4696-7-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-6-git-send-email-aroben@apple.com","subject":"[PATCH 6/9] Add tests for git hash-object","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:34Z","receivedAt":"2007-10-23T05:46:34Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n t/t1006-hash-object.sh |   27 +++++++++++++++++++++++++++\n 1 files changed, 27 insertions(+), 0 deletions(-)\n create mode 100755 t/t1006-hash-object.sh\n\ndiff --git a/t/t1006-hash-object.sh b/t/t1006-hash-object.sh\nnew file mode 100755\nindex 0000000..77b8eca\n--- /dev/null\n+++ b/t/t1006-hash-object.sh\n@@ -0,0 +1,27 @@\n+#!/bin/sh\n+\n+test_description='git hash-object'\n+\n+. ./test-lib.sh\n+\n+hello_content=\"Hello World\"\n+hello_sha1=557db03de997c86a4a028e1ebd3a1ceb225be238\n+echo \"$hello_content\" > hello\n+\n+test_expect_success \\\n+    'hash a file' \\\n+    \"test $hello_sha1 = $(git hash-object hello)\"\n+\n+test_expect_success \\\n+    'hash from stdin' \\\n+    \"test $hello_sha1 = $(echo \"$hello_content\" | git hash-object --stdin)\"\n+\n+test_expect_success \\\n+    'hash a file and write to database' \\\n+    \"test $hello_sha1 = $(git hash-object -w hello)\"\n+\n+test_expect_success \\\n+    'hash from stdin and write to database' \\\n+    \"test $hello_sha1 = $(echo \"$hello_content\" | git hash-object -w --stdin)\"\n+\n+test_done\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56972","messageId":"1193118397-4696-8-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-7-git-send-email-aroben@apple.com","subject":"[PATCH 7/9] git-hash-object: Add --stdin-paths option","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:35Z","receivedAt":"2007-10-23T05:46:35Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"This allows multiple paths to be specified on stdin.\n\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n Documentation/git-hash-object.txt |    5 ++++-\n hash-object.c                     |   29 ++++++++++++++++++++++++++++-\n t/t1006-hash-object.sh            |   22 ++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt\nindex 616f196..50fc401 100644\n--- a/Documentation/git-hash-object.txt\n+++ b/Documentation/git-hash-object.txt\n@@ -8,7 +8,7 @@ git-hash-object - Compute object ID and optionally creates a blob from a file\n \n SYNOPSIS\n --------\n-'git-hash-object' [-t <type>] [-w] [--stdin] [--] <file>...\n+'git-hash-object' [-t <type>] [-w] [--stdin | --stdin-paths] [--] <file>...\n \n DESCRIPTION\n -----------\n@@ -32,6 +32,9 @@ OPTIONS\n --stdin::\n \tRead the object from standard input instead of from a file.\n \n+--stdin-paths::\n+\tRead file names from stdin instead of from the command-line.\n+\n Author\n ------\n Written by Junio C Hamano <junkio@cox.net>\ndiff --git a/hash-object.c b/hash-object.c\nindex 18f5017..fd96d50 100644\n--- a/hash-object.c\n+++ b/hash-object.c\n@@ -20,6 +20,7 @@ static void hash_object(const char *path, enum object_type type, int write_objec\n \t\t    ? \"Unable to add %s to database\"\n \t\t    : \"Unable to hash %s\", path);\n \tprintf(\"%s\\n\", sha1_to_hex(sha1));\n+\tmaybe_flush_or_die(stdout, \"hash to stdout\");\n }\n \n static void hash_stdin(const char *type, int write_object)\n@@ -31,7 +32,7 @@ static void hash_stdin(const char *type, int write_object)\n }\n \n static const char hash_object_usage[] =\n-\"git-hash-object [-t <type>] [-w] [--stdin] <file>...\";\n+\"git-hash-object [-t <type>] [-w] [--stdin | --stdin-paths] <file>...\";\n \n int main(int argc, char **argv)\n {\n@@ -41,6 +42,7 @@ int main(int argc, char **argv)\n \tconst char *prefix = NULL;\n \tint prefix_length = -1;\n \tint no_more_flags = 0;\n+\tint found_stdin_flag = 0;\n \n \tfor (i = 1 ; i < argc; i++) {\n \t\tif (!no_more_flags && argv[i][0] == '-') {\n@@ -62,7 +64,32 @@ int main(int argc, char **argv)\n \t\t\t}\n \t\t\telse if (!strcmp(argv[i], \"--help\"))\n \t\t\t\tusage(hash_object_usage);\n+\t\t\telse if (!strcmp(argv[i], \"--stdin-paths\")) {\n+\t\t\t\tstruct strbuf buf, nbuf;\n+\n+\t\t\t\tif (found_stdin_flag)\n+\t\t\t\t\tdie(\"Can't use both --stdin and --stdin-paths\");\n+\t\t\t\tfound_stdin_flag = 1;\n+\n+\t\t\t\tstrbuf_init(&buf, 0);\n+\t\t\t\tstrbuf_init(&nbuf, 0);\n+\t\t\t\twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n+\t\t\t\t\tif (buf.buf[0] == '\"') {\n+\t\t\t\t\t\tstrbuf_reset(&nbuf);\n+\t\t\t\t\t\tif (unquote_c_style(&nbuf, buf.buf, NULL))\n+\t\t\t\t\t\t\tdie(\"line is badly quoted\");\n+\t\t\t\t\t\tstrbuf_swap(&buf, &nbuf);\n+\t\t\t\t\t}\n+\t\t\t\t\thash_object(buf.buf, type_from_string(type), write_object);\n+\t\t\t\t}\n+\t\t\t\tstrbuf_release(&buf);\n+\t\t\t\tstrbuf_release(&nbuf);\n+\t\t\t}\n \t\t\telse if (!strcmp(argv[i], \"--stdin\")) {\n+\t\t\t\tif (found_stdin_flag)\n+\t\t\t\t\tdie(\"Can't use both --stdin and --stdin-paths\");\n+\t\t\t\tfound_stdin_flag = 1;\n+\n \t\t\t\thash_stdin(type, write_object);\n \t\t\t}\n \t\t\telse\ndiff --git a/t/t1006-hash-object.sh b/t/t1006-hash-object.sh\nindex 77b8eca..e6da1c1 100755\n--- a/t/t1006-hash-object.sh\n+++ b/t/t1006-hash-object.sh\n@@ -24,4 +24,26 @@ test_expect_success \\\n     'hash from stdin and write to database' \\\n     \"test $hello_sha1 = $(echo \"$hello_content\" | git hash-object -w --stdin)\"\n \n+example_content=\"Silly example\"\n+example_sha1=f24c74a2e500f5ee1332c86b94199f52b1d1d962\n+echo \"$example_content\" > example\n+\n+filenames=\"hello\n+example\"\n+\n+sha1s=\"$hello_sha1\n+$example_sha1\"\n+\n+test_expect_success \\\n+    'hash two files with names on stdin' \\\n+    \"test \\\"$sha1s\\\" = \\\"$(echo \"$filenames\" | git hash-object --stdin-paths)\\\"\"\n+\n+test_expect_success \\\n+    'hash two files with names on stdin and write to database' \\\n+    \"test \\\"$sha1s\\\" = \\\"$(echo \"$filenames\" | git hash-object --stdin-paths)\\\"\"\n+\n+test_expect_failure \\\n+    \"Can't use --stdin and --stdin-paths together\" \\\n+    \"echo \\\"$filenames\\\" | git hash-object --stdin --stdin-paths\"\n+\n test_done\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56974","messageId":"1193118397-4696-9-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-8-git-send-email-aroben@apple.com","subject":"[PATCH 8/9] Git.pm: Add command_bidi_pipe and command_close_bidi_pipe","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:36Z","receivedAt":"2007-10-23T05:46:36Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"command_bidi_pipe hands back the stdin and stdout file handles from the\nexecuted command. command_close_bidi_pipe closes these handles and terminates\nthe process.\n\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n perl/Git.pm |   56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 56 insertions(+), 0 deletions(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 3f4080c..eb699ff 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -51,6 +51,7 @@ require Exporter;\n # Methods which can be called as standalone functions as well:\n @EXPORT_OK = qw(command command_oneline command_noisy\n                 command_output_pipe command_input_pipe command_close_pipe\n+                command_bidi_pipe command_close_bidi_pipe\n                 version exec_path hash_object git_cmd_try);\n \n \n@@ -92,6 +93,7 @@ increate nonwithstanding).\n use Carp qw(carp croak); # but croak is bad - throw instead\n use Error qw(:try);\n use Cwd qw(abs_path);\n+use IPC::Open2 qw(open2);\n \n }\n \n@@ -375,6 +377,60 @@ sub command_close_pipe {\n \t_cmd_close($fh, $ctx);\n }\n \n+=item command_bidi_pipe ( COMMAND [, ARGUMENTS... ] )\n+\n+Execute the given C<COMMAND> in the same way as command_output_pipe()\n+does but return both an input pipe filehandle and an output pipe filehandle.\n+\n+The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>.\n+See C<command_close_bidi_pipe()> for details.\n+\n+=cut\n+\n+sub command_bidi_pipe {\n+\tmy ($pid, $in, $out);\n+\t$pid = open2($in, $out, @_);\n+\treturn ($pid, $in, $out, join(' ', @_));\n+}\n+\n+=item command_close_bidi_pipe ( PID, PIPE_IN, PIPE_OUT [, CTX] )\n+\n+Close the C<PIPE_IN> and C<PIPE_OUT> as returned from C<command_bidi_pipe()>,\n+checking whether the command finished successfully. The optional C<CTX>\n+argument is required if you want to see the command name in the error message,\n+and it is the fourth value returned by C<command_bidi_pipe()>.  The call idiom\n+is:\n+\n+\tmy ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --stdin');\n+\tprint \"000000000\\n\" $out;\n+\twhile (<$in>) { ... }\n+\t$r->command_close_bidi_pipe($pid, $in, $out, $ctx);\n+\n+Note that you should not rely on whatever actually is in C<CTX>;\n+currently it is simply the command name but in future the context might\n+have more complicated structure.\n+\n+=cut\n+\n+sub command_close_bidi_pipe {\n+\tmy ($pid, $in, $out, $ctx) = @_;\n+\tforeach my $fh ($in, $out) {\n+\t\tif (not close $fh) {\n+\t\t\tif ($!) {\n+\t\t\t\tcarp \"error closing pipe: $!\";\n+\t\t\t} elsif ($? >> 8) {\n+\t\t\t\tthrow Git::Error::Command($ctx, $? >>8);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\twaitpid $pid, 0;\n+\n+\tif ($? >> 8) {\n+\t\tthrow Git::Error::Command($ctx, $? >>8);\n+\t}\n+}\n+\n \n =item command_noisy ( COMMAND [, ARGUMENTS... ] )\n \n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56973","messageId":"1193118397-4696-10-git-send-email-aroben@apple.com","threadId":"10438","inReplyTo":"1193118397-4696-9-git-send-email-aroben@apple.com","subject":"[PATCH 9/9] git-svn: Make fetch ~1.7x faster","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:46:37Z","receivedAt":"2007-10-23T05:46:37Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"We were spending a lot of time forking/execing git-cat-file and\ngit-hash-object. We now use command_bidi_pipe to keep one instance of each\nrunning and feed it input on stdin.\n\nSigned-off-by: Adam Roben <aroben@apple.com>\n---\n git-svn.perl |   94 ++++++++++++++++++++++++++++++++++++++++++++-------------\n 1 files changed, 72 insertions(+), 22 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 22bb47b..8b72046 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -236,6 +236,8 @@ eval {\n };\n fatal $@ if $@;\n post_fetch_checkout();\n+Git::Commands->close_cat_blob();\n+Git::Commands->close_hash_object();\n exit 0;\n \n ####################### primary functions ######################\n@@ -2683,14 +2685,8 @@ sub apply_textdelta {\n \tmy $base = IO::File->new_tmpfile;\n \t$base->autoflush(1);\n \tif ($fb->{blob}) {\n-\t\tdefined (my $pid = fork) or croak $!;\n-\t\tif (!$pid) {\n-\t\t\topen STDOUT, '>&', $base or croak $!;\n-\t\t\tprint STDOUT 'link ' if ($fb->{mode_a} == 120000);\n-\t\t\texec qw/git-cat-file blob/, $fb->{blob} or croak $!;\n-\t\t}\n-\t\twaitpid $pid, 0;\n-\t\tcroak $? if $?;\n+\t\tmy $contents = Git::Commands->cat_blob($fb->{blob});\n+\t\tprint $base $contents;\n \n \t\tif (defined $exp) {\n \t\t\tseek $base, 0, 0 or croak $!;\n@@ -2729,13 +2725,7 @@ sub close_file {\n \t\t\t$buf eq 'link ' or die \"$path has mode 120000\",\n \t\t\t                       \"but is not a link\\n\";\n \t\t}\n-\t\tdefined(my $pid = open my $out,'-|') or die \"Can't fork: $!\\n\";\n-\t\tif (!$pid) {\n-\t\t\topen STDIN, '<&', $fh or croak $!;\n-\t\t\texec qw/git-hash-object -w --stdin/ or croak $!;\n-\t\t}\n-\t\tchomp($hash = do { local $/; <$out> });\n-\t\tclose $out or croak $!;\n+\t\t$hash = Git::Commands->hash_object($fh);\n \t\tclose $fh or croak $!;\n \t\t$hash =~ /^[a-f\\d]{40}$/ or die \"not a sha1: $hash\\n\";\n \t\tclose $fb->{base} or croak $!;\n@@ -3063,13 +3053,8 @@ sub chg_file {\n \t} elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {\n \t\t$self->change_file_prop($fbat,'svn:special',undef);\n \t}\n-\tdefined(my $pid = fork) or croak $!;\n-\tif (!$pid) {\n-\t\topen STDOUT, '>&', $fh or croak $!;\n-\t\texec qw/git-cat-file blob/, $m->{sha1_b} or croak $!;\n-\t}\n-\twaitpid $pid, 0;\n-\tcroak $? if $?;\n+\tmy $blob = Git::Commands->cat_blob($m->{sha1_b});\n+\tprint $fh $blob;\n \t$fh->flush == 0 or croak $!;\n \tseek $fh, 0, 0 or croak $!;\n \n@@ -4272,6 +4257,71 @@ sub full_path {\n \t       $path . (length $self->{right} ? \"/$self->{right}\" : '');\n }\n \n+package Git::Commands;\n+use vars qw/$_cat_blob_pid $_cat_blob_in $_cat_blob_out $_cat_blob_ctx $_cat_blob_separator\n+\t    $_hash_object_pid $_hash_object_in $_hash_object_out $_hash_object_ctx/;\n+use strict;\n+use warnings;\n+use File::Temp qw/tempfile/;\n+use Git qw/command_bidi_pipe command_close_bidi_pipe/;\n+\n+sub _open_cat_blob_if_needed {\n+\treturn if defined($_cat_blob_pid);\n+\t$_cat_blob_separator = \"--------------GITCATFILESEPARATOR-----------\";\n+\n+\t($_cat_blob_pid, $_cat_blob_in, $_cat_blob_out, $_cat_blob_ctx) = command_bidi_pipe(qw(git-cat-file blob --stdin --separator), $_cat_blob_separator);\n+}\n+\n+sub close_cat_blob {\n+\treturn unless defined($_cat_blob_pid);\n+\n+\tcommand_close_bidi_pipe($_cat_blob_pid, $_cat_blob_in, $_cat_blob_out, $_cat_blob_ctx);\n+}\n+\n+sub cat_blob {\n+\tmy (undef, $sha1) = @_;\n+\n+\t_open_cat_blob_if_needed();\n+\tprint $_cat_blob_out \"$sha1\\n\";\n+\tmy @file = ();\n+\twhile (my $line = <$_cat_blob_in>) {\n+\t\tmy $last = 0;\n+\t\tif ($line =~ s/\\Q$_cat_blob_separator\\E$//) {\n+\t\t\tchomp($line);\n+\t\t\t$last = 1;\n+\t\t}\n+\t\tpush(@file, $line);\n+\t\tlast if $last;\n+\t}\n+\treturn join('', @file);\n+}\n+\n+sub _open_hash_object_if_needed {\n+\treturn if defined($_hash_object_pid);\n+\n+\t($_hash_object_pid, $_hash_object_in, $_hash_object_out, $_hash_object_ctx) = command_bidi_pipe(qw(git-hash-object -w --stdin-paths));\n+}\n+\n+sub close_hash_object {\n+\treturn unless defined($_hash_object_pid);\n+\n+\tcommand_close_bidi_pipe($_hash_object_pid, $_hash_object_in, $_hash_object_out, $_hash_object_ctx);\n+}\n+\n+sub hash_object {\n+\tmy (undef, $fh) = @_;\n+\n+\tmy ($tmp_fh, $tmp_filename) = tempfile(UNLINK => 1);\n+\twhile (my $line = <$fh>) {\n+\t\tprint $tmp_fh $line;\n+\t}\n+\tclose($tmp_fh);\n+\t_open_hash_object_if_needed();\n+\tprint $_hash_object_out $tmp_filename . \"\\n\";\n+\tchomp(my $hash = <$_hash_object_in>);\n+\treturn $hash;\n+}\n+\n __END__\n \n Data structures:\n-- \n1.5.3.4.1333.ga2f32\n"},{"id":"56975","messageId":"20071023055331.GF14735@spearce.org","threadId":"10438","inReplyTo":"1193118397-4696-8-git-send-email-aroben@apple.com","subject":"Re: [PATCH 7/9] git-hash-object: Add --stdin-paths option","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-10-23T05:53:31Z","receivedAt":"2007-10-23T05:53:31Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Adam Roben <aroben@apple.com> wrote:\n> This allows multiple paths to be specified on stdin.\n\ngit-fast-import wasn't suited to the task?\n \n-- \nShawn.\n"},{"id":"56976","messageId":"471D8D34.4050104@apple.com","threadId":"10438","inReplyTo":"20071023055331.GF14735@spearce.org","subject":"Re: [PATCH 7/9] git-hash-object: Add --stdin-paths option","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T05:57:08Z","receivedAt":"2007-10-23T05:57:08Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> Adam Roben <aroben@apple.com> wrote:\n>   \n>> This allows multiple paths to be specified on stdin.\n>>     \n>\n> git-fast-import wasn't suited to the task?\n>   \n\nI actually considered using fast-import for the whole shebang, but \ndecided that I don't yet understand the workings and structure of \ngit-svn well enough to make such a big change.\n\ngit-svn uses git-hash-object to both determine a file's hash and insert \nit into the index in one go -- can fast-import do this? Or will it just \nput it in the index and not give you the hash back? The latter was my \nimpression.\n\n-Adam\n"},{"id":"56977","messageId":"20071023060812.GA30978@glandium.org","threadId":"10438","inReplyTo":"1193118397-4696-1-git-send-email-aroben@apple.com","subject":"Re: [PATCH 0/9] Make git-svn fetch ~1.7x faster","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2007-10-23T06:08:13Z","receivedAt":"2007-10-23T06:08:13Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Mon, Oct 22, 2007 at 10:46:28PM -0700, Adam Roben wrote:\n> \n> This patch series makes git-svn fetch about 1.7x faster by reducing the number\n> of forks/execs that occur for each file retrieved from Subversion. To do so, a\n> few new options are added to git-cat-file and git-hash-object to allow\n> continuous input on stdin and continuous output on stdout, so that one instance\n> of each of these commands can be kept running for the duration of the fetch.\n\nYou don't need to do this to avoid forks. Just use git-fast-import\ninstead.\n\nMike\n"},{"id":"56978","messageId":"20071023061022.GG14735@spearce.org","threadId":"10438","inReplyTo":"471D8D34.4050104@apple.com","subject":"Re: [PATCH 7/9] git-hash-object: Add --stdin-paths option","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-10-23T06:10:22Z","receivedAt":"2007-10-23T06:10:22Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Adam Roben <aroben@apple.com> wrote:\n> Shawn O. Pearce wrote:\n> >Adam Roben <aroben@apple.com> wrote:\n> >  \n> >>This allows multiple paths to be specified on stdin.\n> >\n> >git-fast-import wasn't suited to the task?\n> \n> I actually considered using fast-import for the whole shebang, but \n> decided that I don't yet understand the workings and structure of \n> git-svn well enough to make such a big change.\n> \n> git-svn uses git-hash-object to both determine a file's hash and insert \n> it into the index in one go -- can fast-import do this? Or will it just \n> put it in the index and not give you the hash back? The latter was my \n> impression.\n\nIt doesn't currently give you the hash back.  You can sort of get\nto it by marking the blob then using the 'checkpoint' command to\ndump the marks to a file, which you can read in.  Not good.\n\nIt probably wouldn't be very difficult to give fast-import a way\nto dump marks back on stdout as they are assigned.  So long as the\nfrontend either locksteps with fast-import or is willing to monitor\nit with a select/poll type of arrangement and read from stdout as\nsoon as its ready.\n\nProbably a 5 line code change to fast-import.  Like this.  Only Git\nwon't recognize that object SHA-1 as its in a packfile that has\nno index.  You'd need to 'checkpoint' to flush the object out, or\njust use all of fast-import for the processing.  So yea, I guess\nI can see now how its not suited to this.\n\n\n--8>--\ndiff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt\nindex d511967..7fd8b2c 100644\n--- a/Documentation/git-fast-import.txt\n+++ b/Documentation/git-fast-import.txt\n@@ -67,6 +67,10 @@ OPTIONS\n \tat checkpoint (or completion) the same path can also be\n \tsafely given to \\--import-marks.\n \n+--export-marks-to-stdout::\n+\tDumps marks to stdout as soon as they are assigned.\n+\tMarks are written one per line as `:markid SHA-1`.\n+\n --import-marks=<file>::\n \tBefore processing any input, load the marks specified in\n \t<file>.  The input file must exist, must be readable, and\ndiff --git a/fast-import.c b/fast-import.c\nindex 6f888f6..619ed05 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -272,6 +272,7 @@ struct recent_command\n static unsigned long max_depth = 10;\n static off_t max_packsize = (1LL << 32) - 1;\n static int force_update;\n+static int marks_to_stdout;\n \n /* Stats and misc. counters */\n static uintmax_t alloc_count;\n@@ -561,6 +562,7 @@ static char *pool_strdup(const char *s)\n \n static void insert_mark(uintmax_t idnum, struct object_entry *oe)\n {\n+\tuintmax_t orig_idnum = idnum;\n \tstruct mark_set *s = marks;\n \twhile ((idnum >> s->shift) >= 1024) {\n \t\ts = pool_calloc(1, sizeof(struct mark_set));\n@@ -580,6 +582,8 @@ static void insert_mark(uintmax_t idnum, struct object_entry *oe)\n \tif (!s->data.marked[idnum])\n \t\tmarks_set_count++;\n \ts->data.marked[idnum] = oe;\n+\tif (marks_to_stdout)\n+\t\tprintf(\":%\" PRIuMAX \" %s\\n\", orig_idnum, sha1_to_hex(oe->sha1));\n }\n \n static struct object_entry *find_mark(uintmax_t idnum)\n@@ -2294,6 +2298,8 @@ int main(int argc, const char **argv)\n \t\t\tmax_active_branches = strtoul(a + 18, NULL, 0);\n \t\telse if (!prefixcmp(a, \"--import-marks=\"))\n \t\t\timport_marks(a + 15);\n+\t\telse if (!prefixcmp(a, \"--export-marks-to-stdout\"))\n+\t\t\tmarks_to_stdout = 1;\n \t\telse if (!prefixcmp(a, \"--export-marks=\"))\n \t\t\tmark_file = a + 15;\n \t\telse if (!prefixcmp(a, \"--export-pack-edges=\")) {\n\n-- \nShawn.\n"},{"id":"56979","messageId":"471D911C.6070704@apple.com","threadId":"10438","inReplyTo":"20071023060812.GA30978@glandium.org","subject":"Re: [PATCH 0/9] Make git-svn fetch ~1.7x faster","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-23T06:13:48Z","receivedAt":"2007-10-23T06:13:48Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"Mike Hommey wrote:\n> On Mon, Oct 22, 2007 at 10:46:28PM -0700, Adam Roben wrote:\n>   \n>> This patch series makes git-svn fetch about 1.7x faster by reducing the number\n>> of forks/execs that occur for each file retrieved from Subversion. To do so, a\n>> few new options are added to git-cat-file and git-hash-object to allow\n>> continuous input on stdin and continuous output on stdout, so that one instance\n>> of each of these commands can be kept running for the duration of the fetch.\n>>     \n>\n> You don't need to do this to avoid forks. Just use git-fast-import\n> instead.\n>   \n\n\nI agree that fast-import is probably ultimately a better solution for \nthis, but given that git-svn currently uses the output of every command \nit forks off and that fast-import doesn't seem to give the same output, \nchanging git-svn to use fast-import would be a fairly sweeping change \nthat I didn't feel comfortable making without a better understanding of \ngit-svn.\n\n-Adam\n"},{"id":"56984","messageId":"471D9BC7.2070702@viscovery.net","threadId":"10438","inReplyTo":"1193118397-4696-2-git-send-email-aroben@apple.com","subject":"Re: [PATCH 1/9] Add tests for git cat-file","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2007-10-23T06:59:19Z","receivedAt":"2007-10-23T06:59:19Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Adam Roben schrieb:\n> +    test_expect_success \\\n> +        \"$type exists\" \\\n> +        \"git cat-file -e $hello_sha1\"\n\nYou mean $sha1 here, right?\n\n> +    test_expect_success \\\n> +        \"Type of $type is correct\" \\\n> +        \"test $type = \\\"$(git cat-file -t $sha1)\\\"\"\n\nThis should escape the $(...) in all the tests. Like this:\n\n         \"test $type = \\\"\\$(git cat-file -t $sha1)\\\"\"\n\n> +test_expect_success \\\n> +    \"Reach a blob from a tag pointing to it\" \\\n> +    \"test \\\"$hello_content\\\" = \\\"$(git cat-file blob $tag_sha1)\\\"\"\n\nAnd use single quotes without escaping the double-quotes here.\n\n-- Hannes\n"},{"id":"56985","messageId":"471D9BCE.1010709@viscovery.net","threadId":"10438","inReplyTo":"1193118397-4696-7-git-send-email-aroben@apple.com","subject":"Re: [PATCH 6/9] Add tests for git hash-object","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2007-10-23T06:59:26Z","receivedAt":"2007-10-23T06:59:26Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Adam Roben schrieb:\n> +test_expect_success \\\n> +    'hash a file' \\\n> +    \"test $hello_sha1 = $(git hash-object hello)\"\n\nPut tests in double-quotes; otherwise, the substitutions happen before the \ntest begins, and not as part of the test.\n\n-- Hannes\n"},{"id":"56986","messageId":"471D9C65.6090106@viscovery.net","threadId":"10438","inReplyTo":"1193118397-4696-10-git-send-email-aroben@apple.com","subject":"Re: [PATCH 9/9] git-svn: Make fetch ~1.7x faster","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2007-10-23T07:01:57Z","receivedAt":"2007-10-23T07:01:57Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Adam Roben schrieb:\n> We were spending a lot of time forking/execing git-cat-file and\n> git-hash-object. We now use command_bidi_pipe to keep one instance of each\n> running and feed it input on stdin.\n\nI appreciate this. It's certainly going to be a much bigger win on Windows, \nalthough git svn doesn't work (in the MinGW port) at this time because of \nthe old perl and the missing SVN module.\n\n-- Hannes\n"},{"id":"57044","messageId":"471E9547.2030100@vilain.net","threadId":"10438","inReplyTo":"20071023060812.GA30978@glandium.org","subject":"Re: [PATCH 0/9] Make git-svn fetch ~1.7x faster","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2007-10-24T00:43:51Z","receivedAt":"2007-10-24T00:43:51Z","isPatch":true,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"Mike Hommey wrote:\n> On Mon, Oct 22, 2007 at 10:46:28PM -0700, Adam Roben wrote:\n>> This patch series makes git-svn fetch about 1.7x faster by reducing the number\n>> of forks/execs that occur for each file retrieved from Subversion. To do so, a\n>> few new options are added to git-cat-file and git-hash-object to allow\n>> continuous input on stdin and continuous output on stdout, so that one instance\n>> of each of these commands can be kept running for the duration of the fetch.\n> \n> You don't need to do this to avoid forks. Just use git-fast-import\n> instead.\n\ngit-fast-import only covers the hash-object side of things, not cat-file.\n\ngit-fast-import does not currently suit 'gradual deployment' for\nconverters such as git-svn, because it;\n\n  - returns object IDs at the end, when you checkpoint.\n\n    This could be 'fixed' by allowing a marks log file instead of or in\n    addition to the current behaviour, though if the exporter is\n    continually waiting for the tokens rather than using marks, it will\n    slow it down.\n\n  - you can't use plumbing commands, such as rev-parse, cat-file, etc on\n    objects which have not been checkpointed yet.\n\n  - can't just stream a file of unknown length to it as you can to\n    hash-object\n\nThese are the design trade-offs of using fast-import.  Using\nfast-import, you are creating a 'transaction' area which uses user\nsequences instead of (git)database-issued identifiers.  And this\ntransaction is isolated from the other concurrent users of the object\ndatabase.  However the interface does not have the full git CLI\navailable to it, so unlike a regular database transaction, you end up\nhaving to care.\n\nRewriting the importer so as to correctly deal with these problems is\nquite challenging, and for slow import sources such as Subversion, of\nlimited merit.\n\nSam.\n"},{"id":"57051","messageId":"20071024034301.GE24924@lavos.net","threadId":"10438","inReplyTo":"1193118397-4696-6-git-send-email-aroben@apple.com","subject":"Re: [PATCH 5/9] git-cat-file: Add --separator option","fromName":"Brian Downing","fromEmail":"bdowning@lavos.net","sentAt":"2007-10-24T03:43:01Z","receivedAt":"2007-10-24T03:43:01Z","isPatch":true,"sender":{"key":"bdowning@lavos.net","avatar":"https://avatars.githubusercontent.com/u/366426?v=4"},"body":"On Mon, Oct 22, 2007 at 10:46:33PM -0700, Adam Roben wrote:\n> +--separator::\n> +\tA string to print in between the output for each object passed on\n> +\tstdin. A newline will be appended to the separator each time it is\n> +\tprinted.\n\nMaybe I'm just unreasonably paranoid, but I don't think I could ever\ntrust that you'd never find an arbitrary separator in the data.  I\nsuppose if you scanned the files beforehand you could come up with\nsomething guaranteed to be unique, but that seems like a pain (and\ndoesn't happen regardless in patch 9/9; it just uses\n\"--------------GITCATFILESEPARATOR-----------\")  If I were committing to\nSVN, it's sure not something I'd like to bet the integrity of my data\non.\n\nI think a far more reasonable output format for multiple objects would\nbe something like:\n\n<count> LF\n<raw data> LF\n\nWhere <count> is the number of bytes in the <raw data> as an ASCII\ndecimal integer.\n\nThis is pretty much the spiritual analog to the fast-import \"exact byte\ncount\" data input format as well.\n\n-bcd\n"},{"id":"57053","messageId":"471EC95D.6050201@apple.com","threadId":"10438","inReplyTo":"20071024034301.GE24924@lavos.net","subject":"Re: [PATCH 5/9] git-cat-file: Add --separator option","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-24T04:26:05Z","receivedAt":"2007-10-24T04:26:05Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"Brian Downing wrote:\n> On Mon, Oct 22, 2007 at 10:46:33PM -0700, Adam Roben wrote:\n>   \n>> +--separator::\n>> +\tA string to print in between the output for each object passed on\n>> +\tstdin. A newline will be appended to the separator each time it is\n>> +\tprinted.\n>>     \n>\n> Maybe I'm just unreasonably paranoid, but I don't think I could ever\n> trust that you'd never find an arbitrary separator in the data.  I\n> suppose if you scanned the files beforehand you could come up with\n> something guaranteed to be unique, but that seems like a pain (and\n> doesn't happen regardless in patch 9/9; it just uses\n> \"--------------GITCATFILESEPARATOR-----------\")  If I were committing to\n> SVN, it's sure not something I'd like to bet the integrity of my data\n> on.\n>   \n\nI had some of the same concerns.\n\n> I think a far more reasonable output format for multiple objects would\n> be something like:\n>\n> <count> LF\n> <raw data> LF\n>\n> Where <count> is the number of bytes in the <raw data> as an ASCII\n> decimal integer.\n>   \n\nThis sounds like a much better solution. I'll implement it that way and \nsend out a new patch. Thanks for the suggestion!\n\n-Adam\n"},{"id":"57062","messageId":"20071024061123.GA10916@soma","threadId":"10438","inReplyTo":"20071023061022.GG14735@spearce.org","subject":"Re: [PATCH 7/9] git-hash-object: Add --stdin-paths option","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-10-24T06:11:23Z","receivedAt":"2007-10-24T06:11:23Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"\"Shawn O. Pearce\" <spearce@spearce.org> wrote:\n> Adam Roben <aroben@apple.com> wrote:\n> > Shawn O. Pearce wrote:\n> > >Adam Roben <aroben@apple.com> wrote:\n> > >  \n> > >>This allows multiple paths to be specified on stdin.\n> > >\n> > >git-fast-import wasn't suited to the task?\n> > \n> > I actually considered using fast-import for the whole shebang, but \n> > decided that I don't yet understand the workings and structure of \n> > git-svn well enough to make such a big change.\n> > \n> > git-svn uses git-hash-object to both determine a file's hash and insert \n> > it into the index in one go -- can fast-import do this? Or will it just \n> > put it in the index and not give you the hash back? The latter was my \n> > impression.\n> \n> It doesn't currently give you the hash back.  You can sort of get\n> to it by marking the blob then using the 'checkpoint' command to\n> dump the marks to a file, which you can read in.  Not good.\n> \n> It probably wouldn't be very difficult to give fast-import a way\n> to dump marks back on stdout as they are assigned.  So long as the\n> frontend either locksteps with fast-import or is willing to monitor\n> it with a select/poll type of arrangement and read from stdout as\n> soon as its ready.\n> \n> Probably a 5 line code change to fast-import.  Like this.  Only Git\n> won't recognize that object SHA-1 as its in a packfile that has\n> no index.  You'd need to 'checkpoint' to flush the object out, or\n> just use all of fast-import for the processing.  So yea, I guess\n> I can see now how its not suited to this.\n\nShawn, thanks for clearing that up.  I was previously considering\nfast-import for git-svn, but never had time[1] to really look at it.\n\nI guess Adam is on the right track with his patches.\n\n[1] - Sorry to all on the list, but I've really been slacking on git-svn\n      work.  I was going to get some stuff done this weekend but decided\n      to attempt to fight my nasty caffeine addiction instead :x\n\n-- \nEric Wong\n"},{"id":"57063","messageId":"20071024063401.GB10916@soma","threadId":"10438","inReplyTo":"1193118397-4696-10-git-send-email-aroben@apple.com","subject":"Re: [PATCH 9/9] git-svn: Make fetch ~1.7x faster","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-10-24T06:34:01Z","receivedAt":"2007-10-24T06:34:01Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Adam Roben <aroben@apple.com> wrote:\n> We were spending a lot of time forking/execing git-cat-file and\n> git-hash-object. We now use command_bidi_pipe to keep one instance of each\n> running and feed it input on stdin.\n\nNice job!  I just got access to a very fast SVN repository for a project\nI'm working on (not working on git-svn itself, unfortunately).\n\nA few comments and small nitpicks below:\n\n> Signed-off-by: Adam Roben <aroben@apple.com>\n> ---\n>  git-svn.perl |   94 ++++++++++++++++++++++++++++++++++++++++++++-------------\n>  1 files changed, 72 insertions(+), 22 deletions(-)\n\n> +package Git::Commands;\n\nCan this be a separate file, or a part of Git.pm?  I'm sure other\nscripts can eventually use this and I've been meaning to split\ngit-svn.perl into separate files so it's easier to follow.\n\n> +use vars qw/$_cat_blob_pid $_cat_blob_in $_cat_blob_out $_cat_blob_ctx $_cat_blob_separator\n> +\t    $_hash_object_pid $_hash_object_in $_hash_object_out $_hash_object_ctx/;\n\nI have trouble following long lines, and most of the git code also wraps\nat 80-columns.  Dead-tree publishers got this concept right a long\ntime ago :)\n\n> +use strict;\n> +use warnings;\n> +use File::Temp qw/tempfile/;\n> +use Git qw/command_bidi_pipe command_close_bidi_pipe/;\n> +\n> +sub _open_cat_blob_if_needed {\n> +\treturn if defined($_cat_blob_pid);\n> +\t$_cat_blob_separator = \"--------------GITCATFILESEPARATOR-----------\";\n\nBrian brought this up already, but yes, having pre-defined separators\ninstead of explicitly-specified sizes makes it all too easy for a\nmalicious user to commit code that will break things for git-svn users.\n\n> +sub hash_object {\n> +\tmy (undef, $fh) = @_;\n> +\n> +\tmy ($tmp_fh, $tmp_filename) = tempfile(UNLINK => 1);\n> +\twhile (my $line = <$fh>) {\n> +\t\tprint $tmp_fh $line;\n> +\t}\n> +\tclose($tmp_fh);\n\nRelated to the above.  It's better to sysread()/syswrite() or\nread()/print() in a loop with a predefined buffer size rather than to\nuse a readline() since you could be dealing with files with very long\nlines or binaries with no newline characters in them at all.\n\n> +\t_open_hash_object_if_needed();\n> +\tprint $_hash_object_out $tmp_filename . \"\\n\";\n\nMinor, but\n\n\tprint $_hash_object_out $tmp_filename, \"\\n\";\n\navoids creating a new string.\n\n-- \nEric Wong\n"},{"id":"57064","messageId":"471EEAC5.8070804@apple.com","threadId":"10438","inReplyTo":"20071024063401.GB10916@soma","subject":"Re: [PATCH 9/9] git-svn: Make fetch ~1.7x faster","fromName":"Adam Roben","fromEmail":"aroben@apple.com","sentAt":"2007-10-24T06:48:37Z","receivedAt":"2007-10-24T06:48:37Z","isPatch":true,"sender":{"key":"aroben@apple.com","avatar":"https://gravatar.com/avatar/9d3697e1de53890adf241331f4b970bdd2b18962b2ff0b8028ebb00e085807f8?d=mp&s=160"},"body":"Eric Wong wrote:\n> Adam Roben <aroben@apple.com> wrote:\n>   \n>> +package Git::Commands;\n>>     \n>\n> Can this be a separate file, or a part of Git.pm?  I'm sure other\n> scripts can eventually use this and I've been meaning to split\n> git-svn.perl into separate files so it's easier to follow.\n>   \n\nI had considered doing one of the above, but decided that splitting it \nout could be done if/when it was deemed useful for another script. But \nI'll split it out since you think it's a good idea.\n\n>> +use vars qw/$_cat_blob_pid $_cat_blob_in $_cat_blob_out $_cat_blob_ctx $_cat_blob_separator\n>> +\t    $_hash_object_pid $_hash_object_in $_hash_object_out $_hash_object_ctx/;\n>>     \n>\n> I have trouble following long lines, and most of the git code also wraps\n> at 80-columns.  Dead-tree publishers got this concept right a long\n> time ago :)\n>   \n\nWill fix.\n\n>> +use strict;\n>> +use warnings;\n>> +use File::Temp qw/tempfile/;\n>> +use Git qw/command_bidi_pipe command_close_bidi_pipe/;\n>> +\n>> +sub _open_cat_blob_if_needed {\n>> +\treturn if defined($_cat_blob_pid);\n>> +\t$_cat_blob_separator = \"--------------GITCATFILESEPARATOR-----------\";\n>>     \n>\n> Brian brought this up already, but yes, having pre-defined separators\n> instead of explicitly-specified sizes makes it all too easy for a\n> malicious user to commit code that will break things for git-svn users.\n>   \n\nYup, will fix this. :-)\n\n>> +sub hash_object {\n>> +\tmy (undef, $fh) = @_;\n>> +\n>> +\tmy ($tmp_fh, $tmp_filename) = tempfile(UNLINK => 1);\n>> +\twhile (my $line = <$fh>) {\n>> +\t\tprint $tmp_fh $line;\n>> +\t}\n>> +\tclose($tmp_fh);\n>>     \n>\n> Related to the above.  It's better to sysread()/syswrite() or\n> read()/print() in a loop with a predefined buffer size rather than to\n> use a readline() since you could be dealing with files with very long\n> lines or binaries with no newline characters in them at all.\n>   \n\nHm, OK. I'll look for similar code in git-svn and follow that.\n\n>> +\t_open_hash_object_if_needed();\n>> +\tprint $_hash_object_out $tmp_filename . \"\\n\";\n>>     \n>\n> Minor, but\n>\n> \tprint $_hash_object_out $tmp_filename, \"\\n\";\n>\n> avoids creating a new string.\n>   \n\nGood idea.\n\nThanks for the feedback! I'll send out some new patches sometime soon.\n\n-Adam\n"}]}