{"thread":{"id":"53786","subject":"[PATCH] ref-filter: add support for %(contents:size)","startedAt":"2020-07-01T13:23:25Z","lastAt":"2020-07-01T15:20:42Z","messageCount":2,"participants":["Christian Couder","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"400893","messageId":"20200701132308.16691-1-chriscool@tuxfamily.org","threadId":"53786","inReplyTo":null,"subject":"[PATCH] ref-filter: add support for %(contents:size)","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2020-07-01T13:23:08Z","receivedAt":"2020-07-01T13:23:25Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"It's useful and efficient to be able to get the size of the\ncontents directly without having to pipe through `wc -c`.\n\nAlso the result of the following:\n\n`git for-each-ref --format='%(contents)' | wc -c`\n\nis off by one as `git for-each-ref` appends a newline character\nafter the contents, which can be seen by comparing its ouput\nwith the output from `git cat-file`.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n Documentation/git-for-each-ref.txt | 27 +++++++++++++++------------\n ref-filter.c                       |  7 ++++++-\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 23 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 6dcd39f6f6..673ace94d1 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -232,18 +232,21 @@ Fields that have name-email-date tuple as its value (`author`,\n `committer`, and `tagger`) can be suffixed with `name`, `email`,\n and `date` to extract the named component.\n \n-The complete message in a commit and tag object is `contents`.\n-Its first line is `contents:subject`, where subject is the concatenation\n-of all lines of the commit message up to the first blank line.  The next\n-line is `contents:body`, where body is all of the lines after the first\n-blank line.  The optional GPG signature is `contents:signature`.  The\n-first `N` lines of the message is obtained using `contents:lines=N`.\n-Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1]\n-are obtained as `trailers` (or by using the historical alias\n-`contents:trailers`).  Non-trailer lines from the trailer block can be omitted\n-with `trailers:only`. Whitespace-continuations can be removed from trailers so\n-that each trailer appears on a line by itself with its full content with\n-`trailers:unfold`. Both can be used together as `trailers:unfold,only`.\n+The complete message in a commit and tag object is `contents`.  Its\n+size in bytes is `contents:size`.  Its first line is\n+`contents:subject`, where subject is the concatenation of all lines of\n+the commit message up to the first blank line.  The next line is\n+`contents:body`, where body is all of the lines after the first blank\n+line.  The optional GPG signature is `contents:signature`.  The first\n+`N` lines of the message is obtained using `contents:lines=N`.\n+Additionally, the trailers as interpreted by\n+linkgit:git-interpret-trailers[1] are obtained as `trailers` (or by\n+using the historical alias `contents:trailers`).  Non-trailer lines\n+from the trailer block can be omitted with\n+`trailers:only`. Whitespace-continuations can be removed from trailers\n+so that each trailer appears on a line by itself with its full content\n+with `trailers:unfold`. Both can be used together as\n+`trailers:unfold,only`.\n \n For sorting purposes, fields with numeric values sort in numeric order\n (`objectsize`, `authordate`, `committerdate`, `creatordate`, `taggerdate`).\ndiff --git a/ref-filter.c b/ref-filter.c\nindex bf7b70299b..036a95d0d2 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -127,7 +127,8 @@ static struct used_atom {\n \t\t\tunsigned int nobracket : 1, push : 1, push_remote : 1;\n \t\t} remote_ref;\n \t\tstruct {\n-\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n+\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH,\n+\t\t\t       C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n \t\t\tstruct process_trailer_options trailer_opts;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n@@ -338,6 +339,8 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato\n \t\tatom->u.contents.option = C_BARE;\n \telse if (!strcmp(arg, \"body\"))\n \t\tatom->u.contents.option = C_BODY;\n+\telse if (!strcmp(arg, \"size\"))\n+\t\tatom->u.contents.option = C_LENGTH;\n \telse if (!strcmp(arg, \"signature\"))\n \t\tatom->u.contents.option = C_SIG;\n \telse if (!strcmp(arg, \"subject\"))\n@@ -1253,6 +1256,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \t\t\tv->s = copy_subject(subpos, sublen);\n \t\telse if (atom->u.contents.option == C_BODY_DEP)\n \t\t\tv->s = xmemdupz(bodypos, bodylen);\n+\t\telse if (atom->u.contents.option == C_LENGTH)\n+\t\t\tv->s = xstrfmt(\"%ld\", strlen(subpos));\n \t\telse if (atom->u.contents.option == C_BODY)\n \t\t\tv->s = xmemdupz(bodypos, nonsiglen);\n \t\telse if (atom->u.contents.option == C_SIG)\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex da59fadc5d..4f730acd48 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -125,6 +125,7 @@ test_atom head contents:body ''\n test_atom head contents:signature ''\n test_atom head contents 'Initial\n '\n+test_atom head contents:size '8'\n test_atom head HEAD '*'\n \n test_atom tag refname refs/tags/testtag\n@@ -170,6 +171,7 @@ test_atom tag contents:body ''\n test_atom tag contents:signature ''\n test_atom tag contents 'Tagging at 1151968727\n '\n+test_atom tag contents:size '22'\n test_atom tag HEAD ' '\n \n test_expect_success 'Check invalid atoms names are errors' '\n-- \n2.27.0.221.ga08a83db2b.dirty\n\n"},{"id":"400924","messageId":"20200701152040.GB6726@coredump.intra.peff.net","threadId":"53786","inReplyTo":"20200701132308.16691-1-chriscool@tuxfamily.org","subject":"Re: [PATCH] ref-filter: add support for %(contents:size)","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-07-01T15:20:40Z","receivedAt":"2020-07-01T15:20:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jul 01, 2020 at 03:23:08PM +0200, Christian Couder wrote:\n\n> It's useful and efficient to be able to get the size of the\n> contents directly without having to pipe through `wc -c`.\n> \n> Also the result of the following:\n> \n> `git for-each-ref --format='%(contents)' | wc -c`\n> \n> is off by one as `git for-each-ref` appends a newline character\n> after the contents, which can be seen by comparing its ouput\n> with the output from `git cat-file`.\n\nIt could also be accessed much more quickly, since we don't actually\nneed to load the object contents into memory to know the size.  cat-file\ndoes these kind of optimizations (by building on oid_object_info()), and\nits %(objectsize) will do the minimum amount of work needed.\n\nI was going to suggest that instead of adding %(contents:size), you just\nadd %(objectsize). That would match cat-file's existing option, and we\nhope to unify the formatters eventually. But it already exists (and I\nthink is even optimized courtesy of Olga's work).\n\n> -The complete message in a commit and tag object is `contents`.\n> -Its first line is `contents:subject`, where subject is the concatenation\n> -of all lines of the commit message up to the first blank line.  The next\n> -line is `contents:body`, where body is all of the lines after the first\n> -blank line.  The optional GPG signature is `contents:signature`.  The\n> -first `N` lines of the message is obtained using `contents:lines=N`.\n> -Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1]\n> -are obtained as `trailers` (or by using the historical alias\n> -`contents:trailers`).  Non-trailer lines from the trailer block can be omitted\n> -with `trailers:only`. Whitespace-continuations can be removed from trailers so\n> -that each trailer appears on a line by itself with its full content with\n> -`trailers:unfold`. Both can be used together as `trailers:unfold,only`.\n> +The complete message in a commit and tag object is `contents`.  Its\n> +size in bytes is `contents:size`.  Its first line is\n> +`contents:subject`, where subject is the concatenation of all lines of\n> +the commit message up to the first blank line.  The next line is\n> +`contents:body`, where body is all of the lines after the first blank\n> +line.  The optional GPG signature is `contents:signature`.  The first\n> +`N` lines of the message is obtained using `contents:lines=N`.\n> +Additionally, the trailers as interpreted by\n> +linkgit:git-interpret-trailers[1] are obtained as `trailers` (or by\n> +using the historical alias `contents:trailers`).  Non-trailer lines\n> +from the trailer block can be omitted with\n> +`trailers:only`. Whitespace-continuations can be removed from trailers\n> +so that each trailer appears on a line by itself with its full content\n> +with `trailers:unfold`. Both can be used together as\n> +`trailers:unfold,only`.\n\nDefinitely not a new problem, but boy is that a dense paragraph. I\nsuspect an unordered list might be a nicer way of presenting the list of\nformat specifiers.\n\n-Peff\n"}]}