{"thread":{"id":"25926","subject":"Re: [PATCH] fast-import: add 'ls' command","startedAt":"2010-12-02T10:40:19Z","lastAt":"2011-01-28T17:13:59Z","messageCount":24,"participants":["Sverre Rabbelier","David Barr","David Michael Barr","Jonathan Nieder","Junio C Hamano","Sam Vilain","Peter Baumann"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"157107","messageId":"1291286420-13591-1-git-send-email-david.barr@cordelta.com","threadId":"25926","inReplyTo":null,"subject":"[PATCH/RFC] fast-import: add 'ls' command","fromName":"David Barr","fromEmail":"david.barr@cordelta.com","sentAt":"2010-12-02T10:40:19Z","receivedAt":"2010-12-02T10:40:19Z","isPatch":true,"sender":{"key":"david.barr@cordelta.com","avatar":"https://avatars.githubusercontent.com/u/220594?v=4"},"body":"This patch is by no means complete - I still need to consider the edge cases.\nIt does achieve the basic requirements for simplifying svn-fe.\nThe vcs-svn library currently maintains an in-memory index of all paths\nin all revisions. Introducing an `ls` command to fast-import allows this\nresponsibility to be delegated.\nMost importantly, it will allow access to the tree data on demand which\nis needed for incremental imports.\n\nThe two features that svn-fe will need are access the the current in-flight\ncommit and to previous commits by mark.\n\n fast-import.c |  127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 125 insertions(+), 2 deletions(-)\n"},{"id":"157109","messageId":"1291286420-13591-2-git-send-email-david.barr@cordelta.com","threadId":"25926","inReplyTo":"1291286420-13591-1-git-send-email-david.barr@cordelta.com","subject":"[PATCH] fast-import: add 'ls' command","fromName":"David Barr","fromEmail":"david.barr@cordelta.com","sentAt":"2010-12-02T10:40:20Z","receivedAt":"2010-12-02T10:40:20Z","isPatch":true,"sender":{"key":"david.barr@cordelta.com","avatar":"https://avatars.githubusercontent.com/u/220594?v=4"},"body":"There are two forms of the 'ls' command, one that takes a tree-ish and\none relative to the index. Allow the tree-ish variant to be used anywhere\na comment is allowed. Allow the index variant to be used within a commit\nwhere file change commands would be used.\n\nThe syntax is as such:\n\n 'ls' SP <dataref> SP path LF\n\nand\n\n 'ls' SP 'index' SP path LF\n\nBased-on-patch-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: David Barr <david.barr@cordelta.com>\n---\n fast-import.c |  127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 125 insertions(+), 2 deletions(-)\n\ndiff --git a/fast-import.c b/fast-import.c\nindex fbc70cd..854398a 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -24,10 +24,12 @@ Format of STDIN stream:\n     commit_msg\n     ('from' sp committish lf)?\n     ('merge' sp committish lf)*\n-    file_change*\n+    (file_change | ls)*\n     lf?;\n   commit_msg ::= data;\n \n+  ls ::= 'ls' sp 'index' sp path_str lf;\n+\n   file_change ::= file_clr\n     | file_del\n     | file_rnm\n@@ -132,7 +134,7 @@ Format of STDIN stream:\n   ts    ::= # time since the epoch in seconds, ascii base10 notation;\n   tz    ::= # GIT style timezone;\n \n-     # note: comments and cat requests may appear anywhere\n+     # note: comments, ls and cat requests may appear anywhere\n      # in the input, except within a data command.  Any form\n      # of the data command always escapes the related input\n      # from comment processing.\n@@ -141,7 +143,9 @@ Format of STDIN stream:\n      # must be the first character on that line (an lf\n      # preceded it).\n      #\n+\n   cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;\n+  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;\n \n   comment ::= '#' not_lf* lf;\n   not_lf  ::= # Any byte that is not ASCII newline (LF);\n@@ -369,6 +373,7 @@ static int cat_blob_fd = STDOUT_FILENO;\n \n static void parse_argv(void);\n static void parse_cat_blob(void);\n+static void parse_ls(struct branch *b);\n \n /* Signal handling */\n static volatile sig_atomic_t checkpoint_requested;\n@@ -2607,6 +2612,8 @@ static void parse_new_commit(void)\n \t\t\tnote_change_n(b, prev_fanout);\n \t\telse if (!strcmp(\"deleteall\", command_buf.buf))\n \t\t\tfile_change_deleteall(b);\n+\t\telse if (!prefixcmp(command_buf.buf, \"ls \"))\n+\t\t\tparse_ls(b);\n \t\telse {\n \t\t\tunread_command_buf = 1;\n \t\t\tbreak;\n@@ -2830,6 +2837,120 @@ static void parse_cat_blob(void)\n \tcat_blob(oe, sha1);\n }\n \n+static struct object_entry *parse_treeish_dataref(const char** r)\n+{\n+\tunsigned char sha1[20];\n+\tstruct object_entry *e;\n+\n+\tif (**r == ':') {\n+\t\tchar *x;\n+\t\te = find_mark(strtoumax(*r + 1, &x, 10));\n+\t\tif (x == *r + 1)\n+\t\t\tdie(\"Invalid mark: %s\", command_buf.buf);\n+\t\tif (!e)\n+\t\t\tdie(\"Unknown mark: %s\", command_buf.buf);\n+\t\t*r = x;\n+\t\thashcpy(sha1, e->idx.sha1);\n+\t} else {\n+\t\tif (get_sha1_hex(*r, sha1))\n+\t\t\tdie(\"Invalid SHA1: %s\", command_buf.buf);\n+\t\te = find_object(sha1);\n+\t\t*r += 40;\n+\t}\n+\n+\tfor (;;) {\n+\t\tunsigned long size;\n+\t\tvoid *buf;\n+\t\tif (!e) {\n+\t\t\tenum object_type type = sha1_object_info(sha1, NULL);\n+\t\t\tif (type < 0)\n+\t\t\t\tdie(\"object not found: %s\", sha1_to_hex(sha1));\n+\t\t\te = insert_object(sha1);\n+\t\t\te->type = type;\n+\t\t\te->pack_id = MAX_PACK_ID;\n+\t\t\te->idx.offset = 1;\n+\t\t}\n+\t\tif (e->type == OBJ_TREE)\n+\t\t\tbreak;\n+\n+\t\tif (e->type != OBJ_COMMIT && e->type != OBJ_TAG)\n+\t\t\tdie(\"Not a treeish: %s\", command_buf.buf);\n+\n+\t\tif (e->pack_id != MAX_PACK_ID) {\n+\t\t\tbuf = gfi_unpack_entry(e, &size);\n+\t\t} else {\n+\t\t\tenum object_type type;\n+\t\t\tbuf = read_sha1_file(sha1, &type, &size);\n+\t\t}\n+\t\tif (!buf)\n+\t\t\tdie(\"Can't load object %s\", sha1_to_hex(sha1));\n+\n+\t\tif (e->type == OBJ_COMMIT) {\n+\t\t\tif (size < 40 + strlen(\"tree \") ||\n+\t\t\t    get_sha1_hex(buf + strlen(\"tree \"), sha1))\n+\t\t\t\tdie(\"Invalid SHA1 in commit: %s\", command_buf.buf);\n+\t\t} else {\n+\t\t\tif (size < 40 + strlen(\"object \") ||\n+\t\t\t    get_sha1_hex(buf + strlen(\"object \"), sha1))\n+\t\t\t\tdie(\"Invalid SHA1 in tag: %s\", command_buf.buf);\n+\t\t}\n+\t\tfree(buf);\n+\t\te = find_object(sha1);\n+\t}\n+\n+\treturn e;\n+}\n+\n+static void print_ls(int mode, unsigned char *sha1, char *path)\n+{\n+\tenum object_type type;\n+\tstruct strbuf line = STRBUF_INIT;\n+\ttype = sha1_object_info(sha1, NULL);\n+\t/* mode SP type SP object_name TAB path LF */\n+\tstrbuf_addf(&line, \"%o %s %s\\t%s\\n\",\n+\t\t\tmode, typename(type), sha1_to_hex(sha1), path);\n+\tcat_blob_write(line.buf, line.len);\n+\tstrbuf_release(&line);\n+}\n+\n+static void parse_ls(struct branch *b)\n+{\n+\tconst char *p;\n+\tstruct strbuf uq = STRBUF_INIT;\n+\tstruct tree_entry *root = NULL;\n+\tstruct tree_entry leaf = {0};\n+\n+\t/* ls SP <treeish> SP <path> */\n+\tp = command_buf.buf + strlen(\"ls \");\n+\tif(!prefixcmp(p, \"index\")) {\n+\t\tp += strlen(\"index\");\n+\t\tif (!b)\n+\t\t\tdie(\"Not in a commit: %s\", command_buf.buf);\n+\t\troot = &b->branch_tree;\n+\t} else {\n+\t\tstruct object_entry *e = parse_treeish_dataref(&p);\n+\t\troot = new_tree_entry();\n+\t\thashcpy(root->versions[1].sha1, e->idx.sha1);\n+\t\tload_tree(root);\n+\t}\n+\tif (*p++ != ' ')\n+\t\tdie(\"Missing space after SHA1: %s\", command_buf.buf);\n+\tif (unquote_c_style(&uq, p, &p))\n+\t\tdie(\"Invalid path: %s\", command_buf.buf);\n+\tif (*p)\n+\t\tdie(\"Garbage after path: %s\", command_buf.buf);\n+\ttree_content_get(root, uq.buf, &leaf);\n+\tif (!leaf.versions[1].mode)\n+\t\tdie(\"Path %s not in branch\", uq.buf);\n+\t/* Allow new trees to be listed. */\n+\tif (S_ISDIR(leaf.versions[1].mode))\n+\t\tstore_tree(&leaf);\n+\tprint_ls(leaf.versions[1].mode, leaf.versions[1].sha1, uq.buf);\n+\tstrbuf_release(&uq);\n+\tif (!b || root != &b->branch_tree)\n+\t\trelease_tree_entry(root);\n+}\n+\n static void checkpoint(void)\n {\n \tcheckpoint_requested = 0;\n@@ -3128,6 +3249,8 @@ int main(int argc, const char **argv)\n \twhile (read_next_command() != EOF) {\n \t\tif (!strcmp(\"blob\", command_buf.buf))\n \t\t\tparse_new_blob();\n+\t\telse if (!prefixcmp(command_buf.buf, \"ls \"))\n+\t\t\tparse_ls(NULL);\n \t\telse if (!prefixcmp(command_buf.buf, \"commit \"))\n \t\t\tparse_new_commit();\n \t\telse if (!prefixcmp(command_buf.buf, \"tag \"))\n-- \n1.7.3\n"},{"id":"157106","messageId":"AANLkTi=HuPK+q_+hdkg4yK45UcoOA5PAGi4OmsojCsFa@mail.gmail.com","threadId":"25926","inReplyTo":"1291286420-13591-2-git-send-email-david.barr@cordelta.com","subject":"Re: [PATCH] fast-import: add 'ls' command","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2010-12-02T12:37:24Z","receivedAt":"2010-12-02T12:37:24Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Thu, Dec 2, 2010 at 11:40, David Barr <david.barr@cordelta.com> wrote:\n> There are two forms of the 'ls' command, one that takes a tree-ish and\n> one relative to the index. Allow the tree-ish variant to be used anywhere\n> a comment is allowed. Allow the index variant to be used within a commit\n> where file change commands would be used.\n\nThe commit message doesn't explain why the index variant isn't allowed\nanywhere a comment is allowed. I assume that's because there's a\nhalf-constructed index if you're in the middle of a modify operation\nor such?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"157111","messageId":"2F775519-BF60-4885-BAC1-1A09F7E0D9B2@cordelta.com","threadId":"25926","inReplyTo":"AANLkTi=HuPK+q_+hdkg4yK45UcoOA5PAGi4OmsojCsFa@mail.gmail.com","subject":"Re: [PATCH] fast-import: add 'ls' command","fromName":"David Michael Barr","fromEmail":"david.barr@cordelta.com","sentAt":"2010-12-02T12:57:18Z","receivedAt":"2010-12-02T12:57:18Z","isPatch":true,"sender":{"key":"david.barr@cordelta.com","avatar":"https://avatars.githubusercontent.com/u/220594?v=4"},"body":"Hi Sverre,\n\n>> There are two forms of the 'ls' command, one that takes a tree-ish and\n>> one relative to the index. Allow the tree-ish variant to be used anywhere\n>> a comment is allowed. Allow the index variant to be used within a commit\n>> where file change commands would be used.\n> \n> The commit message doesn't explain why the index variant isn't allowed\n> anywhere a comment is allowed. I assume that's because there's a\n> half-constructed index if you're in the middle of a modify operation\n> or such?\n\nThe index variant is allowed between file change commands,\nat which the index should be consistent albeit deferred for hashing.\nI believe this is the only place where there's naturally an implicit\nactive tree.\n\nSimply, \"relative to the index\" only makes sense when describing\na commit.\n\nUnfortunately, the list server dropped my summary email :(\nThe patch was supposed to be marked as a RFC and I gave an outline\nof the intended use.\n\nThe key features I need for svn-fe are access to the index for the\ncurrent commit and access to the content of older marked commits.\n\nNB: This version doesn't support unquoted paths.\n\n--\nDavid Barr\n"},{"id":"157123","messageId":"20101202173722.GE3962@burratino","threadId":"25926","inReplyTo":"AANLkTi=HuPK+q_+hdkg4yK45UcoOA5PAGi4OmsojCsFa@mail.gmail.com","subject":"Re: [PATCH] fast-import: add 'ls' command","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-12-02T17:37:22Z","receivedAt":"2010-12-02T17:37:22Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Sverre Rabbelier wrote:\n> On Thu, Dec 2, 2010 at 11:40, David Barr <david.barr@cordelta.com> wrote:\n\n>> There are two forms of the 'ls' command, one that takes a tree-ish and\n>> one relative to the index. Allow the tree-ish variant to be used anywhere\n>> a comment is allowed. Allow the index variant to be used within a commit\n>> where file change commands would be used.\n>\n> The commit message doesn't explain why the index variant isn't allowed\n> anywhere a comment is allowed. I assume that's because there's a\n> half-constructed index if you're in the middle of a modify operation\n> or such?\n\nI somewhat agree.  Actually I would go further: the word \"index\" brings\nto mind .git/index and its in-core counterpart, so at the same time as\ndocumenting it better, we might look into making it more self-\nexplanatory.  Maybe a syntax like\n\n\tls current \"path/to/entry\"\n\nwould make it clearer that this is about directory entries in\nfast-import's active commit and not necessarily the usual index file?\n\nI am not sure what syntax other vcs-es use for tree-ishes.  To avoid\nname clashes (what if 'current' is the low-level name of a tree-ish?),\nan alternative might be\n\n\tls-tree :11 \"path/to/historical/entry\"\n\tls \"path/to/current/entry\"\n\nHm (just musing).\nJonathan\n"},{"id":"157145","messageId":"7vr5e0q8wm.fsf@alter.siamese.dyndns.org","threadId":"25926","inReplyTo":"20101202173722.GE3962@burratino","subject":"Re: [PATCH] fast-import: add 'ls' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-02T19:20:09Z","receivedAt":"2010-12-02T19:20:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> I somewhat agree.  Actually I would go further: the word \"index\" brings\n> to mind .git/index and its in-core counterpart, so at the same time as\n> documenting it better, we might look into making it more self-\n> explanatory.  Maybe a syntax like\n>\n> \tls current \"path/to/entry\"\n>\n> would make it clearer that this is about directory entries in\n> fast-import's active commit and not necessarily the usual index file?\n\nI think that explains the feature better.  I was wondering if the stream\nsomehow wanted to access the state the index of the repository happens to\nbe.\n\n> I am not sure what syntax other vcs-es use for tree-ishes.  To avoid\n> name clashes (what if 'current' is the low-level name of a tree-ish?),\n> an alternative might be\n>\n> \tls-tree :11 \"path/to/historical/entry\"\n> \tls \"path/to/current/entry\"\n\nIs it an option to use \"ls\" for both cases and treat one-arg and two-arg\ncases differently?\n"},{"id":"157160","messageId":"201012030951.05020.david.barr@cordelta.com","threadId":"25926","inReplyTo":"7vr5e0q8wm.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] fast-import: add 'ls' command","fromName":"David Barr","fromEmail":"david.barr@cordelta.com","sentAt":"2010-12-02T22:51:04Z","receivedAt":"2010-12-02T22:51:04Z","isPatch":true,"sender":{"key":"david.barr@cordelta.com","avatar":"https://avatars.githubusercontent.com/u/220594?v=4"},"body":"> > I somewhat agree.  Actually I would go further: the word \"index\" brings\n> > to mind .git/index and its in-core counterpart, so at the same time as\n> > documenting it better, we might look into making it more self-\n> > explanatory.  Maybe a syntax like\n> >\n> >       ls current \"path/to/entry\"\n> >\n> > would make it clearer that this is about directory entries in\n> > fast-import's active commit and not necessarily the usual index file?\n> \n> I think that explains the feature better.  I was wondering if the stream\n> somehow wanted to access the state the index of the repository happens to\n> be.\n> \n> > I am not sure what syntax other vcs-es use for tree-ishes.  To avoid\n> > name clashes (what if 'current' is the low-level name of a tree-ish?),\n> > an alternative might be\n> >\n> >       ls-tree :11 \"path/to/historical/entry\"\n> >       ls \"path/to/current/entry\"\n> \n> Is it an option to use \"ls\" for both cases and treat one-arg and two-arg\n> cases differently?\n\nI like that idea, its not hard to implement if we keep quoting mandatory.\n\n--\nDavid Barr.\n\n---\ndiff --git a/fast-import.c b/fast-import.c\nindex 854398a..0b0f2a1 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -2922,8 +2922,7 @@ static void parse_ls(struct branch *b)\n \n \t/* ls SP <treeish> SP <path> */\n \tp = command_buf.buf + strlen(\"ls \");\n-\tif(!prefixcmp(p, \"index\")) {\n-\t\tp += strlen(\"index\");\n+\tif(*p == '\"') {\n \t\tif (!b)\n \t\t\tdie(\"Not in a commit: %s\", command_buf.buf);\n \t\troot = &b->branch_tree;\n@@ -2932,9 +2931,9 @@ static void parse_ls(struct branch *b)\n \t\troot = new_tree_entry();\n \t\thashcpy(root->versions[1].sha1, e->idx.sha1);\n \t\tload_tree(root);\n+\t\tif (*p++ != ' ')\n+\t\t\tdie(\"Missing space after tree-ish: %s\", command_buf.buf);\n \t}\n-\tif (*p++ != ' ')\n-\t\tdie(\"Missing space after SHA1: %s\", command_buf.buf);\n \tif (unquote_c_style(&uq, p, &p))\n \t\tdie(\"Invalid path: %s\", command_buf.buf);\n \tif (*p)\n"},{"id":"158842","messageId":"20110103080130.GA8842@burratino","threadId":"25926","inReplyTo":"1291286420-13591-1-git-send-email-david.barr@cordelta.com","subject":"[PATCH/RFC v2 0/3] fast-import: add 'ls' command","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-03T08:01:30Z","receivedAt":"2011-01-03T08:01:30Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"David Barr wrote:\n\n> This patch is by no means complete - I still need to consider the edge cases.\n> It does achieve the basic requirements for simplifying svn-fe.\n\nIt really does do that.  About time for a reroll.\n\nPatches 1 and 2 are nearby fixes noticed while hacking at this.\nChanges in patch 3 from v1 will be mentioned in the same message as\nthe patch.\n\nThoughts, improvements, especially tests welcome.  Let's get this\nfeature ready for wide use.\n\nDavid Barr (1):\n  fast-import: add 'ls' command\n\nJonathan Nieder (2):\n  fast-import: clarify handling of cat-blob feature\n  fast-import: treat filemodify with empty tree as delete\n\n Documentation/git-fast-import.txt |   49 ++++++++++-\n fast-import.c                     |  181 +++++++++++++++++++++++++++++++++++--\n t/t9300-fast-import.sh            |  158 ++++++++++++++++++++++++++++++--\n 3 files changed, 371 insertions(+), 17 deletions(-)\n\n-- \n1.7.4.rc0\n"},{"id":"158843","messageId":"20110103082246.GB8842@burratino","threadId":"25926","inReplyTo":"20110103080130.GA8842@burratino","subject":"[PATCH 1/3] fast-import: clarify handling of cat-blob feature","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-03T08:22:46Z","receivedAt":"2011-01-03T08:22:46Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Thu Dec 9 14:45:21 2010 -0600\n\nRemove the undocumented --cat-blob command line option.  It used to be\na no-op.\n\nWhile at it, move parsing of --cat-blob-fd to parse_one_feature; this\nmakes the parse_argv loop a little easier to read and puts the code\nimplementing 'feature cat-blob' and --cat-blob-fd closer to each\nother.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nForgot to mention: these are based against v1.7.4-rc0~24 (t9300: use\nperl \"head -c\" clone in place of \"dd bs=1 count=16000\" kluge,\n2010-12-13) but I wouldn't be surprised if they apply cleanly to other\ncommits, too. ;-)\n\n fast-import.c          |    9 +++------\n t/t9300-fast-import.sh |    9 +++++++++\n 2 files changed, 12 insertions(+), 6 deletions(-)\n\ndiff --git a/fast-import.c b/fast-import.c\nindex 7857760..a5cea45 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -2977,8 +2977,10 @@ static int parse_one_feature(const char *feature, int from_stream)\n \t\toption_import_marks(feature + 13, from_stream);\n \t} else if (!prefixcmp(feature, \"export-marks=\")) {\n \t\toption_export_marks(feature + 13);\n-\t} else if (!strcmp(feature, \"cat-blob\")) {\n+\t} else if (from_stream && !strcmp(feature, \"cat-blob\")) {\n \t\t; /* Don't die - this feature is supported */\n+\t} else if (!from_stream && !prefixcmp(feature, \"cat-blob-fd=\")) {\n+\t\toption_cat_blob_fd(feature + strlen(\"cat-blob-fd=\"));\n \t} else if (!prefixcmp(feature, \"relative-marks\")) {\n \t\trelative_marks_paths = 1;\n \t} else if (!prefixcmp(feature, \"no-relative-marks\")) {\n@@ -3073,11 +3075,6 @@ static void parse_argv(void)\n \t\tif (parse_one_feature(a + 2, 0))\n \t\t\tcontinue;\n \n-\t\tif (!prefixcmp(a + 2, \"cat-blob-fd=\")) {\n-\t\t\toption_cat_blob_fd(a + 2 + strlen(\"cat-blob-fd=\"));\n-\t\t\tcontinue;\n-\t\t}\n-\n \t\tdie(\"unknown option %s\", a);\n \t}\n \tif (i != global_argc)\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 222d105..53aad51 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -1769,10 +1769,19 @@ test_expect_success 'R: feature cat-blob supported' '\n \tgit fast-import\n '\n \n+test_expect_success 'R: no command line option for cat-blob feature' '\n+\ttest_must_fail git fast-import --cat-blob <empty\n+'\n+\n test_expect_success 'R: cat-blob-fd must be a nonnegative integer' '\n \ttest_must_fail git fast-import --cat-blob-fd=-1 </dev/null\n '\n \n+test_expect_success 'R: cat-blob-fd cannot be specified in stream' '\n+\techo \"feature cat-blob-fd=1\" |\n+\ttest_must_fail git fast-import\n+'\n+\n test_expect_success 'R: print old blob' '\n \tblob=$(echo \"yes it can\" | git hash-object -w --stdin) &&\n \tcat >expect <<-EOF &&\n-- \n1.7.4.rc0.580.g89dc.dirty\n"},{"id":"158844","messageId":"20110103082458.GC8842@burratino","threadId":"25926","inReplyTo":"20110103080130.GA8842@burratino","subject":"[PATCH 2/3] fast-import: treat filemodify with empty tree as delete","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-03T08:24:58Z","receivedAt":"2011-01-03T08:24:58Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Sat, 11 Dec 2010 16:42:28 -0600\n\nTraditionally, git trees do not contain entries for empty\nsubdirectories.  Generally speaking, subtrees are not created or\ndestroyed explicitly; instead, they automatically appear when needed\nto hold regular files, symlinks, and submodules.\n\nv1.7.3-rc0~75^2 (Teach fast-import to import subtrees named by tree\nid, 2010-06-30) changed that, by allowing an empty subtree to be\nincluded in a fast-import stream explicitly:\n\n\tM 040000 4b825dc642cb6eb9a060e54bf8d69288fbee4904 subdir\n\nThat was unintentional.  Better and more closely analogous to \"git\nread-tree --prefix\" to treat such an input line as a request to delete\n(\"to empty\") subdir.\n\nNoticed-by: David Barr <david.barr@cordelta.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nIf this seems like a good idea it might be a candidate for v1.7.4.x.\nPerhaps fsck.c should learn a \"no empty trees\" rule, too.\n\n fast-import.c          |   10 ++++++++\n t/t9300-fast-import.sh |   58 +++++++++++++++++++++++++++++++++++++++++------\n 2 files changed, 60 insertions(+), 8 deletions(-)\n\ndiff --git a/fast-import.c b/fast-import.c\nindex a5cea45..385d12d 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -2231,6 +2231,16 @@ static void file_change_m(struct branch *b)\n \t\tp = uq.buf;\n \t}\n \n+\t/*\n+\t * Git does not track empty, non-toplevel directories.\n+\t */\n+\tif (S_ISDIR(mode) &&\n+\t    !memcmp(sha1, (const unsigned char *) EMPTY_TREE_SHA1_BIN, 20) &&\n+\t    *p) {\n+\t\ttree_content_remove(&b->branch_tree, p, NULL);\n+\t\treturn;\n+\t}\n+\n \tif (S_ISGITLINK(mode)) {\n \t\tif (inline_data)\n \t\t\tdie(\"Git links cannot be specified 'inline': %s\",\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 53aad51..b9aa3f0 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -42,6 +42,14 @@ echo \"$@\"'\n \n >empty\n \n+test_expect_success 'setup: have pipes?' '\n+\trm -f frob &&\n+\tif mkfifo frob\n+\tthen\n+\t\ttest_set_prereq PIPE\n+\tfi\n+'\n+\n ###\n ### series A\n ###\n@@ -899,6 +907,48 @@ test_expect_success \\\n \t compare_diff_raw expect actual'\n \n test_expect_success \\\n+\t'N: delete directory by copying' \\\n+\t'cat >expect <<-\\EOF &&\n+\tOBJID\n+\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n+\tOBJID\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n+\tEOF\n+\t empty_tree=$(git mktree </dev/null) &&\n+\t cat >input <<-INPUT_END &&\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcollect data to be deleted\n+\tCOMMIT\n+\n+\tdeleteall\n+\tM 100644 inline foo/bar/baz\n+\tdata <<DATA_END\n+\thello\n+\tDATA_END\n+\tC \"foo/bar/baz\" \"foo/bar/qux\"\n+\tC \"foo/bar/baz\" \"foo/bar/quux/1\"\n+\tC \"foo/bar/baz\" \"foo/bar/quuux\"\n+\tM 040000 $empty_tree foo/bar/quux\n+\tM 040000 $empty_tree foo/bar/quuux\n+\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tdelete subdirectory\n+\tCOMMIT\n+\n+\tM 040000 $empty_tree foo/bar/qux\n+\tINPUT_END\n+\t git fast-import <input &&\n+\t git rev-list N-delete |\n+\t\tgit diff-tree -r --stdin --root --always |\n+\t\tsed -e \"s/$_x40/OBJID/g\" >actual &&\n+\t test_cmp expect actual'\n+\n+test_expect_success \\\n \t'N: copy root directory by tree hash' \\\n \t'cat >expect <<-\\EOF &&\n \t:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D\tfile3/newf\n@@ -1898,14 +1948,6 @@ test_expect_success 'R: print two blobs to stdout' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'setup: have pipes?' '\n-\trm -f frob &&\n-\tif mkfifo frob\n-\tthen\n-\t\ttest_set_prereq PIPE\n-\tfi\n-'\n-\n test_expect_success PIPE 'R: copy using cat-file' '\n \texpect_id=$(git hash-object big) &&\n \texpect_len=$(wc -c <big) &&\n-- \n1.7.4.rc0.580.g89dc.dirty\n"},{"id":"158845","messageId":"20110103083727.GD8842@burratino","threadId":"25926","inReplyTo":"20110103080130.GA8842@burratino","subject":"[PATCH 3/3] fast-import: add 'ls' command","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-03T08:37:27Z","receivedAt":"2011-01-03T08:37:27Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"From: David Barr <david.barr@cordelta.com>\nDate: Thu, 2 Dec 2010 21:40:20 +1100\n\nThe vcs-svn library currently maintains an in-core index of all paths\nin all revisions. Introducing an `ls` command to fast-import would\nallow this responsibility to be delegated; and reading this\ninformation from the target repository instead of an in-core data\nstructure would result in support for resuming an import partway\nthrough (i.e., incremental imports) for free.\n\nThere are two forms of the 'ls' command: the two-argument form prints\nthe entry at <path> for the tree underlying the tree, commit, or tag\nnamed by <dataref>:\n\n\t'ls' SP <dataref> SP <path> LF\n\nThe one-argument form prints the entry at <path> in fast-import's\nactive commit.\n\n\t'ls' SP <path> LF\n\nOutput uses ls-tree format.\n\nDirty hack: missing paths are assumed to represent the empty\nsubtree and are printed as\n\n 040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\tpath/to/nowhere\n\nto avoid confusing frontends that inserted such a path before.  But\nfrontends should also be prepared to accept\n\n missing path/to/nowhere\n\nfrom backends that (unlike git) distinguish between empty subtrees and\nnonentities.\n\nSigned-off-by: David Barr <david.barr@cordelta.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThe empty tree handling is an evil hack.  One of the tests illustrates\nthe kind of operation this is meant to support.  It would be easy to\nconvince me that some other evil hack is better.\n\nThis doesn't have tests for the basic functionality.  Maybe there\nshould be a new t9302-fast-import-bidi.sh so there is less to read to\nget started?\n\nNo new \"feature\" for this.  Frontends can easily make a feature test\nfor themselves if they need it. ;-)  And I still have plans for\n\"feature command ls\" et al, as part of a series including Tomas's\nsimplified command dispatch.\n\nOnly compile tested.  (Something similar to this is very well tested\nbut that is not enough to prevent accidents.)\n\nChanges from v1:\n - new documentation and demo (tests)\n - refactored peel-to-tree routines\n - mode is always 6 digits\n - path output uses quoting (especially important for filenames\n   with \\n [though that wouldn't come up in the svn-fe case])\n - persistent buffers to avoid allocation overhead\n - the empty tree hackery\n - mode is based on type, not based on extracting the object itself\n - path after <dataref> does not have to be quoted\n - no-<dataref> form is 'ls \"<path>\"' instead of 'ls index \"<path>\"'\n\nThanks for the original patch and a lot of help improving it go to\nDavid.\n\n'night,\nJonathan\n\n Documentation/git-fast-import.txt |   49 +++++++++++-\n fast-import.c                     |  162 ++++++++++++++++++++++++++++++++++++-\n t/t9300-fast-import.sh            |   91 +++++++++++++++++++++\n 3 files changed, 299 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt\nindex f56dfca..3957f70 100644\n--- a/Documentation/git-fast-import.txt\n+++ b/Documentation/git-fast-import.txt\n@@ -192,7 +192,8 @@ especially when a higher level language such as Perl, Python or\n Ruby is being used.\n \n fast-import is very strict about its input.  Where we say SP below we mean\n-*exactly* one space.  Likewise LF means one (and only one) linefeed.\n+*exactly* one space.  Likewise LF means one (and only one) linefeed\n+and HT one (and only one) horizontal tab.\n Supplying additional whitespace characters will cause unexpected\n results, such as branch names or file names with leading or trailing\n spaces in their name, or early termination of fast-import when it encounters\n@@ -330,6 +331,11 @@ and control the current import process.  More detailed discussion\n \tformat to the file descriptor set with `--cat-blob-fd` or\n \t`stdout` if unspecified.\n \n+`ls`::\n+\tCauses fast-import to print a directory entry in 'ls-tree'\n+\tformat to the file descriptor set with `--cat-blob-fd` or\n+\t`stdout` if unspecified.\n+\n `feature`::\n \tRequire that fast-import supports the specified feature, or\n \tabort if it does not.\n@@ -916,6 +922,47 @@ This command can be used anywhere in the stream that comments are\n accepted.  In particular, the `cat-blob` command can be used in the\n middle of a commit but not in the middle of a `data` command.\n \n+`ls`\n+~~~~\n+Prints a directory entry to a file descriptor previously arranged with\n+the `--cat-blob-fd` argument.  In the current implementation, if that\n+entry represents a subdirectory in the current commit, it will be\n+stored in the object database, but it is not advisable to rely on this\n+detail since it maybe change.\n+\n+....\n+\t'ls' (SP <dataref>)? SP <path> LF\n+....\n+\n+The `<dataref>` can be either a mark reference (`:<idnum>`) or a full\n+40-byte SHA-1 of a Git tag, commit, or tree object, preexisting or\n+waiting to be written.  The directory entry printed is that named by\n+the path, relative to the top level of that tree.\n+\n+The `ls` command can be used anywhere in the stream that comments are\n+accepted, including the middle of a commit.\n+\n+In the middle of a `commit`, the `<dataref>` part of the command can\n+be omitted, in which case the path names a directory entry within\n+fast-import's active commit.  The path must be quoted in this case.\n+\n+Output uses the same format as `git ls-tree <tree> -- <path>`:\n+\n+====\n+\t<mode> SP ('blob' | 'tree') SP <dataref> HT <path> LF\n+====\n+\n+Since git repositories do not distinguish between missing paths and\n+empty subtrees, if a path is not found it will be reported as an\n+empty tree.  Backends for version control systems that do have a\n+notion of empty trees may write\n+\n+====\n+\tmissing SP <path> LF\n+====\n+\n+for paths that do not correspond to a blob or subtree.\n+\n `feature`\n ~~~~~~~~~\n Require that fast-import supports the specified feature, or abort if\ndiff --git a/fast-import.c b/fast-import.c\nindex 385d12d..21cb109 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -24,10 +24,12 @@ Format of STDIN stream:\n     commit_msg\n     ('from' sp committish lf)?\n     ('merge' sp committish lf)*\n-    file_change*\n+    (file_change | ls)*\n     lf?;\n   commit_msg ::= data;\n \n+  ls ::= 'ls' sp '\"' quoted(path) '\"' lf;\n+\n   file_change ::= file_clr\n     | file_del\n     | file_rnm\n@@ -132,7 +134,7 @@ Format of STDIN stream:\n   ts    ::= # time since the epoch in seconds, ascii base10 notation;\n   tz    ::= # GIT style timezone;\n \n-     # note: comments and cat requests may appear anywhere\n+     # note: comments, ls and cat requests may appear anywhere\n      # in the input, except within a data command.  Any form\n      # of the data command always escapes the related input\n      # from comment processing.\n@@ -141,7 +143,9 @@ Format of STDIN stream:\n      # must be the first character on that line (an lf\n      # preceded it).\n      #\n+\n   cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;\n+  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;\n \n   comment ::= '#' not_lf* lf;\n   not_lf  ::= # Any byte that is not ASCII newline (LF);\n@@ -373,6 +377,7 @@ static int cat_blob_fd = STDOUT_FILENO;\n \n static void parse_argv(void);\n static void parse_cat_blob(void);\n+static void parse_ls(struct branch *b);\n \n static void write_branch_report(FILE *rpt, struct branch *b)\n {\n@@ -2613,6 +2618,8 @@ static void parse_new_commit(void)\n \t\t\tnote_change_n(b, prev_fanout);\n \t\telse if (!strcmp(\"deleteall\", command_buf.buf))\n \t\t\tfile_change_deleteall(b);\n+\t\telse if (!prefixcmp(command_buf.buf, \"ls \"))\n+\t\t\tparse_ls(b);\n \t\telse {\n \t\t\tunread_command_buf = 1;\n \t\t\tbreak;\n@@ -2836,6 +2843,155 @@ static void parse_cat_blob(void)\n \tcat_blob(oe, sha1);\n }\n \n+static struct object_entry *dereference(struct object_entry *oe,\n+\t\t\t\t\tunsigned char sha1[20])\n+{\n+\tunsigned long size;\n+\tvoid *buf = NULL;\n+\tif (!oe) {\n+\t\tenum object_type type = sha1_object_info(sha1, NULL);\n+\t\tif (type < 0)\n+\t\t\tdie(\"object not found: %s\", sha1_to_hex(sha1));\n+\t\t/* cache it! */\n+\t\toe = insert_object(sha1);\n+\t\toe->type = type;\n+\t\toe->pack_id = MAX_PACK_ID;\n+\t\toe->idx.offset = 1;\n+\t}\n+\tswitch (oe->type) {\n+\tcase OBJ_TREE:\t/* easy case. */\n+\t\treturn oe;\n+\tcase OBJ_COMMIT:\n+\tcase OBJ_TAG:\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"Not a treeish: %s\", command_buf.buf);\n+\t}\n+\n+\tif (oe->pack_id != MAX_PACK_ID) {\t/* in a pack being written */\n+\t\tbuf = gfi_unpack_entry(oe, &size);\n+\t} else {\n+\t\tenum object_type unused;\n+\t\tbuf = read_sha1_file(sha1, &unused, &size);\n+\t}\n+\tif (!buf)\n+\t\tdie(\"Can't load object %s\", sha1_to_hex(sha1));\n+\n+\t/* Peel one layer. */\n+\tswitch (oe->type) {\n+\tcase OBJ_TAG:\n+\t\tif (size < 40 + strlen(\"object \") ||\n+\t\t    get_sha1_hex(buf + strlen(\"object \"), sha1))\n+\t\t\tdie(\"Invalid SHA1 in tag: %s\", command_buf.buf);\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tif (size < 40 + strlen(\"tree \") ||\n+\t\t    get_sha1_hex(buf + strlen(\"tree \"), sha1))\n+\t\t\tdie(\"Invalid SHA1 in commit: %s\", command_buf.buf);\n+\t}\n+\n+\tfree(buf);\n+\treturn find_object(sha1);\n+}\n+\n+static struct object_entry *parse_treeish_dataref(const char **p)\n+{\n+\tunsigned char sha1[20];\n+\tstruct object_entry *e;\n+\n+\tif (**p == ':') {\t/* <mark> */\n+\t\tchar *endptr;\n+\t\te = find_mark(strtoumax(*p + 1, &endptr, 10));\n+\t\tif (endptr == *p + 1)\n+\t\t\tdie(\"Invalid mark: %s\", command_buf.buf);\n+\t\tif (!e)\n+\t\t\tdie(\"Unknown mark: %s\", command_buf.buf);\n+\t\t*p = endptr;\n+\t\thashcpy(sha1, e->idx.sha1);\n+\t} else {\t/* <sha1> */\n+\t\tif (get_sha1_hex(*p, sha1))\n+\t\t\tdie(\"Invalid SHA1: %s\", command_buf.buf);\n+\t\te = find_object(sha1);\n+\t\t*p += 40;\n+\t}\n+\n+\twhile (!e || e->type != OBJ_TREE)\n+\t\te = dereference(e, sha1);\n+\treturn e;\n+}\n+\n+static void print_ls(int mode, const unsigned char *sha1, const char *path)\n+{\n+\tstatic struct strbuf line = STRBUF_INIT;\n+\n+\t/* See show_tree(). */\n+\tconst char *type =\n+\t\tS_ISGITLINK(mode) ? commit_type :\n+\t\tS_ISDIR(mode) ? tree_type :\n+\t\tblob_type;\n+\n+\t/* mode SP type SP object_name TAB path LF */\n+\tstrbuf_reset(&line);\n+\tstrbuf_addf(&line, \"%06o %s %s\\t\",\n+\t\t\tmode, type, sha1_to_hex(sha1));\n+\tquote_c_style(path, &line, NULL, 0);\n+\tstrbuf_addch(&line, '\\n');\n+\tcat_blob_write(line.buf, line.len);\n+}\n+\n+static void parse_ls(struct branch *b)\n+{\n+\tconst char *p;\n+\tstruct tree_entry *root = NULL;\n+\tstruct tree_entry leaf = {0};\n+\n+\t/* ls SP (<treeish> SP)? <path> */\n+\tp = command_buf.buf + strlen(\"ls \");\n+\tif (*p == '\"') {\n+\t\tif (!b)\n+\t\t\tdie(\"Not in a commit: %s\", command_buf.buf);\n+\t\troot = &b->branch_tree;\n+\t} else {\n+\t\tstruct object_entry *e = parse_treeish_dataref(&p);\n+\t\troot = new_tree_entry();\n+\t\thashcpy(root->versions[1].sha1, e->idx.sha1);\n+\t\tload_tree(root);\n+\t\tif (*p++ != ' ')\n+\t\t\tdie(\"Missing space after tree-ish: %s\", command_buf.buf);\n+\t}\n+\tif (*p == '\"') {\n+\t\tstatic struct strbuf uq = STRBUF_INIT;\n+\t\tconst char *endp;\n+\t\tstrbuf_reset(&uq);\n+\t\tif (unquote_c_style(&uq, p, &endp))\n+\t\t\tdie(\"Invalid path: %s\", command_buf.buf);\n+\t\tif (*endp)\n+\t\t\tdie(\"Garbage after path in: %s\", command_buf.buf);\n+\t\tp = uq.buf;\n+\t}\n+\ttree_content_get(root, p, &leaf);\n+\tif (!leaf.versions[1].mode) {\n+\t\t/*\n+\t\t * Missing path?  Must be an empty subtree!\n+\t\t *\n+\t\t * When git learns to track empty directories, we can report\n+\t\t * this by saying 'missing \"path/to/directory\"' instead.\n+\t\t */\n+\t\tprint_ls(S_IFDIR, (const unsigned char *) EMPTY_TREE_SHA1_BIN, p);\n+\t} else {\n+\t\t/*\n+\t\t * A directory in preparation would have a sha1 of zero\n+\t\t * until it is saved.  Save, for simplicity.\n+\t\t */\n+\t\tif (S_ISDIR(leaf.versions[1].mode))\n+\t\t\tstore_tree(&leaf);\n+\n+\t\tprint_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);\n+\t}\n+\tif (!b || root != &b->branch_tree)\n+\t\trelease_tree_entry(root);\n+}\n+\n static void checkpoint(void)\n {\n \tcheckpoint_requested = 0;\n@@ -3131,6 +3287,8 @@ int main(int argc, const char **argv)\n \twhile (read_next_command() != EOF) {\n \t\tif (!strcmp(\"blob\", command_buf.buf))\n \t\t\tparse_new_blob();\n+\t\telse if (!prefixcmp(command_buf.buf, \"ls \"))\n+\t\t\tparse_ls(NULL);\n \t\telse if (!prefixcmp(command_buf.buf, \"commit \"))\n \t\t\tparse_new_commit();\n \t\telse if (!prefixcmp(command_buf.buf, \"tag \"))\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex b9aa3f0..6842b1f 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -906,6 +906,97 @@ test_expect_success \\\n \t git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&\n \t compare_diff_raw expect actual'\n \n+test_expect_success PIPE 'N: read and copy directory' '\n+\tcat >expect <<-\\EOF\n+\t:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100\tfile2/newf\tfile3/newf\n+\t:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100\tfile2/oldf\tfile3/oldf\n+\tEOF\n+\tgit update-ref -d refs/heads/N4 &&\n+\trm -f backflow &&\n+\tmkfifo backflow &&\n+\t(\n+\t\texec <backflow &&\n+\t\tcat <<-EOF &&\n+\t\tcommit refs/heads/N4\n+\t\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\t\tdata <<COMMIT\n+\t\tcopy by tree hash, part 2\n+\t\tCOMMIT\n+\n+\t\tfrom refs/heads/branch^0\n+\t\tls \"file2\"\n+\t\tEOF\n+\t\tread mode type tree filename &&\n+\t\techo \"M 040000 $tree file3\"\n+\t) |\n+\tgit fast-import --cat-blob-fd=3 3>backflow &&\n+\tgit diff-tree -C --find-copies-harder -r N4^ N4 >actual &&\n+\tcompare_diff_raw expect actual\n+'\n+\n+test_expect_success PIPE 'N: read and copy \"empty\" directory' '\n+\tcat <<-\\EOF >expect &&\n+\tOBJNAME\n+\t:000000 100644 OBJNAME OBJNAME A\tgreeting\n+\tOBJNAME\n+\t:100644 000000 OBJNAME OBJNAME D\tunrelated\n+\tOBJNAME\n+\t:000000 100644 OBJNAME OBJNAME A\tunrelated\n+\tEOF\n+\tgit update-ref -d refs/heads/copy-empty &&\n+\trm -f backflow &&\n+\tmkfifo backflow &&\n+\t(\n+\t\texec <backflow &&\n+\t\tcat <<-EOF &&\n+\t\tcommit refs/heads/copy-empty\n+\t\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\t\tdata <<COMMIT\n+\t\tcopy \"empty\" (missing) directory\n+\t\tCOMMIT\n+\n+\t\tM 100644 inline src/greeting\n+\t\tdata <<BLOB\n+\t\thello\n+\t\tBLOB\n+\t\tC src/greeting dst1/non-greeting\n+\t\tC src/greeting unrelated\n+\t\t# leave behind \"empty\" src directory\n+\t\tD src/greeting\n+\t\tls \"src\"\n+\t\tEOF\n+\t\tread mode type tree filename &&\n+\t\tsed -e \"s/X\\$//\" <<-EOF\n+\t\tM $mode $tree dst1\n+\t\tM $mode $tree dst2\n+\n+\t\tcommit refs/heads/copy-empty\n+\t\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\t\tdata <<COMMIT\n+\t\tcopy empty directory to root\n+\t\tCOMMIT\n+\n+\t\tM $mode $tree X\n+\n+\t\tcommit refs/heads/copy-empty\n+\t\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\t\tdata <<COMMIT\n+\t\tadd another file\n+\t\tCOMMIT\n+\n+\t\tM 100644 inline greeting\n+\t\tdata <<BLOB\n+\t\thello\n+\t\tBLOB\n+\t\tEOF\n+\t) |\n+\tgit fast-import --cat-blob-fd=3 3>backflow &&\n+\tgit rev-list copy-empty |\n+\tgit diff-tree -r --root --stdin |\n+\tsed \"s/$_x40/OBJNAME/g\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success \\\n \t'N: delete directory by copying' \\\n \t'cat >expect <<-\\EOF &&\n-- \n1.7.4.rc0.580.g89dc.dirty\n"},{"id":"159922","messageId":"20110126213922.GA19727@burratino","threadId":"25926","inReplyTo":"20110103080130.GA8842@burratino","subject":"[RFC] fast-import: 'cat-blob' and 'ls' commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-26T21:39:22Z","receivedAt":"2011-01-26T21:39:22Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi fast importers,\n\nI would like your thoughts on a few developments in fast-import\nprotocol (thanks to David, Ram, Sverre, Tomas, and Sam for work so\nfar).  If they seem good, I'd be happy to help make patches to other\nbackends so these can be implemented widely.\n\nContents: cat-blob command, filemodify (M) with trees, ls command.\n\ncat-blob command\n----------------\n\nfast-import 1.7.4-rc0 added a new \"cat-blob\" feature.  It is meant to\nallow exporters that receiving changes in delta form to avoid having\nto remember the full text of blobs already exported or re-retrieve\nthem from the source repository.\n\nIt works like this:\n\n1. Out of band, the fast-import frontend and backend negotiate a\n   channel for the backend to send replies to the frontend.  In\n   git fast-import, this is a file descriptor, defaulting to\n   stdout.  So you can do:\n\n\tmkfifo replies &&\n\n\t$frontend <replies |\n\tgit fast-import --cat-blob-fd=3 3>replies\n\n   The intent is that stdin would typically be a socket and this file\n   descriptor would point to that.\n\n2. The frontend (optionally) declares use of this feature by putting\n\n\tfeature cat-blob\n\n   at the beginning of the stream.\n\n3. When the frontend needs a previously exported blob to use as delta\n   preimage, it uses the cat-blob command.\n\n\tcat-blob :3\n\n   The backend replies with something like\n\n\t7c8987a987ca98c blob 6\n\thello\n\n\n   More precisely, the output format is\n\n\t<dataref> SP 'blob' SP <length> LF\n\t<full text of blob> LF\n\n   The <dataref> can be any text not including whitespace.\n\n   The frontend can rely on a little buffering if it wants to print a\n   command after the \"cat-blob\", but it must read the reply in its\n   entirety if it expects the backend to act on later commands.  In\n   other words, the cat-blob command is not guaranteed to be\n   asynchronous.\n\nThis protocol is used by the svn-fe[1] tool to handle Subversion dump\nfiles in version 3 (--deltas) format and seems to work ok.\n\nDoes this look sane or does it need tweaking or more detailed\nspecification to be widely useful?  Even once git 1.7.4 is out, it\nshould be possible to make improvements using a new \"feature\" name.\n\nfilemodify (M) with trees\n-------------------------\n\nfast-import 1.7.3-rc0 introduced the ability for a filemodify (M)\ncommand to place a tree named by mark or other <dataref> at a given\npath, replacing whatever was there before.  The implementation had\nsome kinks, which fast-import 1.7.4-rc0 ironed out.\n\nWithout some way to specify marks or learn tree names out of band, it\nis not very useful.  With some way to learn tree names, it can be\nused, for example, to rewrite revision metadata while reusing the old\ntree data:\n\n\tcommit refs/heads/master\n\tmark :11\n\tcommitter A U Thor <author@example.com> Wed, 26 Jan 2011 15:14:11 -0600\n\tdata <<EOF\n\tNew change description\n\tEOF\n\tM 040000 4b825dc642cb6eb9a060e54bf8d69288fbee4904 \"\"\n\nThere is no \"feature\" name for this.  Corner case: a command to\nreplace a path with the empty tree is interpreted[2] as meaning to remove\nthat file or subtree, because git does not track empty directories.\n\nDo the semantics seem reasonable?  Should this get a corresponding\n\"feature\"?\n\nls command\n----------\n\nA patch in flight[3] introduces an \"ls\" command to read directory\nentries from the active commit or a named commit.  This allows\nprinting a blob from the active commit or copying a blob or tree from\na previous commit for use in the current one.\n\nIt works like so:\n\n1. Frontend writes\n\n\t'ls' SP <path> LF\n\nor\n\n\t'ls' SP <dataref> SP <path> LF\n\n  In the first form, the <path> _must_ be surrounded in quotes\n  and quoted C-style.  In the second form, the <dataref> can refer\n  to a tag, commit, or tree.\n\n2. Backend replies through the cat-blob channel:\n\n\t<mode> SP <type> SP <dataref> HT <path> LF\n\n   <mode> is a 6-digit octal mode: 040000, 100644, 100755,\n   120000, or 160000 for a directory, regular file, executable file,\n   symlink, or submodule, respectively.\n\n   <type> is 'blob', 'tree', or 'commit'.\n\n   <dataref> represents the corresponding blob, tree, or commit\n   object.\n\n   <path> is the path in question.  It can be quoted C-style and\n   must be if the path starts with '\"' or contains a newline.\n\n3. Frontend reads the reply.  The frontend might use that <dataref> in\n   a later filemodify (M) and cat-blob command.\n\nProposed updates to svn-fe[1] use this heavily and work well.\n\nOne ugly corner case: although it is intended to allow \"missing\n<path>\" as a reply when the path is missing, the proposed patch\nmakes git fast-import use an empty tree to signal that case,\nto ensure that, for example,\n\n\tls \"\"\n\tM <mode> <dataref> \"\"\n\nis always a non-operation.\n\nNo \"feature\" name yet.  Even better, it's not part of git yet so\nI invite to nitpick to your heart's content.  Maybe you'd rather\nthe command be called \"ls-tree\" instead of \"ls\"?  Ask away. :)\n\nThoughts welcome, as always.\nJonathan\n\n[1] http://repo.or.cz/w/git/jrn.git/blob/refs/heads/vcs-svn-pu:/vcs-svn/svndump.c\n[2] Or rather, is not interpreted but ought to be, or else\nfast-import will make it too easy to produce invalid commits.  One of\nthe patches in series [3] fixes it.\n[3] http://thread.gmane.org/gmane.comp.version-control.git/162698/focus=164448\n"},{"id":"159928","messageId":"20110126224104.GA20388@burratino","threadId":"25926","inReplyTo":"20110103082458.GC8842@burratino","subject":"[PATCH v2] fast-import: treat filemodify with empty tree as delete","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-26T22:41:04Z","receivedAt":"2011-01-26T22:41:04Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Sat, 11 Dec 2010 16:42:28 -0600\n\nNormal git processes do not allow one to build a tree with an empty\nsubtree entry without trying hard at it.  This is in keeping with the\ngeneral UI philosophy: git tracks content, not empty directories.\n\nv1.7.3-rc0~75^2 (2010-06-30) changed that by making it easy to include\nan empty subtree in fast-import's active commit:\n\n\tM 040000 4b825dc642cb6eb9a060e54bf8d69288fbee4904 subdir\n\nIt is easy to trigger this by accident by reading an empty tree (for\nexample, the tree corresponding to an empty root commit) and trying to\nmove it to a subtree.  It is better and more closely analogous to \"git\nread-tree --prefix\" to treat such commands as a request to remove\n(\"to empty\") the subdir.\n\nNoticed-by: David Barr <david.barr@cordelta.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nChange since v1: commit message.\n\nResubmitting this fix separately from the 3-part series it came from.\nSeems to work okay. :)\n\n fast-import.c          |   10 ++++++++\n t/t9300-fast-import.sh |   58 +++++++++++++++++++++++++++++++++++++++++------\n 2 files changed, 60 insertions(+), 8 deletions(-)\n\ndiff --git a/fast-import.c b/fast-import.c\nindex 7857760..8b19d87 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -2231,6 +2231,16 @@ static void file_change_m(struct branch *b)\n \t\tp = uq.buf;\n \t}\n \n+\t/*\n+\t * Git does not track empty, non-toplevel directories.\n+\t */\n+\tif (S_ISDIR(mode) &&\n+\t    !memcmp(sha1, (const unsigned char *) EMPTY_TREE_SHA1_BIN, 20) &&\n+\t    *p) {\n+\t\ttree_content_remove(&b->branch_tree, p, NULL);\n+\t\treturn;\n+\t}\n+\n \tif (S_ISGITLINK(mode)) {\n \t\tif (inline_data)\n \t\t\tdie(\"Git links cannot be specified 'inline': %s\",\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 222d105..80ddfe0 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -42,6 +42,14 @@ echo \"$@\"'\n \n >empty\n \n+test_expect_success 'setup: have pipes?' '\n+\trm -f frob &&\n+\tif mkfifo frob\n+\tthen\n+\t\ttest_set_prereq PIPE\n+\tfi\n+'\n+\n ###\n ### series A\n ###\n@@ -899,6 +907,48 @@ test_expect_success \\\n \t compare_diff_raw expect actual'\n \n+test_expect_success \\\n+\t'N: delete directory by copying' \\\n+\t'cat >expect <<-\\EOF &&\n+\tOBJID\n+\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n+\tOBJID\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n+\tEOF\n+\t empty_tree=$(git mktree </dev/null) &&\n+\t cat >input <<-INPUT_END &&\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcollect data to be deleted\n+\tCOMMIT\n+\n+\tdeleteall\n+\tM 100644 inline foo/bar/baz\n+\tdata <<DATA_END\n+\thello\n+\tDATA_END\n+\tC \"foo/bar/baz\" \"foo/bar/qux\"\n+\tC \"foo/bar/baz\" \"foo/bar/quux/1\"\n+\tC \"foo/bar/baz\" \"foo/bar/quuux\"\n+\tM 040000 $empty_tree foo/bar/quux\n+\tM 040000 $empty_tree foo/bar/quuux\n+\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tdelete subdirectory\n+\tCOMMIT\n+\n+\tM 040000 $empty_tree foo/bar/qux\n+\tINPUT_END\n+\t git fast-import <input &&\n+\t git rev-list N-delete |\n+\t\tgit diff-tree -r --stdin --root --always |\n+\t\tsed -e \"s/$_x40/OBJID/g\" >actual &&\n+\t test_cmp expect actual'\n+\n test_expect_success \\\n \t'N: copy root directory by tree hash' \\\n \t'cat >expect <<-\\EOF &&\n \t:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D\tfile3/newf\n@@ -1889,14 +1939,6 @@ test_expect_success 'R: print two blobs to stdout' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'setup: have pipes?' '\n-\trm -f frob &&\n-\tif mkfifo frob\n-\tthen\n-\t\ttest_set_prereq PIPE\n-\tfi\n-'\n-\n test_expect_success PIPE 'R: copy using cat-file' '\n \texpect_id=$(git hash-object big) &&\n \texpect_len=$(wc -c <big) &&\n-- \n1.7.4.rc3\n"},{"id":"159929","messageId":"AANLkTimNWLFgTk0Bueiscw-WkAX53v0Xsepn9esXOt7+@mail.gmail.com","threadId":"25926","inReplyTo":"20110126224104.GA20388@burratino","subject":"Re: [PATCH v2] fast-import: treat filemodify with empty tree as delete","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-01-26T22:45:44Z","receivedAt":"2011-01-26T22:45:44Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Wed, Jan 26, 2011 at 23:41, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Seems to work okay. :)\n\nShould it go on maint now that it's factored out, since it shipped in\n1.7.3, or just master?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"159930","messageId":"20110126230608.GA26787@burratino","threadId":"25926","inReplyTo":"AANLkTimNWLFgTk0Bueiscw-WkAX53v0Xsepn9esXOt7+@mail.gmail.com","subject":"[PATCH jn/fast-import-fix v3] fast-import: treat filemodify with empty tree as delete","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-26T23:06:08Z","receivedAt":"2011-01-26T23:06:08Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Sat, 11 Dec 2010 16:42:28 -0600\n\nNormal git processes do not allow one to build a tree with an empty\nsubtree entry without trying hard at it.  This is in keeping with the\ngeneral UI philosophy: git tracks content, not empty directories.\n\nUnfortunately, v1.7.3-rc0~75^2 (2010-06-30) changed that by making it\neasy to include an empty subtree in fast-import's active commit:\n\n\tM 040000 4b825dc642cb6eb9a060e54bf8d69288fbee4904 subdir\n\nIt is easy to trigger this by accident by reading an empty tree (for\nexample, the tree corresponding to an empty root commit) and trying to\nmove it to a subtree.  It would be better and more closely analogous\nto \"git read-tree --prefix\" to treat such commands as a request to\nremove (\"to empty\") the subdir.\n\nNoticed-by: David Barr <david.barr@cordelta.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nSverre Rabbelier wrote:\n\n> Should it go on maint now that it's factored out, since it shipped in\n> 1.7.3, or just master?\n\nHmm.  I suppose on top of b2124125 (jn/fast-import-fix).\n\nWhile applying it there I noticed that the change to t9300 includes an\nunrelated change (residue of an old rebase).  Here's a fixed version.\n\n fast-import.c          |   10 ++++++++++\n t/t9300-fast-import.sh |   42 ++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 52 insertions(+), 0 deletions(-)\n\ndiff --git a/fast-import.c b/fast-import.c\nindex d881630..9cf26f1 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -2194,6 +2194,16 @@ static void file_change_m(struct branch *b)\n \t\tp = uq.buf;\n \t}\n \n+\t/*\n+\t * Git does not track empty, non-toplevel directories.\n+\t */\n+\tif (S_ISDIR(mode) &&\n+\t    !memcmp(sha1, (const unsigned char *) EMPTY_TREE_SHA1_BIN, 20) &&\n+\t    *p) {\n+\t\ttree_content_remove(&b->branch_tree, p, NULL);\n+\t\treturn;\n+\t}\n+\n \tif (S_ISGITLINK(mode)) {\n \t\tif (inline_data)\n \t\t\tdie(\"Git links cannot be specified 'inline': %s\",\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex dd90a09..ef3a347 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -875,6 +875,48 @@ test_expect_success \\\n \t compare_diff_raw expect actual'\n \n+test_expect_success \\\n+\t'N: delete directory by copying' \\\n+\t'cat >expect <<-\\EOF &&\n+\tOBJID\n+\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n+\tOBJID\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n+\tEOF\n+\t empty_tree=$(git mktree </dev/null) &&\n+\t cat >input <<-INPUT_END &&\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcollect data to be deleted\n+\tCOMMIT\n+\n+\tdeleteall\n+\tM 100644 inline foo/bar/baz\n+\tdata <<DATA_END\n+\thello\n+\tDATA_END\n+\tC \"foo/bar/baz\" \"foo/bar/qux\"\n+\tC \"foo/bar/baz\" \"foo/bar/quux/1\"\n+\tC \"foo/bar/baz\" \"foo/bar/quuux\"\n+\tM 040000 $empty_tree foo/bar/quux\n+\tM 040000 $empty_tree foo/bar/quuux\n+\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tdelete subdirectory\n+\tCOMMIT\n+\n+\tM 040000 $empty_tree foo/bar/qux\n+\tINPUT_END\n+\t git fast-import <input &&\n+\t git rev-list N-delete |\n+\t\tgit diff-tree -r --stdin --root --always |\n+\t\tsed -e \"s/$_x40/OBJID/g\" >actual &&\n+\t test_cmp expect actual'\n+\n test_expect_success \\\n \t'N: copy root directory by tree hash' \\\n \t'cat >expect <<-\\EOF &&\n \t:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D\tfile3/newf\n-- \n1.7.4.rc3\n"},{"id":"159931","messageId":"4D40B255.4020403@vilain.net","threadId":"25926","inReplyTo":"20110126213922.GA19727@burratino","subject":"Re: [RFC] fast-import: 'cat-blob' and 'ls' commands","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2011-01-26T23:46:29Z","receivedAt":"2011-01-26T23:46:29Z","isPatch":false,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"On 27/01/11 07:39, Jonathan Nieder wrote:\n> Hi fast importers,\n>\n> I would like your thoughts on a few developments in fast-import\n> protocol (thanks to David, Ram, Sverre, Tomas, and Sam for work so\n> far).  If they seem good, I'd be happy to help make patches to other\n> backends so these can be implemented widely.\n>\n> Contents: cat-blob command, filemodify (M) with trees, ls command.\n\nOk.  My first thoughts here are to be careful about the design: this\nfast-import protocol is fast becoming close to getting an RFC, having\nmultiple interoperable implementations available, so do consider whether\nall syntax will be cleanly extensible to eventually support full basic\nplumbing requirements.\n\nie, using the command 'cat-blob' instead of a 'cat' command with 'blob'\nas an argument as git cat-file currently works seems to be an\ninflexibility and may eventually be considered legacy.\n\nOtherwise it looks fine, seems to support all the file types etc. \nThanks for keeping the work up!\n\nCheers,\nSam\n"},{"id":"159932","messageId":"7vd3nji54o.fsf@alter.siamese.dyndns.org","threadId":"25926","inReplyTo":"20110126230608.GA26787@burratino","subject":"Re: [PATCH jn/fast-import-fix v3] fast-import: treat filemodify with empty tree as delete","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-01-27T00:04:23Z","receivedAt":"2011-01-27T00:04:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Sverre Rabbelier wrote:\n>\n>> Should it go on maint now that it's factored out, since it shipped in\n>> 1.7.3, or just master?\n>\n> Hmm.  I suppose on top of b2124125 (jn/fast-import-fix).\n\nHmm, why not on top of v1.7.3-rc0~75^2 aka 334fba6 (Teach fast-import to\nimport subtrees named by tree id, 2010-06-30) then?\n\n> While applying it there I noticed that the change to t9300 includes an\n> unrelated change (residue of an old rebase).  Here's a fixed version.\n\n> diff --git a/fast-import.c b/fast-import.c\n> index d881630..9cf26f1 100644\n> --- a/fast-import.c\n> +++ b/fast-import.c\n> @@ -2194,6 +2194,16 @@ static void file_change_m(struct branch *b)\n>  \t\tp = uq.buf;\n>  \t}\n>  \n> +\t/*\n> +\t * Git does not track empty, non-toplevel directories.\n> +\t */\n> +\tif (S_ISDIR(mode) &&\n> +\t    !memcmp(sha1, (const unsigned char *) EMPTY_TREE_SHA1_BIN, 20) &&\n\nDo you need this cast?\n"},{"id":"159936","messageId":"20110127002607.GA30911@burratino","threadId":"25926","inReplyTo":"7vd3nji54o.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH jn/fast-import-fix v3] fast-import: treat filemodify with empty tree as delete","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-27T00:26:07Z","receivedAt":"2011-01-27T00:26:07Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n> Jonathan Nieder <jrnieder@gmail.com> writes:\n\n>> Hmm.  I suppose on top of b2124125 (jn/fast-import-fix).\n>\n> Hmm, why not on top of v1.7.3-rc0~75^2 aka 334fba6 (Teach fast-import to\n> import subtrees named by tree id, 2010-06-30) then?\n\nThat could work, too. ;-)\n\nI was too lazy to check if the test case happens to work in the\nabsence of the fixes from the fast-import-fix branch.\n\n>> +++ b/fast-import.c\n>> @@ -2194,6 +2194,16 @@ static void file_change_m(struct branch *b)\n>>  \t\tp = uq.buf;\n>>  \t}\n>>  \n>> +\t/*\n>> +\t * Git does not track empty, non-toplevel directories.\n>> +\t */\n>> +\tif (S_ISDIR(mode) &&\n>> +\t    !memcmp(sha1, (const unsigned char *) EMPTY_TREE_SHA1_BIN, 20) &&\n>\n> Do you need this cast?\n\nNo, it's not needed.\n\n(EMPTY_TREE_SHA1_BIN is a string constant, originally intended for\nuse in initializers like\n\n\tstatic const unsigned char empty_tree_sha1[20] = EMPTY_TREE_SHA1_BIN;\n\nmemcmp does not care about such considerations.)\n"},{"id":"159940","messageId":"20110127060749.GA5586@burratino","threadId":"25926","inReplyTo":"7vd3nji54o.fsf@alter.siamese.dyndns.org","subject":"[PATCH v4] fast-import: treat filemodify with empty tree as delete","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-27T06:07:49Z","receivedAt":"2011-01-27T06:07:49Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Normal git processes do not allow one to build a tree with an empty\nsubtree entry without trying hard at it.  This is in keeping with the\ngeneral UI philosophy: git tracks content, not empty directories.\n\nv1.7.3-rc0~75^2 (2010-06-30) changed that by making it easy to include\nan empty subtree in fast-import's active commit:\n\n\tM 040000 4b825dc642cb6eb9a060e54bf8d69288fbee4904 subdir\n\nOne can trigger this by reading an empty tree (for example, the tree\ncorresponding to an empty root commit) and trying to move it to a\nsubtree.  It is better and more closely analogous to 'git read-tree\n--prefix' to treat such commands as requests to remove the subtree.\n\nNoticed-by: David Barr <david.barr@cordelta.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nJunio C Hamano wrote:\n\n> Hmm, why not on top of v1.7.3-rc0~75^2 aka 334fba6 (Teach fast-import to\n> import subtrees named by tree id, 2010-06-30) then?\n\nOkay, I found time to try it.  Some other small simplifications while\nat it.\n\n fast-import.c          |    6 ++++++\n t/t9300-fast-import.sh |   42 ++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 48 insertions(+), 0 deletions(-)\n\ndiff --git a/fast-import.c b/fast-import.c\nindex ad6843a..cd9310d 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -2163,6 +2163,12 @@ static void file_change_m(struct branch *b)\n \t\tp = uq.buf;\n \t}\n \n+\t/* Git does not track empty, non-toplevel directories. */\n+\tif (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {\n+\t\ttree_content_remove(&b->branch_tree, p, NULL);\n+\t\treturn;\n+\t}\n+\n \tif (S_ISGITLINK(mode)) {\n \t\tif (inline_data)\n \t\t\tdie(\"Git links cannot be specified 'inline': %s\",\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 50d5913..8487734 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -818,6 +818,48 @@ test_expect_success \\\n \t compare_diff_raw expect actual'\n \n+test_expect_success \\\n+\t'N: delete directory by copying' \\\n+\t'cat >expect <<-\\EOF &&\n+\tOBJID\n+\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n+\tOBJID\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n+\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n+\tEOF\n+\t empty_tree=$(git mktree </dev/null) &&\n+\t cat >input <<-INPUT_END &&\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcollect data to be deleted\n+\tCOMMIT\n+\n+\tdeleteall\n+\tM 100644 inline foo/bar/baz\n+\tdata <<DATA_END\n+\thello\n+\tDATA_END\n+\tC \"foo/bar/baz\" \"foo/bar/qux\"\n+\tC \"foo/bar/baz\" \"foo/bar/quux/1\"\n+\tC \"foo/bar/baz\" \"foo/bar/quuux\"\n+\tM 040000 $empty_tree foo/bar/quux\n+\tM 040000 $empty_tree foo/bar/quuux\n+\n+\tcommit refs/heads/N-delete\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tdelete subdirectory\n+\tCOMMIT\n+\n+\tM 040000 $empty_tree foo/bar/qux\n+\tINPUT_END\n+\t git fast-import <input &&\n+\t git rev-list N-delete |\n+\t\tgit diff-tree -r --stdin --root --always |\n+\t\tsed -e \"s/$_x40/OBJID/g\" >actual &&\n+\t test_cmp expect actual'\n+\n test_expect_success \\\n \t'N: modify copied tree' \\\n \t'cat >expect <<-\\EOF &&\n \t:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100\tnewdir/interesting\tfile3/file5\n-- \n1.7.4.rc3\n"},{"id":"159953","messageId":"20110127193353.GA19378@m62s10.vlinux.de","threadId":"25926","inReplyTo":"20110127060749.GA5586@burratino","subject":"Re: [PATCH v4] fast-import: treat filemodify with empty tree as delete","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2011-01-27T19:33:53Z","receivedAt":"2011-01-27T19:33:53Z","isPatch":true,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Thu, Jan 27, 2011 at 12:07:49AM -0600, Jonathan Nieder wrote:\n> diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\n> index 50d5913..8487734 100755\n> --- a/t/t9300-fast-import.sh\n> +++ b/t/t9300-fast-import.sh\n> @@ -818,6 +818,48 @@ test_expect_success \\\n>  \t compare_diff_raw expect actual'\n>  \n> +test_expect_success \\\n> +\t'N: delete directory by copying' \\\n> +\t'cat >expect <<-\\EOF &&\n> +\tOBJID\n> +\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n> +\tOBJID\n> +\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n> +\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n> +\tEOF\n> +\t empty_tree=$(git mktree </dev/null) &&\n\n[ Feel free to ignore me ... ]\n\nJust a (stupid?) suggestion: Why not put a $EMPTY_TREE definiton in test-lib.sh\n(or any other global file sourced in the tests) so if another caller needs this\ndefinition it won't waste cpu cycles doing the calculation via mktree < /dev/null\nagain?\n"},{"id":"159955","messageId":"20110127194844.GA20894@burratino","threadId":"25926","inReplyTo":"20110127193353.GA19378@m62s10.vlinux.de","subject":"Re: [PATCH v4] fast-import: treat filemodify with empty tree as delete","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-27T19:48:45Z","receivedAt":"2011-01-27T19:48:45Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Peter Baumann wrote:\n> On Thu, Jan 27, 2011 at 12:07:49AM -0600, Jonathan Nieder wrote:\n\n>> +++ b/t/t9300-fast-import.sh\n>> @@ -818,6 +818,48 @@ test_expect_success \\\n>>  \t compare_diff_raw expect actual'\n>>  \n>> +test_expect_success \\\n>> +\t'N: delete directory by copying' \\\n>> +\t'cat >expect <<-\\EOF &&\n>> +\tOBJID\n>> +\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n>> +\tOBJID\n>> +\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n>> +\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n>> +\tEOF\n>> +\t empty_tree=$(git mktree </dev/null) &&\n>\n> [ Feel free to ignore me ... ]\n>\n> Just a (stupid?) suggestion: Why not put a $EMPTY_TREE definiton in test-lib.sh\n> (or any other global file sourced in the tests) so if another caller needs this\n> definition it won't waste cpu cycles doing the calculation via mktree < /dev/null\n> again?\n\nMight be a good idea.  Note, though, that that would mean more cpu\ncycles used rather than less, unless we hardcode the object name\n(which I prefer not to do).\n\nOne possibility would be a lib-object-names.sh defining EMPTY_BLOB and\nEMPTY_TREE to be sourced by tests that need it.\n"},{"id":"159957","messageId":"20110127204649.GB19378@m62s10.vlinux.de","threadId":"25926","inReplyTo":"20110127194844.GA20894@burratino","subject":"Re: [PATCH v4] fast-import: treat filemodify with empty tree as delete","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2011-01-27T20:46:50Z","receivedAt":"2011-01-27T20:46:50Z","isPatch":true,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Thu, Jan 27, 2011 at 01:48:45PM -0600, Jonathan Nieder wrote:\n> Peter Baumann wrote:\n> > On Thu, Jan 27, 2011 at 12:07:49AM -0600, Jonathan Nieder wrote:\n> \n> >> +++ b/t/t9300-fast-import.sh\n> >> @@ -818,6 +818,48 @@ test_expect_success \\\n> >>  \t compare_diff_raw expect actual'\n> >>  \n> >> +test_expect_success \\\n> >> +\t'N: delete directory by copying' \\\n> >> +\t'cat >expect <<-\\EOF &&\n> >> +\tOBJID\n> >> +\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n> >> +\tOBJID\n> >> +\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n> >> +\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n> >> +\tEOF\n> >> +\t empty_tree=$(git mktree </dev/null) &&\n> >\n> > [ Feel free to ignore me ... ]\n> >\n> > Just a (stupid?) suggestion: Why not put a $EMPTY_TREE definiton in test-lib.sh\n> > (or any other global file sourced in the tests) so if another caller needs this\n> > definition it won't waste cpu cycles doing the calculation via mktree < /dev/null\n> > again?\n> \n> Might be a good idea.  Note, though, that that would mean more cpu\n> cycles used rather than less, unless we hardcode the object name\n> (which I prefer not to do).\n> \n\nWny not? It *is* already hardcoded in the GIT source code (see grep -a1 cache.h\noutput).\n\n> One possibility would be a lib-object-names.sh defining EMPTY_BLOB and\n> EMPTY_TREE to be sourced by tests that need it.\n\nHm. Might be a possibility, but if this file only contains 2 hardcoded variables\nI would prefer putting it into test-list.sh instead of an extra file.\n"},{"id":"159958","messageId":"20110127204839.GC19378@m62s10.vlinux.de","threadId":"25926","inReplyTo":"20110127204649.GB19378@m62s10.vlinux.de","subject":"Re: [PATCH v4] fast-import: treat filemodify with empty tree as delete","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2011-01-27T20:48:39Z","receivedAt":"2011-01-27T20:48:39Z","isPatch":true,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Thu, Jan 27, 2011 at 09:46:50PM +0100, Peter Baumann wrote:\n> On Thu, Jan 27, 2011 at 01:48:45PM -0600, Jonathan Nieder wrote:\n> > Peter Baumann wrote:\n> > > On Thu, Jan 27, 2011 at 12:07:49AM -0600, Jonathan Nieder wrote:\n> > \n> > >> +++ b/t/t9300-fast-import.sh\n> > >> @@ -818,6 +818,48 @@ test_expect_success \\\n> > >>  \t compare_diff_raw expect actual'\n> > >>  \n> > >> +test_expect_success \\\n> > >> +\t'N: delete directory by copying' \\\n> > >> +\t'cat >expect <<-\\EOF &&\n> > >> +\tOBJID\n> > >> +\t:100644 000000 OBJID OBJID D\tfoo/bar/qux\n> > >> +\tOBJID\n> > >> +\t:000000 100644 OBJID OBJID A\tfoo/bar/baz\n> > >> +\t:000000 100644 OBJID OBJID A\tfoo/bar/qux\n> > >> +\tEOF\n> > >> +\t empty_tree=$(git mktree </dev/null) &&\n> > >\n> > > [ Feel free to ignore me ... ]\n> > >\n> > > Just a (stupid?) suggestion: Why not put a $EMPTY_TREE definiton in test-lib.sh\n> > > (or any other global file sourced in the tests) so if another caller needs this\n> > > definition it won't waste cpu cycles doing the calculation via mktree < /dev/null\n> > > again?\n> > \n> > Might be a good idea.  Note, though, that that would mean more cpu\n> > cycles used rather than less, unless we hardcode the object name\n> > (which I prefer not to do).\n> > \n> \n> Wny not? It *is* already hardcoded in the GIT source code (see grep -a1   cache.h\n                                                                          ^\n                                                                        EMPTY\n\n> output).\n> \n> > One possibility would be a lib-object-names.sh defining EMPTY_BLOB and\n> > EMPTY_TREE to be sourced by tests that need it.\n> \n> Hm. Might be a possibility, but if this file only contains 2 hardcoded variables\n> I would prefer putting it into test-list.sh instead of an extra file.\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n> \n"},{"id":"159995","messageId":"20110128171359.GA26930@burratino","threadId":"25926","inReplyTo":"20110127204649.GB19378@m62s10.vlinux.de","subject":"Re: [PATCH v4] fast-import: treat filemodify with empty tree as delete","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-01-28T17:13:59Z","receivedAt":"2011-01-28T17:13:59Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Peter Baumann wrote:\n> On Thu, Jan 27, 2011 at 01:48:45PM -0600, Jonathan Nieder wrote:\n>>> On Thu, Jan 27, 2011 at 12:07:49AM -0600, Jonathan Nieder wrote:\n\n>>>> +\t empty_tree=$(git mktree </dev/null) &&\n[...]\n>>                               unless we hardcode the object name\n>> (which I prefer not to do).\n>\n> Wny not? It *is* already hardcoded in the GIT source code (see\n> grep -a1 EMPTY cache.h output).\n\nI think it is okay for the git implementation to rely on an\nimplementation detail. ;-)  Likewise, t0000 checks that the empty tree\nhas id 4b825dc6.  Meanwhile I would like to see people's scripts\nand other tests using the $(git mktree </dev/null) form, since it is\nmore self-explanatory and avoids hardcoding an implementation detail.\n\nOf course this is not an absolute thing.\n\nHope that helps,\nJonathan\n\nFurther reading: t0000-basic.h --help:\n\n\tNote that this test *deliberately* hard-codes many expected object\n\tIDs.  When object ID computation changes, like in the previous case of\n\tswapping compression and hashing order, the person who is making the\n\tmodification *should* take notice and update the test vectors here.\n\n\"Tips for Writing Tests\" in t/README:\n\n\tHowever, other tests that simply rely on basic parts of the core\n\tGIT working properly should not have that level of intimate\n\tknowledge of the core GIT internals.  If all the test scripts\n\thardcoded the object IDs like t0000-basic.sh does, that defeats\n\tthe purpose of t0000-basic.sh, which is to isolate that level of\n\tvalidation in one place.  Your test also ends up needing\n\tupdating when such a change to the internal happens, so do _not_\n\tdo it and leave the low level of validation to t0000-basic.sh.\n"}]}