From: Teng Long <hidden> Date: 2021-11-15 11:52:34
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practical.
The patch contains three commits
1. Implementation of the option.
2. Add new tests in "t3104".
3. Documentation modifications.
I'm appreciate if someone help to review the patch.
Thanks.
Teng Long (3):
ls-tree.c: support `--oid-only` option for "git-ls-tree"
t3104: add related tests for `--oid-only` option
git-ls-tree.txt: description of the 'oid-only' option
Documentation/git-ls-tree.txt | 8 +++--
builtin/ls-tree.c | 11 +++++++
t/t3104-ls-tree-oid.sh | 55 +++++++++++++++++++++++++++++++++++
3 files changed, 72 insertions(+), 2 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
--
2.33.1.9.g5fbd2fc599.dirty
From: Teng Long <hidden> Date: 2021-11-15 11:52:35
This commit supply an option names `--oid-only` to let `git ls-tree`
only print out the OID of the object. `--oid-only` and `--name-only`
are mutually exclusive in use.
Signed-off-by: Teng Long <redacted>
---
builtin/ls-tree.c | 11 +++++++++++
1 file changed, 11 insertions(+)
@@ -90,6 +91,14 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,elseif(ls_options&LS_TREE_ONLY)return0;+if((ls_options&LS_NAME_ONLY)&&(ls_options&LS_OID_ONLY))+die(_("cannot specify --oid-only and --name-only at the same time"));++if(ls_options&LS_OID_ONLY){+printf("%s\n",find_unique_abbrev(oid,abbrev));+return0;+}+if(!(ls_options&LS_NAME_ONLY)){if(ls_options&LS_SHOW_SIZE){charsize_text[24];
@@ -139,6 +148,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)LS_NAME_ONLY),OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),LS_NAME_ONLY),+OPT_BIT(0,"oid-only",&ls_options,N_("list only oids"),+LS_OID_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
@@ -0,0 +1,55 @@+#!/bin/sh++test_description='git ls-tree oids handling.'++../test-lib.sh++test_expect_success'setup''+echo111>1.txt&&+echo222>2.txt&&+mkdir-ppath0/a/b/c&&+echo333>path0/a/b/c/3.txt&&+find*.txtpath*\(-typef-o-typel\)-print|+xargsgitupdate-index--add&&+tree=$(gitwrite-tree)&&+echo$tree+'+++test_expect_success'specify with --oid-only''+gitls-tree--oid-only$tree>current&&+cat>expected<<\EOF&&+58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c+c200906efd24ec5e783bee7f23b5d7c941b0c12c+4e3849a078083863912298a25db30997cb8ca6d6+EOF+test_cmpcurrentexpected+'++test_expect_success'specify with --oid-only and -r''+gitls-tree--oid-only-r$tree>current&&+cat>expected<<\EOF&&+58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c+c200906efd24ec5e783bee7f23b5d7c941b0c12c+55bd0ac4c42e46cd751eb7405e12a35e61425550+EOF+test_cmpcurrentexpected+'++test_expect_success'specify with --oid-only and --abbrev''+gitls-tree--oid-only--abbrev=6$tree>current&&+cat>expected<<\EOF&&+58c9bd+c20090+4e3849+EOF+test_cmpcurrentexpected+'++test_expect_success'cannot specify --name-only and --oid-only as the same time''+test_must_failgitls-tree--oid-only--name-only$tree>current2>&1>/dev/null&&+echo"fatal: cannot specify --oid-only and --name-only at the same time">expected&&+test_cmpcurrentexpected+'++test_done
@@ -59,7 +60,10 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.-+ Cannot be used with `--oid-only` together.+--oid-only::+ List only OIDs of the objects, one per line. Cannot be used with+ `--name-only` or `--name-status` together. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object lines, show the shortest prefix that is at least '<n>'
This commit supply an option names `--oid-only` to let `git ls-tree`
only print out the OID of the object. `--oid-only` and `--name-only`
are mutually exclusive in use.
Signed-off-by: Teng Long <redacted>
---
builtin/ls-tree.c | 11 +++++++++++
1 file changed, 11 insertions(+)
@@ -90,6 +91,14 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,elseif(ls_options&LS_TREE_ONLY)return0;+if((ls_options&LS_NAME_ONLY)&&(ls_options&LS_OID_ONLY))+die(_("cannot specify --oid-only and --name-only at the same time"));
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practical.
The patch contains three commits
1. Implementation of the option.
2. Add new tests in "t3104".
3. Documentation modifications.
I'm appreciate if someone help to review the patch.
I've looked it over, they look correct mostly, the test code in 2/3
looks a bit too complex (using find?).
But I'd much rather see this be done with adding strbuf_expand() to
ls-tree. I.e. its docs say that it can emit:
<mode> SP <type> SP <object> TAB <file>
Or, with -l:
<mode> SP <type> SP <object> SP <object size> TAB <file>
If you use strbuf_expand() you can just define a default format of:
%(objectmode) SP %(objecttype) SP %(objectname) TAB %(path)
Then make the existing -l option a shorthand for tweaking that to:
%(objectmode) SP %(objecttype) SP %(objectsize) SP %(objectname) TAB %(path)
Then you can get what you want out of this with a simple:
git ls-tree --format="%(objectname)"
See e.g. git-cat-file for an existing use of strbuf_expand().
@@ -90,6 +91,14 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,elseif(ls_options&LS_TREE_ONLY)return0;+if((ls_options&LS_NAME_ONLY)&&(ls_options&LS_OID_ONLY))+die(_("cannot specify --oid-only and --name-only at the same time"));
This seems reasonable to me. Letting them overwrite each other (i.e.,
"last one wins") would also be fine, but we can always loosen to that
behavior later if we choose.
This is a somewhat funny place to put the check, though. It will be run
for every entry in the tree (so is a tiny bit less efficient, but also
would not trigger for an empty tree). It probably should go in
cmd_ls_tree(), perhaps here:
@@ -91,9 +91,6 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,elseif(ls_options&LS_TREE_ONLY)return0;-if((ls_options&LS_NAME_ONLY)&&(ls_options&LS_OID_ONLY))-die(_("cannot specify --oid-only and --name-only at the same time"));-if(ls_options&LS_OID_ONLY){printf("%s\n",find_unique_abbrev(oid,abbrev));return0;
@@ -175,6 +172,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)((LS_TREE_ONLY|LS_RECURSIVE)&ls_options))ls_options|=LS_SHOW_TREES;+if((ls_options&LS_NAME_ONLY)&&(ls_options&LS_OID_ONLY))+die(_("cannot specify --oid-only and --name-only at the same time"));+if(argc<1)usage_with_options(ls_tree_usage,ls_tree_options);if(get_oid(argv[0],&oid))
Ævar also mentioned using OPT_CMDMODE(), which I think would naturally
move the logic in a similar way.
-Peff
From: Jeff King <hidden> Date: 2021-11-15 20:32:20
On Mon, Nov 15, 2021 at 07:51:50PM +0800, Teng Long wrote:
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practical.
The patch contains three commits
1. Implementation of the option.
2. Add new tests in "t3104".
3. Documentation modifications.
I'm appreciate if someone help to review the patch.
This seems like a good feature to have. I think it would make sense to
squash the three patches into a single one. The documentation and test
patches do not stand on their own, which is why there was nothing useful
to say in their commit messages.
The implementation looks generally sensible (modulo the comments already
given). I was surprised that there was not an existing ls-tree script
that these would fit into. But there really isn't; t3101 covers
--name-only and other output, but is really focused on the pathnames
(though I think it would be OK to refactor it to cover output more
generally).
-Peff
On Mon, Nov 15, 2021 at 04:13:24PM +0100, Ævar Arnfjörð Bjarmason wrote:
quoted
But I'd much rather see this be done with adding strbuf_expand() to
ls-tree. I.e. its docs say that it can emit:
I had a similar thought, but that's a much bigger task. I think it would
be reasonable to add --oid-only to match the existing --name-only, etc.
If we later add a custom --format option, then it can easily be folded
in and explained as "this is an alias for --format=%(objectname)", just
like --name-only would become "this is an alias for --format=%(path)".
A quick patch to do it below, seems to work, passes all tests, but I
don't know how much I'd trust it. It's also quite an add use of
strbuf_expa(). We print to stdout directly since
write_name_quoted_relative() really wants to write to stdout, and not
give you a buffer. But I guess it makes sense in a way.
The hardcoded %7s for %(objectsize) is a bit nasty, but I don't know if
we've got anything existing that handles format specifiers with
strbuf_expand() that we could steal.
I really wouldn't trust this code much, I found it when writing it that
our tests for ls-tree are really lacking, e.g. we may not have a single
test for "-l" anywhere (or maybe I didn't look enough, I was just
running t/*ls*tree* while hacking it.
I do thin that we should consider just going with --format in either
case if we agree that this is a good direction. I.e. could just support
3-4 hardcoded formats now and die if anything else is specified.
Then we'd be future-proof with the same interface expanding later, and
wouldn't need to support options that we're only carrying because we
didn't implement the more generic format support.
(Assume my Signed-off-by, if there's any interest...)
@@ -61,9 +75,69 @@ static int show_recursive(const char *base, int baselen, const char *pathname)return0;}+staticsize_texpand_show_tree(structstrbuf*sb,+constchar*start,+void*context)+{+structexpand_ls_tree_data*data=context;+constchar*end;+constchar*p;+size_tlen;+constchar*type=blob_type;++if(sb->len){+fputs(sb->buf,stdout);+strbuf_reset(sb);+}++if(*start!='(')+die(_("bad format as of '%s'"),start);+end=strchr(start+1,')');+if(!end)+die(_("ls-tree format element '%s' does not end in ')'"),+start);+len=end-start+1;++if(skip_prefix(start,"(objectmode)",&p)){+printf("%06o",data->mode);+}elseif(skip_prefix(start,"(objecttype)",&p)){+fputs(data->type,stdout);+}elseif(skip_prefix(start,"(objectsize)",&p)){+charsize_text[24];+conststructobject_id*oid=data->oid;++if(!strcmp(type,blob_type)){+unsignedlongsize;+if(oid_object_info(the_repository,oid,&size)==OBJ_BAD)+xsnprintf(size_text,sizeof(size_text),+"BAD");+else+xsnprintf(size_text,sizeof(size_text),+"%"PRIuMAX,(uintmax_t)size);+}else{+xsnprintf(size_text,sizeof(size_text),"-");+}+printf("%7s",size_text);+}elseif(skip_prefix(start,"(objectname)",&p)){+fputs(find_unique_abbrev(data->oid,data->abbrev),stdout);+}elseif(skip_prefix(start,"(path)",&p)){+write_name_quoted_relative(data->basebuf,+chomp_prefix?ls_tree_prefix:NULL,+stdout,line_termination);++}else{+unsignedinterrlen=(unsignedlong)len;+die(_("bad ls-tree format specifiec %%%.*s"),errlen,start);+}++returnlen;+}+staticintshow_tree(conststructobject_id*oid,structstrbuf*base,constchar*pathname,unsignedmode,void*context){+structexpand_ls_tree_data*data=context;+structstrbufsb=STRBUF_INIT;intretval=0;intbaselen;constchar*type=blob_type;
@@ -161,8 +225,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)}/* -d -r should imply -t, but -d by itself should not have to. */if((LS_TREE_ONLY|LS_RECURSIVE)==-((LS_TREE_ONLY|LS_RECURSIVE)&ls_options))+((LS_TREE_ONLY|LS_RECURSIVE)&ls_options)){ls_options|=LS_SHOW_TREES;+}+if(ls_options&LS_NAME_ONLY)+ls_tree_cb_data.format=ls_tree_format_n;++if(ls_options&LS_SHOW_SIZE)+ls_tree_cb_data.format=ls_tree_format_l;if(argc<1)usage_with_options(ls_tree_usage,ls_tree_options);
@@ -185,6 +255,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)tree=parse_tree_indirect(&oid);if(!tree)die("not a tree object");+return!!read_tree(the_repository,tree,-&pathspec,show_tree,NULL);+&pathspec,show_tree,&ls_tree_cb_data);}
@@ -90,6 +91,14 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,elseif(ls_options&LS_TREE_ONLY)return0;+if((ls_options&LS_NAME_ONLY)&&(ls_options&LS_OID_ONLY))+die(_("cannot specify --oid-only and --name-only at the same time"));
This seems reasonable to me. Letting them overwrite each other (i.e.,
"last one wins") would also be fine, but we can always loosen to that
behavior later if we choose.
Oh, and whichever direction we go, it would probably make sense for
--long to be handled in the same way. I.e.:
git ls-tree --long --oid-only
does not really make sense. Though we currently just ignore --long for:
git ls-tree --long --name-only
which is arguably a bug.
-Peff
From: Jeff King <hidden> Date: 2021-11-16 01:13:24
On Mon, Nov 15, 2021 at 04:13:24PM +0100, Ævar Arnfjörð Bjarmason wrote:
But I'd much rather see this be done with adding strbuf_expand() to
ls-tree. I.e. its docs say that it can emit:
I had a similar thought, but that's a much bigger task. I think it would
be reasonable to add --oid-only to match the existing --name-only, etc.
If we later add a custom --format option, then it can easily be folded
in and explained as "this is an alias for --format=%(objectname)", just
like --name-only would become "this is an alias for --format=%(path)".
-Peff
Thank you very much for providing this input.
So I try to read this patch your mentioned and try to repeat the idea in my understanding.
First, OPT_CMDMODE() can be used for:
1. Easy for checking the combined command options, such as "mutually exclusive" conditions.
2. Die and output the error message consistently when the incompatible options are found.
3. Brings better extensibilites, no need to change a lot of if/elses.
Then, you suggest to consider about to use OPT_CMDMODE instead of the current implementations.
Did I understand your suggestion right and comprehensive?
Thank you very much for providing this input.
So I try to read this patch your mentioned and try to repeat the idea in my understanding.
First, OPT_CMDMODE() can be used for:
1. Easy for checking the combined command options, such as "mutually exclusive" conditions.
2. Die and output the error message consistently when the incompatible options are found.
3. Brings better extensibilites, no need to change a lot of if/elses.
Then, you suggest to consider about to use OPT_CMDMODE instead of the current implementations.
Did I understand your suggestion right and comprehensive?
Yes, all of that is correct.
It's a way of defining N options, --foo, --bar, --baz, where combining
any of them is an error.
We usually use it for a "command mode" (hence the name), but it can be
used when the command has flags that are mutually exclusive.
I think (but am not sure, and didn't check) that you can even use it for
--foo AND --bar that are exclusive, and --other --flags that are also
mutually exclusive (but could be combined with one of --foo or --bar),
you just need to provide another variable for it to set.
But I haven't tested that or used it like that, maybe it doesn't work
for some reason I'm forgetting...
From: Teng Long <hidden> Date: 2021-11-18 11:25:52
On Mon, 15 Nov 2021 14:16:27 -0500, Jeff King wrote:
This is a somewhat funny place to put the check, though. It will be run
for every entry in the tree (so is a tiny bit less efficient, but also
would not trigger for an empty tree). It probably should go in
cmd_ls_tree(), perhaps here:
Yes, it's better here as a fail-fast case.
According to the suggestion of the new location I think why not put the logic
further head, after the parse_options() return, like:
@@ -166,6 +166,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)argc=parse_options(argc,argv,prefix,ls_tree_options,ls_tree_usage,0);++if((ls_options&LS_NAME_ONLY)&&(ls_options&LS_OID_ONLY))+die(_("cannot specify --oid-only and --name-only at the same time"));+if(full_tree){ls_tree_prefix=prefix=NULL;chomp_prefix=0;
From: Teng Long <hidden> Date: 2021-11-19 02:58:07
A quick patch to do it below, seems to work, passes all tests, but I
don't know how much I'd trust it. It's also quite an add use of
strbuf_expa(). We print to stdout directly since
write_name_quoted_relative() really wants to write to stdout, and not
give you a buffer. But I guess it makes sense in a way.
Thanks for the patch and the inputs about "strbuf_expa()".
Then we'd be future-proof with the same interface expanding later, and
wouldn't need to support options that we're only carrying because we
didn't implement the more generic format support.
I agree but like Peff said it maybe another bigger task. I think I will
firstly solve the existing problems in next patch.
I will consider about the generic format support but not sure whether
it will continue to iterate in this patchset.
(Assume my Signed-off-by, if there's any interest...)
Of course I will.
Thank you very much for your advice and guidance again.
From: Teng Long <hidden> Date: 2021-11-19 12:10:06
This patch series supports for only outputing the "objects" (OID)
with a new option names `--oid-only`.
Changes with the first patch are :
1. Three commits are squashed to 1 commit (Peff's advice)
2. The tests issues (Đoàn Trần Công Danh's advice)
3. Use `OPT_CMDMODE()` for mutually exclusive control
(Ævar Arnfjörð Bjarmason's advice)
Some discussions are not included in Patch 2 :
1. `git ls-tree --long --name-only` and
`git ls-tree --long --oid-only` which is arguably a bug
(Peff's advice)
2. Support `--format` for `git-ls-tree`
(Ævar Arnfjörð Bjarmason's advice)
The reason why these 2 discussions not included is I'm not sure whether
I should continue on the current patchset or start a new one. And for the
second, I think current implementation is clear and simple to use, meeting
the needs of the moment. Maybe I will to support `--format` option, but
before that, I'm appreciate if there are more suggestions appear.
Thanks.
Teng Long (1):
ls-tree.c: support `--oid-only` option for "git-ls-tree"
Documentation/git-ls-tree.txt | 8 +++++--
builtin/ls-tree.c | 27 ++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 40 +++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+), 10 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
Range-diff against v1:
1: c4479178d7 < -: ---------- ls-tree.c: support `--oid-only` option for "git-ls-tree"
2: 853ebbcf88 < -: ---------- t3104: add related tests for `--oid-only` option
3: 33c68c1f11 < -: ---------- git-ls-tree.txt: description of the 'oid-only' option
-: ---------- > 1: 8b68568d6c ls-tree.c: support `--oid-only` option for "git-ls-tree"
--
2.33.1.10.g1f74a882e4
From: Teng Long <hidden> Date: 2021-11-19 12:10:08
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practical.
This commit supply an option names `--oid-only` to let `git ls-tree`
only print out the OID of the object. `--oid-only` and `--name-only`
are mutually exclusive in use.
Reviewed-by: Jeff King <redacted>
Reviewed-by: Ævar Arnfjörð Bjarmason <redacted>
Reviewed-by: Đoàn Trần Công Danh <redacted>
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 8 +++++--
builtin/ls-tree.c | 27 ++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 40 +++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+), 10 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,7 +60,10 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.-+ Cannot be used with `--oid-only` together.+--oid-only::+ List only OIDs of the objects, one per line. Cannot be used with+ `--name-only` or `--name-status` together. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object lines, show the shortest prefix that is at least '<n>'
@@ -135,10 +147,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)N_("terminate entries with NUL byte"),0),OPT_BIT('l',"long",&ls_options,N_("include object size"),LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('n',"name-only",&cmdmode,N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('s',"name-status",&cmdmode,N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('o',"oid-only",&cmdmode,N_("list only oids"),MODE_OID_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
Reviewed-by: Jeff King <redacted>
Reviewed-by: Ævar Arnfjörð Bjarmason <redacted>
Reviewed-by: Đoàn Trần Công Danh <redacted>
Please don't add the Reviewed-by headers yourself, either Junio
accumulates them, or if someone explicitly mentions that you can add it
with their name it's OK.
It doesn't just mean this person reviewed this series in some ML thread,
but "this person is 100% OK with this in its current form".
List only filenames (instead of the "long" output), one per line.
-
+ Cannot be used with `--oid-only` together.
Better: "Cannot be combined with OPT."
+--oid-only::
+ List only OIDs of the objects, one per line. Cannot be used with
+ `--name-only` or `--name-status` together.
Stray echo? Unclear why this test setup is so complex, shouldn't this just be (continued from above):
mkdir -p C &&
test_commit C/D.txt
To test nested dirs?
From: Teng Long <hidden> Date: 2021-11-22 07:47:11
On Fri, 19 Nov 2021 14:30:52 +0100, Ævar Arnfjörð Bjarmason wrote
Please don't add the Reviewed-by headers yourself, either Junio
accumulates them, or if someone explicitly mentions that you can add it
with their name it's OK.
I think I misunderstood the meanings of the header before.
Thanks for the important tips.
Better: "Cannot be combined with OPT."
Better: "Cannot be combined with OPT or OPT2."
...
Better to preserve the wrapping here, to stay within 79 columns.
Will apply.
Just use:
test_commit A &&
test_commit B
etc?
...
Stray echo? Unclear why this test setup is so complex, shouldn't this just be (continued from above):
mkdir -p C &&
test_commit C/D.txt
To test nested dirs?
Will apply.
just cut -f1 instead of awk? Also don't put "git" on the LHS of a pipe,
it might hide segfaults. Also applies to the below.
Will apply, and could you please describe the problem with more details?
(appreciate if there is an executable example)
Thank you.
From: Teng Long <hidden> Date: 2021-11-22 08:08:55
Diffs from previous patch:
1. Remove "Reviewed-by" headers in commit message.
2. Optimize option descriptions in Doc.
(Ævar Arnfjörð Bjarmason' advice)
3. Optimize and bugfix in "t3104".
(Ævar Arnfjörð Bjarmason' advice)
4. The formatting problems of line wrappers (over 79 col)
All the advices are from Ævar Arnfjörð Bjarmason and Junio C Hamano,
thank you very much.
Althought some advices are apply in this path, but some questions
remains, they are in link [1].
[1] https://public-inbox.org/git/20211122074538.87255-1-dyroneteng@gmail.com/
Teng Long (1):
ls-tree.c: support `--oid-only` option for "git-ls-tree"
Documentation/git-ls-tree.txt | 8 +++++--
builtin/ls-tree.c | 27 ++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 40 +++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+), 10 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
Range-diff against v2:
1: 8b68568d6c ! 1: 6c15b4c176 ls-tree.c: support `--oid-only` option for "git-ls-tree"
@@ Commit message
only print out the OID of the object. `--oid-only` and `--name-only`
are mutually exclusive in use.
- Reviewed-by: Jeff King [off-list ref]
- Reviewed-by: Ævar Arnfjörð Bjarmason [off-list ref]
- Reviewed-by: Đoàn Trần Công Danh [off-list ref]
Signed-off-by: Teng Long [off-list ref]
## Documentation/git-ls-tree.txt ##
--
2.33.1.10.g438dd9044d.dirty
From: Teng Long <hidden> Date: 2021-11-22 08:08:57
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practical.
This commit supply an option names `--oid-only` to let `git ls-tree`
only print out the OID of the object. `--oid-only` and `--name-only`
are mutually exclusive in use.
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 8 +++++--
builtin/ls-tree.c | 27 ++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 40 +++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+), 10 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,7 +60,10 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.-+ Cannot be used with `--oid-only` together.+--oid-only::+ List only OIDs of the objects, one per line. Cannot be used with+ `--name-only` or `--name-status` together. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object lines, show the shortest prefix that is at least '<n>'
@@ -135,10 +147,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)N_("terminate entries with NUL byte"),0),OPT_BIT('l',"long",&ls_options,N_("include object size"),LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('n',"name-only",&cmdmode,N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('s',"name-status",&cmdmode,N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('o',"oid-only",&cmdmode,N_("list only oids"),MODE_OID_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
On Fri, 19 Nov 2021 14:30:52 +0100, Ævar Arnfjörð Bjarmason wrote
quoted
just cut -f1 instead of awk? Also don't put "git" on the LHS of a pipe,
it might hide segfaults. Also applies to the below.
Will apply, and could you please describe the problem with more details?
(appreciate if there is an executable example)
Run this in a terminal:
git stawtus | cat; echo $?;
The LHS of the pipe fails, but the exit code of that command is
hidden. So we prefer:
git stawtus >out && # fails
[...]
From: Peter Baumann <hidden> Date: 2021-11-22 18:11:20
[ Sorry if you receive this mail twice, it seems like it didn't get
through the first time. ]
On Mon, Nov 22, 2021 at 9:50 AM Teng Long [off-list ref] wrote:
quoted hunk
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practical.
This commit supply an option names `--oid-only` to let `git ls-tree`
only print out the OID of the object. `--oid-only` and `--name-only`
are mutually exclusive in use.
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 8 +++++--
builtin/ls-tree.c | 27 ++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 40 +++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+), 10 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
Shouldn't the synopsis also indicate that the options are exclusive, e.g.
[--name-only | --oid-only] ?
Besides adding the new --oid-only mode, you also add one letter acronyms for
[-n | --name-only]
[-s | --name-status ]
and one letter abbreviation
[-o | --oid-only ]
which are all undocumented in the help page. If we want the short one
letter version,
they should be documented. For me, it is at least questionable why we
introduce them
and more so in a commit adding --oid-only.
quoted hunk
DESCRIPTION
@@ -59,7 +60,10 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.-+ Cannot be used with `--oid-only` together.+--oid-only::+ List only OIDs of the objects, one per line. Cannot be used with+ `--name-only` or `--name-status` together. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object lines, show the shortest prefix that is at least '<n>'
@@ -135,10 +147,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)N_("terminate entries with NUL byte"),0),OPT_BIT('l',"long",&ls_options,N_("include object size"),LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('n',"name-only",&cmdmode,N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('s',"name-status",&cmdmode,N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('o',"oid-only",&cmdmode,N_("list only oids"),MODE_OID_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
From: Đoàn Trần Công Danh <hidden> Date: 2021-11-23 00:14:26
On 2021-11-22 16:07:28+0800, Teng Long [off-list ref] wrote:
quoted hunk
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practical.
This commit supply an option names `--oid-only` to let `git ls-tree`
only print out the OID of the object. `--oid-only` and `--name-only`
are mutually exclusive in use.
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 8 +++++--
builtin/ls-tree.c | 27 ++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 40 +++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+), 10 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,7 +60,10 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.-+ Cannot be used with `--oid-only` together.+--oid-only::+ List only OIDs of the objects, one per line. Cannot be used with+ `--name-only` or `--name-status` together. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object lines, show the shortest prefix that is at least '<n>'
From: Teng Long <hidden> Date: 2021-11-23 04:58:22
Thanks for the discussions on v3 (even I send a patch with
wrong contents and the right cover). So I looked at them, and
I think I have to send a new patch first, so this includes:
1. Commit message modifications (Junio C Hamano's advice)
2. Documentation modifications (Peter Baumann's advice)
3. To use the MODE enum name instead (Đoàn Trần Công Danh's advice)
The other discussions I will reply today.
Teng Long (1):
ls-tree.c: support `--oid-only` option for "git-ls-tree"
Documentation/git-ls-tree.txt | 18 ++++++++++++---
builtin/ls-tree.c | 30 +++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 43 +++++++++++++++++++++++++++++++++++
3 files changed, 80 insertions(+), 11 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
Range-diff against v3:
1: 8b68568d6c ! 1: 63876dbeb7 ls-tree.c: support `--oid-only` option for "git-ls-tree"
@@ Commit message
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
- origin output to achieve this purpose in practical.
+ origin output to achieve this purpose in practice.
- This commit supply an option names `--oid-only` to let `git ls-tree`
- only print out the OID of the object. `--oid-only` and `--name-only`
- are mutually exclusive in use.
+ This commit teach the "--oid-only" option to tell the command to
+ only show the object name, just like "--name-only" option tells the
+ command to only show the path component, for each entry. These two
+ options are mutually exclusive.
- Reviewed-by: Jeff King [off-list ref]
- Reviewed-by: Ævar Arnfjörð Bjarmason [off-list ref]
- Reviewed-by: Đoàn Trần Công Danh [off-list ref]
Signed-off-by: Teng Long [off-list ref]
## Documentation/git-ls-tree.txt ##
-@@ Documentation/git-ls-tree.txt: SYNOPSIS
+@@ Documentation/git-ls-tree.txt: git-ls-tree - List the contents of a tree object
+ SYNOPSIS
--------
[verse]
- 'git ls-tree' [-d] [-r] [-t] [-l] [-z]
+-'git ls-tree' [-d] [-r] [-t] [-l] [-z]
- [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]
-+ [--name-only] [--name-status] [--oid-only]
++'git ls-tree' [-d] [-r] [-t] [-l] [-z] [-n] [-s] [-o]
++ [--name-only | --oid-only]
++ [--name-status | --oid-only]
+ [--full-name] [--full-tree] [--abbrev[=<n>]]
<tree-ish> [<path>...]
DESCRIPTION
@@ Documentation/git-ls-tree.txt: OPTIONS
+ \0 line termination on output and do not quote filenames.
+ See OUTPUT FORMAT below for more information.
+
++-n::
--name-only::
- --name-status::
+---name-status::
List only filenames (instead of the "long" output), one per line.
--
-+ Cannot be used with `--oid-only` together.
++ Cannot be combined with `--oid-only`.
++
++-s::
++--name-status::
++ Consistent behavior with `--name-only`.
++
++-o::
+--oid-only::
-+ List only OIDs of the objects, one per line. Cannot be used with
-+ `--name-only` or `--name-status` together.
++ List only names of the objects, one per line. Cannot be combined
++ with `--name-only` or `--name-status`.
+
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
- lines, show the shortest prefix that is at least '<n>'
## builtin/ls-tree.c ##
@@ builtin/ls-tree.c: static int line_termination = '\n';
@@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb
return 0;
- if (!(ls_options & LS_NAME_ONLY)) {
-+ if (cmdmode == 2) {
++ if (cmdmode == MODE_OID_ONLY) {
+ printf("%s\n", find_unique_abbrev(oid, abbrev));
+ return 0;
+ }
+
-+ if (cmdmode == 0) {
++ if (cmdmode == MODE_UNSPECIFIED) {
if (ls_options & LS_SHOW_SIZE) {
char size_text[24];
if (!strcmp(type, blob_type)) {
@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref
- LS_NAME_ONLY),
- OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"),
- LS_NAME_ONLY),
-+ OPT_CMDMODE('n', "name-only", &cmdmode, N_("list only filenames"), MODE_NAME_ONLY),
-+ OPT_CMDMODE('s', "name-status", &cmdmode, N_("list only filenames"), MODE_NAME_ONLY),
-+ OPT_CMDMODE('o', "oid-only", &cmdmode, N_("list only oids"), MODE_OID_ONLY),
++ OPT_CMDMODE('n', "name-only", &cmdmode,
++ N_("list only filenames"), MODE_NAME_ONLY),
++ OPT_CMDMODE('s', "name-status", &cmdmode,
++ N_("list only filenames"), MODE_NAME_ONLY),
++ OPT_CMDMODE('o', "oid-only", &cmdmode,
++ N_("list only oids"), MODE_OID_ONLY),
OPT_SET_INT(0, "full-name", &chomp_prefix,
N_("use full path names"), 0),
OPT_BOOL(0, "full-tree", &full_tree,
@@ t/t3104-ls-tree-oid.sh (new)
+. ./test-lib.sh
+
+test_expect_success 'setup' '
-+ echo 111 >1.txt &&
-+ echo 222 >2.txt &&
-+ mkdir -p path0/a/b/c &&
-+ echo 333 >path0/a/b/c/3.txt &&
++ test_commit A &&
++ test_commit B &&
++ mkdir -p C &&
++ test_commit C/D.txt &&
+ find *.txt path* \( -type f -o -type l \) -print |
+ xargs git update-index --add &&
+ tree=$(git write-tree) &&
@@ t/t3104-ls-tree-oid.sh (new)
+
+test_expect_success 'usage: --oid-only' '
+ git ls-tree --oid-only $tree >current &&
-+ git ls-tree $tree | awk "{print \$3}" >expected &&
++ git ls-tree $tree >result &&
++ cut -f1 result | cut -d " " -f3 >expected &&
+ test_cmp current expected
+'
+
+test_expect_success 'usage: --oid-only with -r' '
+ git ls-tree --oid-only -r $tree >current &&
-+ git ls-tree -r $tree | awk "{print \$3}" >expected &&
++ git ls-tree -r $tree >result &&
++ cut -f1 result | cut -d " " -f3 >expected &&
+ test_cmp current expected
+'
+
+test_expect_success 'usage: --oid-only with --abbrev' '
+ git ls-tree --oid-only --abbrev=6 $tree >current &&
-+ git ls-tree --abbrev=6 $tree | awk "{print \$3}" > expected &&
++ git ls-tree --abbrev=6 $tree >result &&
++ cut -f1 result | cut -d " " -f3 >expected &&
+ test_cmp current expected
+'
+
-+test_expect_failure 'usage: incompatible options: --name-only with --oid-only' '
-+ test_incompatible_usage git ls-tree --oid-only --name-only
++test_expect_success 'usage: incompatible options: --name-only with --oid-only' '
++ test_expect_code 129 git ls-tree --oid-only --name-only
+'
+
+test_done
--
2.33.1.10.g75523f744f.dirty
From: Teng Long <hidden> Date: 2021-11-23 04:58:27
Sometimes, we only want to get the objects from output of `ls-tree`
and commands like `sed` or `cut` is usually used to intercept the
origin output to achieve this purpose in practice.
This commit teach the "--oid-only" option to tell the command to
only show the object name, just like "--name-only" option tells the
command to only show the path component, for each entry. These two
options are mutually exclusive.
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 18 ++++++++++++---
builtin/ls-tree.c | 30 +++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 43 +++++++++++++++++++++++++++++++++++
3 files changed, 80 insertions(+), 11 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -9,8 +9,10 @@ git-ls-tree - List the contents of a tree object SYNOPSIS -------- [verse]-'git ls-tree' [-d] [-r] [-t] [-l] [-z]- [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]+'git ls-tree' [-d] [-r] [-t] [-l] [-z] [-n] [-s] [-o]+ [--name-only | --oid-only]+ [--name-status | --oid-only]+ [--full-name] [--full-tree] [--abbrev[=<n>]] <tree-ish> [<path>...] DESCRIPTION
@@ -56,9 +58,19 @@ OPTIONS \0 line termination on output and do not quote filenames. See OUTPUT FORMAT below for more information.+-n:: --name-only::---name-status:: List only filenames (instead of the "long" output), one per line.+ Cannot be combined with `--oid-only`.++-s::+--name-status::+ Consistent behavior with `--name-only`.++-o::+--oid-only::+ List only names of the objects, one per line. Cannot be combined+ with `--name-only` or `--name-status`. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object
@@ -135,10 +147,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)N_("terminate entries with NUL byte"),0),OPT_BIT('l',"long",&ls_options,N_("include object size"),LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('n',"name-only",&cmdmode,+N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('s',"name-status",&cmdmode,+N_("list only filenames"),MODE_NAME_ONLY),+OPT_CMDMODE('o',"oid-only",&cmdmode,+N_("list only oids"),MODE_OID_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
From: Teng Long <hidden> Date: 2021-12-08 02:08:42
Diffs from patch v4:
* Change `--oid-only` to `--object-only`.
Word "oid" may not be easily understood for users.
* The commit message was modified in terms of Junio's advice.
* Use "OPT_CMDMODE()" to make `--name-only`, `--object-only` and
`--long` mutually exclusive with each other.
* After options been parsed, translate options to bitmask, then use
cleaner bitwise to determine which fields will be shown.
* Add tests for mutually exclusive options.
* Documentation modifications about the change of option name.
Thanks.
Teng Long (1):
ls-tree.c: support `--object-only` option for "git-ls-tree"
Documentation/git-ls-tree.txt | 7 +-
builtin/ls-tree.c | 125 ++++++++++++++++++++++++----------
t/t3103-ls-tree-misc.sh | 8 +++
t/t3104-ls-tree-oid.sh | 51 ++++++++++++++
4 files changed, 154 insertions(+), 37 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
Range-diff against v4:
-: ---------- > 1: 38d55a878c ls-tree.c: support `--object-only` option for "git-ls-tree"
--
2.33.1.10.gd2a07a0ec5.dirty
From: Teng Long <hidden> Date: 2021-12-08 02:08:44
We usually pipe the output from `git ls-trees` to tools like
`sed` or `cut` when we only want to extract some fields.
When we want only the pathname component, we can pass
`--name-only` option to omit such a pipeline, but there are no
options for extracting other fields.
Teach the "--object-only" option to the command to only show the
object name. This option cannot be used together with
"--name-only" or "--long" (mutually exclusive).
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 7 +-
builtin/ls-tree.c | 125 ++++++++++++++++++++++++----------
t/t3103-ls-tree-misc.sh | 8 +++
t/t3104-ls-tree-oid.sh | 51 ++++++++++++++
4 files changed, 154 insertions(+), 37 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,6 +59,11 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.+ Cannot be combined with `--object-only`.++--object-only::+ List only names of the objects, one per line. Cannot be combined+ with `--name-only` or `--name-status`. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object
@@ -133,12 +183,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)LS_SHOW_TREES),OPT_SET_INT('z',NULL,&line_termination,N_("terminate entries with NUL byte"),0),-OPT_BIT('l',"long",&ls_options,N_("include object size"),-LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('l',"long",&cmdmode,N_("include object size"),+MODE_LONG),+OPT_CMDMODE(0,"name-only",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"name-status",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"object-only",&cmdmode,N_("list only objects"),+MODE_OBJECT_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
@@ -169,6 +221,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)if(get_oid(argv[0],&oid))die("Not a valid object name %s",argv[0]);+parse_shown_fields();/**show_recursive()rollsitsownmatchingcodeandis*generallyignorantof'structpathspec'.Themagicmask
From: Teng Long <hidden> Date: 2021-12-17 06:57:28
We usually pipe the output from `git ls-trees` to tools like
`sed` or `cut` when we only want to extract some fields.
When we want only the pathname component, we can pass
`--name-only` option to omit such a pipeline, but there are no
options for extracting other fields.
Teach the "--object-only" option to the command to only show the
object name. This option cannot be used together with
"--name-only" or "--long" (mutually exclusive).
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 7 +-
builtin/ls-tree.c | 131 ++++++++++++++++++++++++----------
quote.c | 8 +--
quote.h | 19 +++++
t/t3103-ls-tree-misc.sh | 8 +++
t/t3104-ls-tree-oid.sh | 51 +++++++++++++
6 files changed, 183 insertions(+), 41 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,6 +59,11 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.+ Cannot be combined with `--object-only`.++--object-only::+ List only names of the objects, one per line. Cannot be combined+ with `--name-only` or `--name-status`. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object
@@ -133,12 +189,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)LS_SHOW_TREES),OPT_SET_INT('z',NULL,&line_termination,N_("terminate entries with NUL byte"),0),-OPT_BIT('l',"long",&ls_options,N_("include object size"),-LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('l',"long",&cmdmode,N_("include object size"),+MODE_LONG),+OPT_CMDMODE(0,"name-only",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"name-status",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"object-only",&cmdmode,N_("list only objects"),+MODE_OBJECT_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
@@ -169,6 +227,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)if(get_oid(argv[0],&oid))die("Not a valid object name %s",argv[0]);+parse_shown_fields();/**show_recursive()rollsitsownmatchingcodeandis*generallyignorantof'structpathspec'.Themagicmask
Very nice to have the OPT_CMDMODE for asserting the usage, but this
would be even better if it were done as a separate commit. I.e. let's
first do prep cleanups, then the new --object-name mode.
These tests don't check for what you think they check, because you don't
supply a <tree-ish>. So they're really just dying for the same reason a:
git ls-tree
Would.
Many thanks to Junio and Ævar for your help and patient explanation.
I noticed Ævar suggest the solution with using `--format`, but in
this patch, the current approach continues. If this part of code needs
to be improved or we want to support "--format" in "ls-tree" in the
future, I'm more than glad to continue to contribute.
FWIW here's the changes I had locally & cleaned up now that did the
alternate --format approach.
I think you'll probably want to steal some of this, e.g. you're
patching the dead comment I removed in 1/6, 2-4/6 can be skipped, but
I thought they were nice.
Back when I last looked at this series, your --object-name patch was
much shorter, but now it's about the same size as the generic --format
support. So maybe it's worth considering implementing the more generic
path.
One reason I didn't submit this before is that I couldn't get past the
performance regression this would inttroduce, i.e. if moved entirely
to strbuf_expand(). Here though I'm keeping the old code, so it's no
slower than "master", unlike your patch. But I haven't dug into why
yours is slower:
$ git hyperfine -L rev origin/master,tl/object-name,avar/ls-tree-format -s 'make CFLAGS=-O3' './git -C /run/user/1001/linux ls-tree -r HEAD' --warmup 10 -r 10
Benchmark 1: ./git -C /run/user/1001/linux ls-tree -r HEAD' in 'origin/master
Time (mean ± σ): 67.8 ms ± 0.3 ms [User: 48.8 ms, System: 18.9 ms]
Range (min … max): 67.4 ms … 68.4 ms 10 runs
Benchmark 2: ./git -C /run/user/1001/linux ls-tree -r HEAD' in 'tl/object-name
Time (mean ± σ): 72.8 ms ± 0.4 ms [User: 50.6 ms, System: 22.1 ms]
Range (min … max): 72.0 ms … 73.2 ms 10 runs
Benchmark 3: ./git -C /run/user/1001/linux ls-tree -r HEAD' in 'avar/ls-tree-format
Time (mean ± σ): 67.6 ms ± 0.4 ms [User: 50.5 ms, System: 17.0 ms]
Range (min … max): 67.1 ms … 68.4 ms 10 runs
Summary
'./git -C /run/user/1001/linux ls-tree -r HEAD' in 'avar/ls-tree-format' ran
1.00 ± 0.01 times faster than './git -C /run/user/1001/linux ls-tree -r HEAD' in 'origin/master'
1.08 ± 0.01 times faster than './git -C /run/user/1001/linux ls-tree -r HEAD' in 'tl/object-name'
I then tacket a 6/6 at the end here to implement your --object-name in
terms of --format (but didn't update the comimt message etc.). That's
slower as expected:
$ git hyperfine -L rev tl/object-name,avar/ls-tree-format -s 'make CFLAGS=-O3' './git -C /run/user/1001/linux ls-tree --object-only -r HEAD' --warmup 10 -r 10
Benchmark 1: ./git -C /run/user/1001/linux ls-tree --object-only -r HEAD' in 'tl/object-name
Time (mean ± σ): 58.7 ms ± 0.4 ms [User: 43.0 ms, System: 15.6 ms]
Range (min … max): 58.4 ms … 59.6 ms 10 runs
Benchmark 2: ./git -C /run/user/1001/linux ls-tree --object-only -r HEAD' in 'avar/ls-tree-format
Time (mean ± σ): 65.6 ms ± 0.2 ms [User: 42.4 ms, System: 23.0 ms]
Range (min … max): 65.1 ms … 65.9 ms 10 runs
Summary
'./git -C /run/user/1001/linux ls-tree --object-only -r HEAD' in 'tl/object-name' ran
1.12 ± 0.01 times faster than './git -C /run/user/1001/linux ls-tree --object-only -r HEAD' in 'avar/ls-tree-format'
But it's not too bad, so maybe it's fine & worth making it more
generic?
Anyway. Just food for thought and and FYI in case you're
interested. Junio noted already that he'd like the --object-name
approach first, so if you still want to pursue your current
implementation I don't mind.
I do think you should be making performance testing a part of your
testing & cover letter writing though. A 8-10% slowdown isn't nothing,
especially for exactly the sort of plumbing command that'll likely to
be used to e.g. slurp up all paths in a very large repo.
These patches really aren't "ready". There's no docs, and as I noted
in some earlier thread the tests for ls-tree are really
lacking. E.g. I seem to have a rather obvious bug in how -t and the
--format interact here, but no test catches it.
Well, that one's me not having added a test, but I'm fairly sure there
might also be hidden bugs here due to lack of testing.
Teng Long (1):
ls-tree.c: support `--object-only` option for "git-ls-tree"
Ævar Arnfjörð Bjarmason (6):
ls-tree: remove commented-out code
ls-tree: add missing braces to "else" arms
ls-tree: use "enum object_type", not {blob,tree,commit}_type
ls-tree: use "size_t", not "int" for "struct strbuf"'s "len"
ls-tree: split up the "init" part of show_tree()
ls-tree: add a --format=<fmt> option
Documentation/git-ls-tree.txt | 7 +-
builtin/ls-tree.c | 226 ++++++++++++++++++++++++++++++----
t/t3103-ls-tree-misc.sh | 8 ++
t/t3104-ls-tree-oid.sh | 51 ++++++++
t/t3105-ls-tree-format.sh | 49 ++++++++
5 files changed, 313 insertions(+), 28 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
create mode 100755 t/t3105-ls-tree-format.sh
--
2.34.1.1119.g7a3fc8778ee
Remove code added in f35a6d3bce7 (Teach core object handling functions
about gitlinks, 2007-04-09), later patched in 7d0b18a4da1 (Add output
flushing before fork(), 2008-08-04), and then finally ending up in its
current form in d3bee161fef (tree.c: allow read_tree_recursive() to
traverse gitlink entries, 2009-01-25). All while being commented-out!
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 9 ---------
1 file changed, 9 deletions(-)
The "struct strbuf"'s "len" member is a "size_t", not an "int", so
let's change our corresponding types accordingly. This also changes
the "len" and "speclen" variables, which are likewise used to store
the return value of strlen(), which returns "size_t", not "int".
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
Change the ls-tree.c code to use type_name() on the enum instead of
using the string constants. This doesn't matter either way for
performance, but makes this a bit easier to read as we'll no longer
need a strcmp() here.
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
Add a --format option to ls-tree. It has an existing default output,
and then --long and --name-only options to emit the default output
along with the objectsize and, or to only emit object paths.
Rather than add --type-only, --object-only etc. we can just support a
--format using a strbuf_expand() similar to "for-each-ref
--format". We might still add such options in the future for
convenience.
The --format implementation is slower than the existing code, but this
change does not cause any performance regressions. We'll leave the
existing show_tree() unchanged, and only run show_tree_format() in if
a --format different than the hardcoded built-in ones corresponding to
the existing modes is provided.
"Slower" here can bee seen via the the following "hyperfine"
command. This uses GIT_TEST_LS_TREE_FORMAT_BACKEND=<bool> to force the
use of the new backend:
$ hyperfine -L env false,true -L f "-r,-r -l,-r --name-only,-r --format='%(objectname)'" 'GIT_TEST_LS_TREE_FORMAT_BACKEND={env} ./git -C ~/g/linux ls-tree {f} HEAD' -r 10
Benchmark 1: GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r HEAD
Time (mean ± σ): 86.1 ms ± 0.6 ms [User: 65.2 ms, System: 20.9 ms]
Range (min … max): 85.2 ms … 87.5 ms 10 runs
Benchmark 2: GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r HEAD
Time (mean ± σ): 122.5 ms ± 0.6 ms [User: 101.3 ms, System: 21.1 ms]
Range (min … max): 121.8 ms … 123.4 ms 10 runs
Benchmark 3: GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r -l HEAD
Time (mean ± σ): 277.7 ms ± 1.3 ms [User: 234.6 ms, System: 43.0 ms]
Range (min … max): 275.9 ms … 279.7 ms 10 runs
Benchmark 4: GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r -l HEAD
Time (mean ± σ): 332.8 ms ± 2.6 ms [User: 282.0 ms, System: 50.7 ms]
Range (min … max): 329.6 ms … 338.2 ms 10 runs
Benchmark 5: GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r --name-only HEAD
Time (mean ± σ): 71.8 ms ± 0.4 ms [User: 54.1 ms, System: 17.6 ms]
Range (min … max): 71.2 ms … 72.5 ms 10 runs
Benchmark 6: GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r --name-only HEAD
Time (mean ± σ): 86.6 ms ± 0.5 ms [User: 65.7 ms, System: 20.7 ms]
Range (min … max): 85.9 ms … 87.4 ms 10 runs
Benchmark 7: GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r --format='%(objectname)' HEAD
Time (mean ± σ): 85.8 ms ± 0.6 ms [User: 66.2 ms, System: 19.5 ms]
Range (min … max): 85.0 ms … 86.9 ms 10 runs
Benchmark 8: GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r --format='%(objectname)' HEAD
Time (mean ± σ): 85.3 ms ± 0.2 ms [User: 66.6 ms, System: 18.7 ms]
Range (min … max): 85.0 ms … 85.7 ms 10 runs
Summary
'GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r --name-only HEAD' ran
1.19 ± 0.01 times faster than 'GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r --format='%(objectname)' HEAD'
1.19 ± 0.01 times faster than 'GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r --format='%(objectname)' HEAD'
1.20 ± 0.01 times faster than 'GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r HEAD'
1.21 ± 0.01 times faster than 'GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r --name-only HEAD'
1.71 ± 0.01 times faster than 'GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r HEAD'
3.87 ± 0.03 times faster than 'GIT_TEST_LS_TREE_FORMAT_BACKEND=false ./git -C ~/g/linux ls-tree -r -l HEAD'
4.64 ± 0.05 times faster than 'GIT_TEST_LS_TREE_FORMAT_BACKEND=true ./git -C ~/g/linux ls-tree -r -l HEAD'
I.e. something like the "--long" output would be much slower with
this, mainly due to how we need to allocate various things to do with
quote.c instead of spewing the output directly to stdout.
But even a --format='%(objectname)' is fast with the new backend, so
this is viable as a replacement for adding new formats, and we'll pay
for this added complexity as a one-off, and not again every time a new
format needs to be added. See [1] for an example of what it would
otherwise take to add an --object-name flag.
1. https://lore.kernel.org/git/2e449d1c792ff81da5f22c8bf65ed33c393d62f8.1639721750.git.dyroneteng@gmail.com/
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 167 +++++++++++++++++++++++++++++++++++++-
t/t3105-ls-tree-format.sh | 49 +++++++++++
2 files changed, 215 insertions(+), 1 deletion(-)
create mode 100755 t/t3105-ls-tree-format.sh
@@ -61,6 +84,76 @@ static int show_recursive(const char *base, size_t baselen, const char *pathnamereturn0;}+staticvoidexpand_objectsize(structstrbuf*sb,+conststructobject_id*oid,+constenumobject_typetype,+unsignedintpadded)+{+if(type==OBJ_BLOB){+unsignedlongsize;+if(oid_object_info(the_repository,oid,&size)<0)+die(_("could not get object info about '%s'"),oid_to_hex(oid));+if(padded)+strbuf_addf(sb,"%7"PRIuMAX,(uintmax_t)size);+else+strbuf_addf(sb,"%"PRIuMAX,(uintmax_t)size);+}elseif(padded){+strbuf_addf(sb,"%7s","-");+}else{+strbuf_addstr(sb,"-");+}+}++staticsize_texpand_show_tree(structstrbuf*sb,+constchar*start,+void*context)+{+structexpand_ls_tree_data*data=context;+constchar*end;+constchar*p;+size_tlen;++len=strbuf_expand_literal_cb(sb,start,NULL);+if(len)+returnlen;++if(*start!='(')+die(_("bad format as of '%s'"),start);+end=strchr(start+1,')');+if(!end)+die(_("ls-tree format element '%s' does not end in ')'"),+start);+len=end-start+1;++if(skip_prefix(start,"(objectmode)",&p)){+strbuf_addf(sb,"%06o",data->mode);+}elseif(skip_prefix(start,"(objecttype)",&p)){+strbuf_addstr(sb,type_name(data->type));+}elseif(skip_prefix(start,"(objectsize:padded)",&p)){+expand_objectsize(sb,data->oid,data->type,1);+}elseif(skip_prefix(start,"(objectsize)",&p)){+expand_objectsize(sb,data->oid,data->type,0);+}elseif(skip_prefix(start,"(objectname)",&p)){+strbuf_addstr(sb,find_unique_abbrev(data->oid,abbrev));+}elseif(skip_prefix(start,"(path)",&p)){+constchar*name=data->basebuf;+constchar*prefix=chomp_prefix?ls_tree_prefix:NULL;++if(prefix)+name=relative_path(name,prefix,data->sb_scratch);+quote_c_style(name,data->sb_tmp,NULL,0);+strbuf_add(sb,data->sb_tmp->buf,data->sb_tmp->len);++strbuf_reset(data->sb_tmp);+/* The relative_path() function resets "scratch" */+}else{+unsignedinterrlen=(unsignedlong)len;+die(_("bad ls-tree format specifiec %%%.*s"),errlen,start);+}++returnlen;+}+staticintshow_tree_init(enumobject_type*type,structstrbuf*base,constchar*pathname,unsignedmode,int*retval){
@@ -125,6 +250,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)structobject_idoid;structtree*tree;inti,full_tree=0;+constchar*implicit_format=NULL;+constchar*format=NULL;+structread_tree_ls_tree_dataread_tree_cb_data={+.sb_scratch=STRBUF_INIT,+.sb_tmp=STRBUF_INIT,+};conststructoptionls_tree_options[]={OPT_BIT('d',NULL,&ls_options,N_("only show trees"),LS_TREE_ONLY),
@@ -145,9 +276,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)OPT_BOOL(0,"full-tree",&full_tree,N_("list entire tree; not just current directory ""(implies --full-name)")),+OPT_STRING_F(0,"format",&format,N_("format"),+N_("format to use for the output"),PARSE_OPT_NONEG),OPT__ABBREV(&abbrev),OPT_END()};+read_tree_fn_tfn=show_tree;git_config(git_default_config,NULL);ls_tree_prefix=prefix;
@@ -164,6 +298,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)if((LS_TREE_ONLY|LS_RECURSIVE)==((LS_TREE_ONLY|LS_RECURSIVE)&ls_options))ls_options|=LS_SHOW_TREES;+if(ls_options&LS_NAME_ONLY)+implicit_format=ls_tree_format_n;+if(ls_options&LS_SHOW_SIZE)+implicit_format=ls_tree_format_l;++if(format&&implicit_format)+usage_msg_opt(_("providing --format cannot be combined with other format-altering options"),+ls_tree_usage,ls_tree_options);+if(implicit_format)+format=implicit_format;+if(!format)+format=ls_tree_format_d;if(argc<1)usage_with_options(ls_tree_usage,ls_tree_options);
@@ -186,6 +332,25 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)tree=parse_tree_indirect(&oid);if(!tree)die("not a tree object");++/*+*Thegenericshow_tree_fmt()isslowerthanshow_tree(),so+*takethefastpathifpossible.+*/+if(format&&(!strcmp(format,ls_tree_format_d)||+!strcmp(format,ls_tree_format_l)||+!strcmp(format,ls_tree_format_n)))+fn=show_tree;+elseif(format)+fn=show_tree_fmt;+/*+*Allowforcingtheshow_tree_fmt(),totestthatitcan+*handlethetestsuite.+*/+if(git_env_bool("GIT_TEST_LS_TREE_FORMAT_BACKEND",0))+fn=show_tree_fmt;++read_tree_cb_data.format=format;return!!read_tree(the_repository,tree,-&pathspec,show_tree,NULL);+&pathspec,fn,&read_tree_cb_data);}
Split up the "init" part of the show_tree() function where we decide
what the "type" is, and whether we'll return early. This makes things
a bit less readable for now, but we'll soon re-use this in a sibling
function, and avoiding the duplication will be worth it.
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
From: Teng Long <redacted>
We usually pipe the output from `git ls-trees` to tools like
`sed` or `cut` when we only want to extract some fields.
When we want only the pathname component, we can pass
`--name-only` option to omit such a pipeline, but there are no
options for extracting other fields.
Teach the "--object-only" option to the command to only show the
object name. This option cannot be used together with
"--name-only" or "--long" (mutually exclusive).
Signed-off-by: Teng Long <redacted>
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
Documentation/git-ls-tree.txt | 7 ++++-
builtin/ls-tree.c | 6 +++++
t/t3103-ls-tree-misc.sh | 8 ++++++
t/t3104-ls-tree-oid.sh | 51 +++++++++++++++++++++++++++++++++++
4 files changed, 71 insertions(+), 1 deletion(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,6 +59,11 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.+ Cannot be combined with `--object-only`.++--object-only::+ List only names of the objects, one per line. Cannot be combined+ with `--name-only` or `--name-status`. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object
@@ -271,6 +273,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)LS_NAME_ONLY),OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),LS_NAME_ONLY),+OPT_BIT(0,"object-only",&ls_options,N_("list only objects"),+LS_OBJECT_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
@@ -302,6 +306,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)implicit_format=ls_tree_format_n;if(ls_options&LS_SHOW_SIZE)implicit_format=ls_tree_format_l;+if(ls_options&LS_OBJECT_ONLY)+implicit_format=ls_tree_format_o;if(format&&implicit_format)usage_msg_opt(_("providing --format cannot be combined with other format-altering options"),
From: Teng Long <hidden> Date: 2022-01-01 13:51:00
From: Ævar Arnfjörð Bjarmason <redacted>
Remove code added in f35a6d3bce7 (Teach core object handling functions
about gitlinks, 2007-04-09), later patched in 7d0b18a4da1 (Add output
flushing before fork(), 2008-08-04), and then finally ending up in its
current form in d3bee161fef (tree.c: allow read_tree_recursive() to
traverse gitlink entries, 2009-01-25). All while being commented-out!
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 9 ---------
1 file changed, 9 deletions(-)
From: Teng Long <hidden> Date: 2022-01-01 13:51:00
Diff from v6 (Origin) and v7 (RFC by Ævar):
1. [v6] Performance Regression
In v6, Ævar pointed out that there's a nearly 10% performance
regression under linux [1]. This is cause by in V6, I chose to
use a bitwisp operation to check whether the specified field to
be printed, this will separate the original to many "printf" to
combined the final output format. But some "checks" are unnecessary,
like we will check whether to print the "mode" and the "type", but
we do not really need to do that because only print them are
meaningless.
So in commit cb881183cb if this patch, I kept some parts of
bitwise logic in "show_tree" because it's more intuitive than before
I think. Then, move the original logic to function "show_default", now
in "show_tree" the procedure is clearer, first "show_tree_init", then
check whether it's asked only to print objectname or filename, or to
print a default format. After this, the performance regression problem
was solved, here is the performance test result based on linux in my env:
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.8 ms ± 2.7 ms [User: 85.7 ms, System: 20.0 ms]
Range (min … max): 101.5 ms … 111.3 ms 28 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.0 ms ± 3.0 ms [User: 83.7 ms, System: 21.2 ms]
Range (min … max): 99.3 ms … 109.5 ms 27 runs
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 337.4 ms ± 10.9 ms [User: 308.3 ms, System: 29.0 ms]
Range (min … max): 323.0 ms … 355.0 ms 10 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 337.6 ms ± 6.2 ms [User: 309.4 ms, System: 28.1 ms]
Range (min … max): 330.4 ms … 349.9 ms 10 runs
2. [v6] Bugs in "t3104"
Ævar found that[2] I forgot to supply a <tree-ish> in tests, that's obviously a
bug need to fix and already done in this patch.
3. [RFC v7 by Ævar] the pre-works commits
Ævar helped to do some pre-works commits in V7, they are 2fcff7e0d4, 6fd1dd9383,
208654b5e2, 2637464fd8 and d77c895a4b, I think these are all reasonable, so I
just cherry-pick to this patch and continue the work base on them.
First, is to support `--object-only`, had been mentioned above. The second commit is
to create a "shown_data" struct to prepare the next "--format" commit for reusing the
struct. The last one (ls-tree.c: introduce "--format" option) is to support the
"--format" option.
Ævar posted a commit for mainly supporting "--format" in RFC v7[3] and give some design
and performance test context. My commit based on Ævar's (I'm not sure I have to mark
something about Ævar in commit message, because I only made some modifications but the
idea is from Ævar) but exists some changes:
1). Changed the format field names, the original's and the current's are:
objectmode -> mode
objecttype -> type
objectname -> object
path -> file
The original's are ok, just I prefer to make the name more simple to memorize and
type, in addition, the current Documentation/git-ls-tree.txt, at "Output Format"
section use "<mode> SP <type> SP <object> TAB <file>" to describe the format.
I think the names with "object" prefix are from Documentation/git-for-each-ref.txt,
use a "objectname" is not a redundant expression because there are also "authorname"
and "refname" to be distingushed in `git-for-each-ref`, but in "git-ls-tree",
currently, seems like no necessary, but I'm not so much sure about the naming rules
if I was missing something.
2). OPT_CMDMODE and OPT_BIT:
I noticed Ævar uses "OPT_BIT" in his patch but I use "OPT_CMDMODE" (actually
OPT_CMDMODE also is Ævar teached me) and they seems like both supporting to
make a mutual exclusive betweem options. I didn't change them to "OPT_BIT"
because they looked like working well, plz told me if I misunderstood.
2. Performance comparation between "master" and v8
1). Default format( "git ls-tree -r" vs "hitten builtin formats" vs "miss builtin formats")
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.2 ms ± 3.3 ms [User: 84.3 ms, System: 20.8 ms]
Range (min … max): 99.2 ms … 113.2 ms 28 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)' HEAD
Time (mean ± σ): 106.4 ms ± 2.7 ms [User: 86.1 ms, System: 20.2 ms]
Range (min … max): 100.2 ms … 110.5 ms 29 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='> %(mode) %(type) %(object)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='> %(mode) %(type) %(object)%x09%(file)' HEAD
Time (mean ± σ): 145.3 ms ± 3.9 ms [User: 119.0 ms, System: 26.2 ms]
Range (min … max): 139.7 ms … 150.8 ms 20 runs
2). Default format that including object size (( "git ls-tree -r -l" vs "hitten builtin formats" vs "miss builtin formats"))
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 335.1 ms ± 6.5 ms [User: 304.6 ms, System: 30.4 ms]
Range (min … max): 327.5 ms … 348.4 ms 10 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD
Time (mean ± σ): 337.2 ms ± 8.2 ms [User: 309.2 ms, System: 27.9 ms]
Range (min … max): 328.8 ms … 349.4 ms 10 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='> %(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='> %(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD
Time (mean ± σ): 396.9 ms ± 8.9 ms [User: 364.2 ms, System: 32.7 ms]
Range (min … max): 379.6 ms … 408.6 ms 10 runs
Thanks.
[1] https://public-inbox.org/git/RFC-cover-0.7-00000000000-20211217T131635Z-avarab@gmail.com/
[2] https://public-inbox.org/git/211217.86o85f8jey.gmgdl@evledraar.gmail.com/
[3] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/
Teng Long (3):
ls-tree.c: support --object-only option for "git-ls-tree"
ls-tree.c: introduce struct "shown_data"
ls-tree.c: introduce "--format" option
Ævar Arnfjörð Bjarmason (5):
ls-tree: remove commented-out code
ls-tree: add missing braces to "else" arms
ls-tree: use "enum object_type", not {blob,tree,commit}_type
ls-tree: use "size_t", not "int" for "struct strbuf"'s "len"
ls-tree: split up the "init" part of show_tree()
Documentation/git-ls-tree.txt | 55 +++++-
builtin/ls-tree.c | 315 +++++++++++++++++++++++++++-------
t/t3104-ls-tree-oid.sh | 51 ++++++
t/t3105-ls-tree-format.sh | 55 ++++++
4 files changed, 415 insertions(+), 61 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
create mode 100755 t/t3105-ls-tree-format.sh
Range-diff against v7:
-: ---------- > 1: 2fcff7e0d4 ls-tree: remove commented-out code
-: ---------- > 2: 6fd1dd9383 ls-tree: add missing braces to "else" arms
-: ---------- > 3: 208654b5e2 ls-tree: use "enum object_type", not {blob,tree,commit}_type
-: ---------- > 4: 2637464fd8 ls-tree: use "size_t", not "int" for "struct strbuf"'s "len"
-: ---------- > 5: d77c895a4b ls-tree: split up the "init" part of show_tree()
1: 2e449d1c79 ! 6: cb881183cb ls-tree.c: support `--object-only` option for "git-ls-tree"
@@ Metadata
Author: Teng Long [off-list ref]
## Commit message ##
- ls-tree.c: support `--object-only` option for "git-ls-tree"
+ ls-tree.c: support --object-only option for "git-ls-tree"
We usually pipe the output from `git ls-trees` to tools like
`sed` or `cut` when we only want to extract some fields.
@@ Commit message
Teach the "--object-only" option to the command to only show the
object name. This option cannot be used together with
- "--name-only" or "--long" (mutually exclusive).
+ "--name-only" or "--long" , they are mutually exclusive (actually
+ "--name-only" and "--long" can be combined together before, this
+ commit by the way fix this bug).
+
+ A simple refactoring was done to the "show_tree" function, intead by
+ using bitwise operations to recognize the format for printing to
+ stdout. The reason for doing this is that we don't want to increase
+ the readability difficulty with the addition of "-object-only",
+ making this part of the logic easier to read and expand.
+
+ In terms of performance, there is no loss comparing to the
+ "master" (2ae0a9cb8298185a94e5998086f380a355dd8907), here are the
+ results of the performance tests in my environment based on linux
+ repository:
+
+ $hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r HEAD"
+ Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD
+ Time (mean ± σ): 105.8 ms ± 2.7 ms [User: 85.7 ms, System: 20.0 ms]
+ Range (min … max): 101.5 ms … 111.3 ms 28 runs
+
+ $hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD"
+ Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD
+ Time (mean ± σ): 105.0 ms ± 3.0 ms [User: 83.7 ms, System: 21.2 ms]
+ Range (min … max): 99.3 ms … 109.5 ms 27 runs
+
+ $hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r -l HEAD"
+ Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD
+ Time (mean ± σ): 337.4 ms ± 10.9 ms [User: 308.3 ms, System: 29.0 ms]
+ Range (min … max): 323.0 ms … 355.0 ms 10 runs
+
+ $hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD"
+ Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD
+ Time (mean ± σ): 337.6 ms ± 6.2 ms [User: 309.4 ms, System: 28.1 ms]
+ Range (min … max): 330.4 ms … 349.9 ms 10 runs
Signed-off-by: Teng Long [off-list ref]
@@ builtin/ls-tree.c
NULL
};
+-static int show_recursive(const char *base, size_t baselen, const char *pathname)
+enum {
+ MODE_UNSPECIFIED = 0,
+ MODE_NAME_ONLY,
@@ builtin/ls-tree.c
+
+static int cmdmode = MODE_UNSPECIFIED;
+
- static int show_recursive(const char *base, int baselen, const char *pathname)
++static int parse_shown_fields(void)
++{
++ if (cmdmode == MODE_NAME_ONLY) {
++ shown_bits = SHOW_FILE_NAME;
++ return 0;
++ }
++ if (cmdmode == MODE_OBJECT_ONLY) {
++ shown_bits = SHOW_OBJECT_NAME;
++ return 0;
++ }
++ if (!ls_options || (ls_options & LS_RECURSIVE)
++ || (ls_options & LS_SHOW_TREES)
++ || (ls_options & LS_TREE_ONLY))
++ shown_bits = SHOW_DEFAULT;
++ if (cmdmode == MODE_LONG)
++ shown_bits = SHOW_DEFAULT | SHOW_SIZE;
++ return 1;
++}
++
++static int show_recursive(const char *base, size_t baselen,
++ const char *pathname)
{
int i;
-@@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,
- {
- int retval = 0;
- int baselen;
-+ int interspace = 0;
- const char *type = blob_type;
- if (S_ISGITLINK(mode)) {
-@@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,
- *
- * Something similar to this incomplete example:
- *
-- if (show_subprojects(base, baselen, pathname))
-- retval = READ_TREE_RECURSIVE;
-+ * if (show_subprojects(base, baselen, pathname))
-+ * retval = READ_TREE_RECURSIVE;
- *
- */
- type = commit_type;
+@@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname
+ return 0;
+ }
+
++static int show_default(const struct object_id *oid, enum object_type type,
++ const char *pathname, unsigned mode,
++ struct strbuf *base)
++{
++ size_t baselen = base->len;
++
++ if (shown_bits & SHOW_SIZE) {
++ char size_text[24];
++ if (type == OBJ_BLOB) {
++ unsigned long size;
++ if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
++ xsnprintf(size_text, sizeof(size_text), "BAD");
++ else
++ xsnprintf(size_text, sizeof(size_text),
++ "%" PRIuMAX, (uintmax_t)size);
++ } else {
++ xsnprintf(size_text, sizeof(size_text), "-");
++ }
++ printf("%06o %s %s %7s\t", mode, type_name(type),
++ find_unique_abbrev(oid, abbrev), size_text);
++ } else {
++ printf("%06o %s %s\t", mode, type_name(type),
++ find_unique_abbrev(oid, abbrev));
++ }
++ baselen = base->len;
++ strbuf_addstr(base, pathname);
++ write_name_quoted_relative(base->buf,
++ chomp_prefix ? ls_tree_prefix : NULL, stdout,
++ line_termination);
++ strbuf_setlen(base, baselen);
++ return 1;
++}
++
+ static int show_tree_init(enum object_type *type, struct strbuf *base,
+ const char *pathname, unsigned mode, int *retval)
+ {
@@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,
- else if (ls_options & LS_TREE_ONLY)
- return 0;
+ if (show_tree_init(&type, base, pathname, mode, &retval))
+ return retval;
- if (!(ls_options & LS_NAME_ONLY)) {
- if (ls_options & LS_SHOW_SIZE) {
- char size_text[24];
-- if (!strcmp(type, blob_type)) {
+- if (type == OBJ_BLOB) {
- unsigned long size;
- if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
- xsnprintf(size_text, sizeof(size_text),
@@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb
- else
- xsnprintf(size_text, sizeof(size_text),
- "%"PRIuMAX, (uintmax_t)size);
-- } else
+- } else {
- xsnprintf(size_text, sizeof(size_text), "-");
-- printf("%06o %s %s %7s\t", mode, type,
+- }
+- printf("%06o %s %s %7s\t", mode, type_name(type),
- find_unique_abbrev(oid, abbrev),
- size_text);
-+ if (shown_bits & SHOW_MODE) {
-+ printf("%06o", mode);
-+ interspace = 1;
-+ }
-+ if (shown_bits & SHOW_TYPE) {
-+ printf("%s%s", interspace ? " " : "", type);
-+ interspace = 1;
-+ }
-+ if (shown_bits & SHOW_OBJECT_NAME) {
-+ printf("%s%s", interspace ? " " : "",
-+ find_unique_abbrev(oid, abbrev));
-+ if (!(shown_bits ^ SHOW_OBJECT_NAME))
-+ goto LINE_FINISH;
-+ interspace = 1;
-+ }
-+ if (shown_bits & SHOW_SIZE) {
-+ char size_text[24];
-+ if (!strcmp(type, blob_type)) {
-+ unsigned long size;
-+ if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
-+ xsnprintf(size_text, sizeof(size_text), "BAD");
-+ else
-+ xsnprintf(size_text, sizeof(size_text),
-+ "%"PRIuMAX, (uintmax_t)size);
- } else
-- printf("%06o %s %s\t", mode, type,
+- } else {
+- printf("%06o %s %s\t", mode, type_name(type),
- find_unique_abbrev(oid, abbrev));
-+ xsnprintf(size_text, sizeof(size_text), "-");
-+ printf("%s%7s", interspace ? " " : "", size_text);
-+ interspace = 1;
-+ }
-+ if (shown_bits & SHOW_FILE_NAME) {
-+ if (interspace)
-+ printf("\t");
-+ baselen = base->len;
-+ strbuf_addstr(base, pathname);
-+ write_name_quoted_relative(base->buf,
-+ chomp_prefix ? ls_tree_prefix : NULL,
-+ stdout,
-+ line_termination
-+ ? CQ_NO_TERMINATOR_C_QUOTED
-+ : CQ_NO_TERMINATOR_AS_IS);
-+ strbuf_setlen(base, baselen);
+- }
++ if (!(shown_bits ^ SHOW_OBJECT_NAME)) {
++ printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination);
++ return retval;
}
- baselen = base->len;
- strbuf_addstr(base, pathname);
@@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb
- stdout, line_termination);
- strbuf_setlen(base, baselen);
+
-+LINE_FINISH:
-+ putchar(line_termination);
++ if (!(shown_bits ^ SHOW_FILE_NAME)) {
++ baselen = base->len;
++ strbuf_addstr(base, pathname);
++ write_name_quoted_relative(base->buf,
++ chomp_prefix ? ls_tree_prefix : NULL,
++ stdout, line_termination);
++ strbuf_setlen(base, baselen);
++ }
++
++ if (!(shown_bits ^ SHOW_DEFAULT) ||
++ !(shown_bits ^ (SHOW_DEFAULT | SHOW_SIZE)))
++ show_default(oid, type, pathname, mode, base);
++
return retval;
}
-+static int parse_shown_fields(void)
-+{
-+ if (cmdmode == MODE_NAME_ONLY) {
-+ shown_bits = SHOW_FILE_NAME;
-+ return 0;
-+ }
-+ if (cmdmode == MODE_OBJECT_ONLY) {
-+ shown_bits = SHOW_OBJECT_NAME;
-+ return 0;
-+ }
-+ if (!ls_options || (ls_options & LS_RECURSIVE)
-+ || (ls_options & LS_SHOW_TREES)
-+ || (ls_options & LS_TREE_ONLY))
-+ shown_bits = SHOW_DEFAULT;
-+ if (cmdmode == MODE_LONG)
-+ shown_bits = SHOW_DEFAULT | SHOW_SIZE;
-+ return 1;
-+}
-+
- int cmd_ls_tree(int argc, const char **argv, const char *prefix)
- {
- struct object_id oid;
@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)
LS_SHOW_TREES),
OPT_SET_INT('z', NULL, &line_termination,
@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref
die("Not a valid object name %s", argv[0]);
+ parse_shown_fields();
++
/*
* show_recursive() rolls its own matching code and is
* generally ignorant of 'struct pathspec'. The magic mask
- ## quote.c ##
-@@ quote.c: void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path,
-
- void write_name_quoted(const char *name, FILE *fp, int terminator)
- {
-- if (terminator) {
-+ if (0 < terminator || terminator == CQ_NO_TERMINATOR_C_QUOTED)
- quote_c_style(name, NULL, fp, 0);
-- } else {
-+ else
- fputs(name, fp);
-- }
-- fputc(terminator, fp);
-+ if (0 <= terminator)
-+ fputc(terminator, fp);
- }
-
- void write_name_quoted_relative(const char *name, const char *prefix,
-
- ## quote.h ##
-@@ quote.h: int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
- #define CQUOTE_NODQ 01
- size_t quote_c_style(const char *name, struct strbuf *, FILE *, unsigned);
- void quote_two_c_style(struct strbuf *, const char *, const char *, unsigned);
-+/*
-+ * Write a name, typically a filename, followed by a terminator that
-+ * separates it from what comes next.
-+ * When terminator is NUL, the name is given as-is. Otherwise, the
-+ * name is c-quoted, suitable for text output. HT and LF are typical
-+ * values used for the terminator, but other positive values are possible.
-+ *
-+ * In addition to non-negative values two special values in terminator
-+ * are possible.
-+ *
-+ * -1: show the name c-quoted, without adding any terminator.
-+ * -2: show the name as-is, without adding any terminator.
-+ */
-+#define CQ_NO_TERMINATOR_C_QUOTED (-1)
-+#define CQ_NO_TERMINATOR_AS_IS (-2)
-
- void write_name_quoted(const char *name, FILE *, int terminator);
-+/*
-+ * Similar to the above, but the name is first made relative to the prefix
-+ * before being shown.
-+ */
- void write_name_quoted_relative(const char *name, const char *prefix,
- FILE *fp, int terminator);
-
-
- ## t/t3103-ls-tree-misc.sh ##
-@@ t/t3103-ls-tree-misc.sh: test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
- test_must_fail git ls-tree -r HEAD
- '
-
-+test_expect_success 'usage: incompatible options: --name-status with --long' '
-+ test_expect_code 129 git ls-tree --long --name-status
-+'
-+
-+test_expect_success 'usage: incompatible options: --name-only with --long' '
-+ test_expect_code 129 git ls-tree --long --name-only
-+'
-+
- test_done
-
## t/t3104-ls-tree-oid.sh (new) ##
@@
+#!/bin/sh
@@ t/t3104-ls-tree-oid.sh (new)
+'
+
+test_expect_success 'usage: incompatible options: --name-only with --object-only' '
-+ test_expect_code 129 git ls-tree --object-only --name-only
++ test_expect_code 129 git ls-tree --object-only --name-only $tree
+'
+
+test_expect_success 'usage: incompatible options: --name-status with --object-only' '
-+ test_expect_code 129 git ls-tree --object-only --name-status
++ test_expect_code 129 git ls-tree --object-only --name-status $tree
+'
+
+test_expect_success 'usage: incompatible options: --long with --object-only' '
-+ test_expect_code 129 git ls-tree --object-only --long
++ test_expect_code 129 git ls-tree --object-only --long $tree
+'
+
+test_done
-: ---------- > 7: 296ebacafe ls-tree.c: introduce struct "shown_data"
-: ---------- > 8: e0add802fb ls-tree.c: introduce "--format" option
--
2.33.0.rc1.1802.gbb1c3936fb.dirty
From: Teng Long <hidden> Date: 2022-01-01 13:51:09
From: Ævar Arnfjörð Bjarmason <redacted>
Change the ls-tree.c code to use type_name() on the enum instead of
using the string constants. This doesn't matter either way for
performance, but makes this a bit easier to read as we'll no longer
need a strcmp() here.
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
From: Teng Long <hidden> Date: 2022-01-01 13:51:12
From: Ævar Arnfjörð Bjarmason <redacted>
The "struct strbuf"'s "len" member is a "size_t", not an "int", so
let's change our corresponding types accordingly. This also changes
the "len" and "speclen" variables, which are likewise used to store
the return value of strlen(), which returns "size_t", not "int".
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
From: Teng Long <hidden> Date: 2022-01-01 13:51:19
From: Ævar Arnfjörð Bjarmason <redacted>
Split up the "init" part of the show_tree() function where we decide
what the "type" is, and whether we'll return early. This makes things
a bit less readable for now, but we'll soon re-use this in a sibling
function, and avoiding the duplication will be worth it.
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
From: Teng Long <hidden> Date: 2022-01-01 13:51:22
We usually pipe the output from `git ls-trees` to tools like
`sed` or `cut` when we only want to extract some fields.
When we want only the pathname component, we can pass
`--name-only` option to omit such a pipeline, but there are no
options for extracting other fields.
Teach the "--object-only" option to the command to only show the
object name. This option cannot be used together with
"--name-only" or "--long" , they are mutually exclusive (actually
"--name-only" and "--long" can be combined together before, this
commit by the way fix this bug).
A simple refactoring was done to the "show_tree" function, intead by
using bitwise operations to recognize the format for printing to
stdout. The reason for doing this is that we don't want to increase
the readability difficulty with the addition of "-object-only",
making this part of the logic easier to read and expand.
In terms of performance, there is no loss comparing to the
"master" (2ae0a9cb8298185a94e5998086f380a355dd8907), here are the
results of the performance tests in my environment based on linux
repository:
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.8 ms ± 2.7 ms [User: 85.7 ms, System: 20.0 ms]
Range (min … max): 101.5 ms … 111.3 ms 28 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.0 ms ± 3.0 ms [User: 83.7 ms, System: 21.2 ms]
Range (min … max): 99.3 ms … 109.5 ms 27 runs
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 337.4 ms ± 10.9 ms [User: 308.3 ms, System: 29.0 ms]
Range (min … max): 323.0 ms … 355.0 ms 10 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 337.6 ms ± 6.2 ms [User: 309.4 ms, System: 28.1 ms]
Range (min … max): 330.4 ms … 349.9 ms 10 runs
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 7 +-
builtin/ls-tree.c | 140 +++++++++++++++++++++++++---------
t/t3104-ls-tree-oid.sh | 51 +++++++++++++
3 files changed, 159 insertions(+), 39 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,6 +59,11 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.+ Cannot be combined with `--object-only`.++--object-only::+ List only names of the objects, one per line. Cannot be combined+ with `--name-only` or `--name-status`. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object
@@ -134,12 +194,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)LS_SHOW_TREES),OPT_SET_INT('z',NULL,&line_termination,N_("terminate entries with NUL byte"),0),-OPT_BIT('l',"long",&ls_options,N_("include object size"),-LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('l',"long",&cmdmode,N_("include object size"),+MODE_LONG),+OPT_CMDMODE(0,"name-only",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"name-status",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"object-only",&cmdmode,N_("list only objects"),+MODE_OBJECT_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
@@ -170,6 +232,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)if(get_oid(argv[0],&oid))die("Not a valid object name %s",argv[0]);+parse_shown_fields();+/**show_recursive()rollsitsownmatchingcodeandis*generallyignorantof'structpathspec'.Themagicmask
From: Teng Long <hidden> Date: 2022-01-01 13:51:25
"show_data" is a struct that packages the necessary fields for
reusing. This commit is a front-loaded commit for support
"--format" argument and does not affect any existing functionality.
Signed-off-by: Teng Long <redacted>
---
builtin/ls-tree.c | 47 +++++++++++++++++++++++++++++------------------
1 file changed, 29 insertions(+), 18 deletions(-)
From: Teng Long <hidden> Date: 2022-01-01 13:51:33
Add a --format option to ls-tree. It has an existing default output,
and then --long and --name-only options to emit the default output
along with the objectsize and, or to only emit object paths.
Rather than add --type-only, --object-only etc. we can just support a
--format using a strbuf_expand() similar to "for-each-ref
--format". We might still add such options in the future for
convenience.
The --format implementation is slower than the existing code, but this
change does not cause any performance regressions. We'll leave the
existing show_tree() unchanged, and only run show_tree_fmt() in if
a --format different than the hardcoded built-in ones corresponding to
the existing modes is provided.
I.e. something like the "--long" output would be much slower with
this, mainly due to how we need to allocate various things to do with
quote.c instead of spewing the output directly to stdout.
The new option of '--format' comes from Ævar Arnfjörð Bjarmasonn's
idea and suggestion, this commit makes modifications in terms of the
original discussion on community [1].
Here is the statistics about performance tests:
1. Default format (hitten the builtin formats):
"git ls-tree <tree-ish>" vs "--format='%(mode) %(type) %(object)%x09%(file)'"
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.2 ms ± 3.3 ms [User: 84.3 ms, System: 20.8 ms]
Range (min … max): 99.2 ms … 113.2 ms 28 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)' HEAD
Time (mean ± σ): 106.4 ms ± 2.7 ms [User: 86.1 ms, System: 20.2 ms]
Range (min … max): 100.2 ms … 110.5 ms 29 runs
2. Default format includes object size (hitten the builtin formats):
"git ls-tree -l <tree-ish>" vs "--format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'"
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 335.1 ms ± 6.5 ms [User: 304.6 ms, System: 30.4 ms]
Range (min … max): 327.5 ms … 348.4 ms 10 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD
Time (mean ± σ): 337.2 ms ± 8.2 ms [User: 309.2 ms, System: 27.9 ms]
Range (min … max): 328.8 ms … 349.4 ms 10 runs
Links:
[1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 50 ++++++++-
builtin/ls-tree.c | 191 ++++++++++++++++++++++++++++------
t/t3105-ls-tree-format.sh | 55 ++++++++++
3 files changed, 259 insertions(+), 37 deletions(-)
create mode 100755 t/t3105-ls-tree-format.sh
@@ -10,9 +10,9 @@ SYNOPSIS -------- [verse] 'git ls-tree' [-d] [-r] [-t] [-l] [-z]- [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]]- <tree-ish> [<path>...]-+ [--name-only] [--name-status] [--object-only]+ [--full-name] [--full-tree] [--abbrev[=<n>]]+ [--format=<format>] <tree-ish> [<path>...] DESCRIPTION ----------- Lists the contents of a given tree object, like what "/bin/ls -a" does
@@ -79,6 +79,16 @@ OPTIONS Do not limit the listing to the current working directory. Implies --full-name.+--format=<format>::+ A string that interpolates `%(fieldname)` from the result+ being shown. It also interpolates `%%` to `%`, and+ `%xx` where `xx`are hex digits interpolates to character+ with hex code `xx`; for example `%00` interpolates to+ `\0` (NUL), `%09` to `\t` (TAB) and `%0a` to `\n` (LF).+ When specified, `--format` cannot be combined with other+ format-altering options, including `--long`, `--name-only`+ and `--object-only`.+ [<path>...]:: When paths are given, show them (note that this isn't really raw pathnames, but rather a list of patterns to match). Otherwise
@@ -87,6 +97,9 @@ OPTIONS Output Format -------------++Default format:+ <mode> SP <type> SP <object> TAB <file> This output format is compatible with what `--index-info --stdin` of
@@ -105,6 +118,37 @@ quoted as explained for the configuration variable `core.quotePath` (see linkgit:git-config[1]). Using `-z` the filename is output verbatim and the line is terminated by a NUL byte.+Customized format:++It's support to print customized format by `%(fieldname)` with `--format` option.+For example, if you want to only print the <object> and <file> fields with a+JSON style, executing with a specific "--format" like++ git ls-tree --format='{"object":"%(object)", "file":"%(file)"}' <tree-ish>++The output format changes to:++ {"object":"<object>", "file":"<file>"}++FIELD NAMES+-----------++Various values from structured fields can be used to interpolate+into the resulting output. For each outputing line, the following+names can be used:++mode::+ The mode of the object.+type::+ The type of the object (`blob` or `tree`).+object::+ The name of the object.+size[:padded]::+ The size of the object ("-" if it's a tree).+ It also supports a padded format of size with "%(size:padded)".+file::+ The filename of the object.+ GIT --- Part of the linkgit:git[1] suite
@@ -56,23 +56,75 @@ enum {staticintcmdmode=MODE_UNSPECIFIED;-staticintparse_shown_fields(void)+staticconstchar*format;+staticconstchar*default_format="%(mode) %(type) %(object)%x09%(file)";+staticconstchar*long_format="%(mode) %(type) %(object) %(size:padded)%x09%(file)";+staticconstchar*name_only_format="%(file)";+staticconstchar*object_only_format="%(object)";++staticvoidexpand_objectsize(structstrbuf*line,conststructobject_id*oid,+constenumobject_typetype,unsignedintpadded){-if(cmdmode==MODE_NAME_ONLY){-shown_bits=SHOW_FILE_NAME;-return0;+if(type==OBJ_BLOB){+unsignedlongsize;+if(oid_object_info(the_repository,oid,&size)<0)+die(_("could not get object info about '%s'"),+oid_to_hex(oid));+if(padded)+strbuf_addf(line,"%7"PRIuMAX,(uintmax_t)size);+else+strbuf_addf(line,"%"PRIuMAX,(uintmax_t)size);+}elseif(padded){+strbuf_addf(line,"%7s","-");+}else{+strbuf_addstr(line,"-");}-if(cmdmode==MODE_OBJECT_ONLY){-shown_bits=SHOW_OBJECT_NAME;-return0;+}++staticsize_texpand_show_tree(structstrbuf*line,constchar*start,+void*context)+{+structshown_data*data=context;+constchar*end;+constchar*p;+unsignedinterrlen;+size_tlen;+len=strbuf_expand_literal_cb(line,start,NULL);+if(len)+returnlen;++if(*start!='(')+die(_("bad ls-tree format: as '%s'"),start);++end=strchr(start+1,')');+if(!end)+die(_("bad ls-tree format: element '%s' does not end in ')'"),start);++len=end-start+1;+if(skip_prefix(start,"(mode)",&p)){+strbuf_addf(line,"%06o",data->mode);+}elseif(skip_prefix(start,"(type)",&p)){+strbuf_addstr(line,type_name(data->type));+}elseif(skip_prefix(start,"(size:padded)",&p)){+expand_objectsize(line,data->oid,data->type,1);+}elseif(skip_prefix(start,"(size)",&p)){+expand_objectsize(line,data->oid,data->type,0);+}elseif(skip_prefix(start,"(object)",&p)){+strbuf_addstr(line,find_unique_abbrev(data->oid,abbrev));+}elseif(skip_prefix(start,"(file)",&p)){+constchar*name=data->base->buf;+constchar*prefix=chomp_prefix?ls_tree_prefix:NULL;+structstrbufquoted=STRBUF_INIT;+structstrbufsb=STRBUF_INIT;+strbuf_addstr(data->base,data->pathname);+name=relative_path(data->base->buf,prefix,&sb);+quote_c_style(name,"ed,NULL,0);+strbuf_addstr(line,quoted.buf);+}else{+errlen=(unsignedlong)len;+die(_("bad ls-tree format: %%%.*s"),errlen,start);}-if(!ls_options||(ls_options&LS_RECURSIVE)-||(ls_options&LS_SHOW_TREES)-||(ls_options&LS_TREE_ONLY))-shown_bits=SHOW_DEFAULT;-if(cmdmode==MODE_LONG)-shown_bits=SHOW_DEFAULT|SHOW_SIZE;-return1;+returnlen;}staticintshow_recursive(constchar*base,size_tbaselen,
@@ -137,24 +258,6 @@ static int show_default(struct shown_data *data)return1;}-staticintshow_tree_init(enumobject_type*type,structstrbuf*base,-constchar*pathname,unsignedmode,int*retval)-{-if(S_ISGITLINK(mode)){-*type=OBJ_COMMIT;-}elseif(S_ISDIR(mode)){-if(show_recursive(base->buf,base->len,pathname)){-*retval=READ_TREE_RECURSIVE;-if(!(ls_options&LS_SHOW_TREES))-return1;-}-*type=OBJ_TREE;-}-elseif(ls_options&LS_TREE_ONLY)-return1;-return0;-}-staticintshow_tree(conststructobject_id*oid,structstrbuf*base,constchar*pathname,unsignedmode,void*context){
@@ -196,6 +299,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)structobject_idoid;structtree*tree;inti,full_tree=0;+read_tree_fn_tfn=show_tree;conststructoptionls_tree_options[]={OPT_BIT('d',NULL,&ls_options,N_("only show trees"),LS_TREE_ONLY),
@@ -218,6 +322,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)OPT_BOOL(0,"full-tree",&full_tree,N_("list entire tree; not just current directory ""(implies --full-name)")),+OPT_STRING_F(0,"format",&format,N_("format"),+N_("format to use for the output"),+PARSE_OPT_NONEG),OPT__ABBREV(&abbrev),OPT_END()};
@@ -238,6 +345,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)((LS_TREE_ONLY|LS_RECURSIVE)&ls_options))ls_options|=LS_SHOW_TREES;+if(format&&cmdmode)+usage_msg_opt(+_("--format can't be combined with other format-altering options"),+ls_tree_usage,ls_tree_options);if(argc<1)usage_with_options(ls_tree_usage,ls_tree_options);if(get_oid(argv[0],&oid))
@@ -261,6 +372,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)tree=parse_tree_indirect(&oid);if(!tree)die("not a tree object");-return!!read_tree(the_repository,tree,-&pathspec,show_tree,NULL);++/*+*Thegenericshow_tree_fmt()isslowerthanshow_tree(),so+*takethefastpathifpossible.+*/+if(format&&(!strcmp(format,default_format)||+!strcmp(format,long_format)||+!strcmp(format,name_only_format)||+!strcmp(format,object_only_format)))+fn=show_tree;+elseif(format)+fn=show_tree_fmt;++return!!read_tree(the_repository,tree,&pathspec,fn,NULL);}
From: Junio C Hamano <hidden> Date: 2022-01-04 01:21:08
Teng Long [off-list ref] writes:
A simple refactoring was done to the "show_tree" function, intead by
using bitwise operations to recognize the format for printing to
stdout. The reason for doing this is that we don't want to increase
the readability difficulty with the addition of "-object-only",
making this part of the logic easier to read and expand.
The resulting code looks unnecessarily complex and brittle; some
SHOW_FOO mean SHOW_FOO_ONLY_AND_NOTHING_ELSE while other SHOW_BAR
means SHOW_BAR_BUT_WE_MAY_SHOW_OTHER_THINGS_IN_LATER_PART, and the
distinction is not clear from their names (which means it is hard
to later extend and enhance the behaviour of the code).
+ if (!(shown_bits ^ SHOW_FILE_NAME)) {
Is the use of XOR operator significant here?
I.e. "if (shown_bits & SHOW_FILE_NAME)" would have been a much more
natural way to guard "this is a block that shows the file name",
than "the result MUST BE all bits off if we flip SHOW_FILE_NAME bit
off". If various SHOW_FOO bits are meant to be mutually exclusive,
then "if ((shown_bits & SHOW_FILE_NAME) == SHOW_FILE_NAME)" would
also make sense, but as I said upfront, it is unclear to me if
shown_bits are meant to be a collection of "this bit means this
field is shown (and it implies nothing else)", so I dunno.
Don't we need some comment that explains what the function does,
what its return value means, etc.?
{
- int retval = 0;
- size_t baselen;
- enum object_type type = OBJ_BLOB;
-
if (S_ISGITLINK(mode)) {
- type = OBJ_COMMIT;
+ *type = OBJ_COMMIT;
} else if (S_ISDIR(mode)) {
if (show_recursive(base->buf, base->len, pathname)) {
- retval = READ_TREE_RECURSIVE;
+ *retval = READ_TREE_RECURSIVE;
if (!(ls_options & LS_SHOW_TREES))
- return retval;
+ return 1;
}
- type = OBJ_TREE;
+ *type = OBJ_TREE;
}
else if (ls_options & LS_TREE_ONLY)
- return 0;
+ return 1;
+ return 0;
+}
It seems that even from its returned value, the caller cannot tell
if *retval was set by the function or not. Perhaps it makes a much
cleaner API to assign 0 to *retval at the beginning of this function,
just like the original did so anyway? ...
From: Teng Long <hidden> Date: 2022-01-04 07:30:12
Junio C Hamano [off-list ref] writes:
The resulting code looks unnecessarily complex and brittle; some
SHOW_FOO mean SHOW_FOO_ONLY_AND_NOTHING_ELSE while other SHOW_BAR
means SHOW_BAR_BUT_WE_MAY_SHOW_OTHER_THINGS_IN_LATER_PART, and the
distinction is not clear from their names (which means it is hard
to later extend and enhance the behaviour of the code).
I agree with you that the relevant code is not very clear, So I
think I will take these steps:
1. Rename "shown_bits" -> "shown_fields"
essentially we want to show the fields but not bits to user that
may firstly solve the unclear nameing problem for "shown_bits"
itself.
2. Rename related macro definitions of "shown_fields by :
SHOW_FILE_NAME -> FILE_NAME_FIELD
SHOW_SIZE -> SIZE_FIELD
SHOW_OBJECT_NAME -> OBJECT_NAME_FIELD
SHOW_TYPE -> TYPE_FIELD
SHOW_DEFAULT -> DEFAULT_FIELDS
I think the confusion comes from " SHOW_FOO_ONLY_AND_NOTHING_ELSE"
and " SHOW_BAR_BUT_WE_MAY_SHOW_OTHER_THINGS_IN_LATER_PART" is
because some macros's is named by mixed the "flags" and "the
operation of flags" together.
So with renamings, we try to unify these definitions meaning,
they are just used for defining a "field" with a specified
non-repetitive bits.
After that, you can show many "fields" by combining any of "fields",
such as what the builtin "DEFAULT_FIELDS" does, it shows all the
fields but except the "size" field.
By far, the "field(s)" only means the definition themselfs, and
no business with "which one/ones" or "how" to shown.
3. Decide "which" fields need to show
The definition of "WHICH_FIELDS_TO_SHOWN" is by "shown_fields",
it's used for parse from the options and compute it's value
by function "parse_shown_fields()".
Actually, the function already represent what work it do,
but the problem is I didn't notice to rename "show_bits"
to "show_fields" before. This may bring some confusion,
because we are not going to show the bits but the field(s).
So, I will do the <STEP.1>.
4. Decide "How" fields to be shown
Now we have already know the field(s) we care about, next
step is to show the fields.
quoted
+ if (!(shown_bits ^ SHOW_FILE_NAME)) {
Is the use of XOR operator significant here?
I.e. "if (shown_bits & SHOW_FILE_NAME)" would have been a much more
natural way to guard "this is a block that shows the file name",
than "the result MUST BE all bits off if we flip SHOW_FILE_NAME bit
off". If various SHOW_FOO bits are meant to be mutually exclusive,
then "if ((shown_bits & SHOW_FILE_NAME) == SHOW_FILE_NAME)" would
also make sense, but as I said upfront, it is unclear to me if
shown_bits are meant to be a collection of "this bit means this
field is shown (and it implies nothing else)", so I dunno.
Not significant. Both work and It's all right for me. Your
readability is better, and now I know how to handle this
situation better.
E.g, if we only want to show a "filename" field
(with `--name-only`), we will use a way like
"if ((shown_fields & FILE_NAME_FIELD) == FILE_NAME_FIELD)"
to judge this situation.
And if we want to show fields in a builtin way
(as described in 'git-ls-tree.txt', default output format
is compatible with what `--index-info --stdin` of
'git update-index' expects.), we will use "DEFAULT_FIELDS"
instead.
I am not sure if this solves the problem you are considering as I
may misunderstand, I can quickly finish this new patch and we can
look at it then。
Thanks.
From: Teng Long <hidden> Date: 2022-01-04 09:49:52
Junio C Hamano writes:
Don't we need some comment that explains what the function does,
what its return value means, etc.?
It seems that even from its returned value, the caller cannot tell
if *retval was set by the function or not. Perhaps it makes a much
cleaner API to assign 0 to *retval at the beginning of this function,
just like the original did so anyway? ...
Oh, sorry for that, I did not notice the "retval" before because the
naming is unimpressive and the tests were passed, though...
I just looked at it, actually, it's important, not as what it is named, it
affects the result. The "retval" actually determine whether to
CONTINUE reading the current "tree" or BREAK into the next
one [1] .
So, I think this commit should be modified despite the tests are passed,
firstly, I want to rename "retval" to another name that makes sense,
then just make the relevant "if" and "return" logic more clearly with the
newname, finally, it'll be consistent with the definitions in "read_tree_at()"
at "tree.c" [1].
[1] https://github.com/dyrone/git/blob/master/tree.c#L40
Thanks.
Junio C Hamano [off-list ref] 于2022年1月4日周二 10:06写道:
Don't we need some comment that explains what the function does,
what its return value means, etc.?
quoted
{
- int retval = 0;
- size_t baselen;
- enum object_type type = OBJ_BLOB;
-
if (S_ISGITLINK(mode)) {
- type = OBJ_COMMIT;
+ *type = OBJ_COMMIT;
} else if (S_ISDIR(mode)) {
if (show_recursive(base->buf, base->len, pathname)) {
- retval = READ_TREE_RECURSIVE;
+ *retval = READ_TREE_RECURSIVE;
if (!(ls_options & LS_SHOW_TREES))
- return retval;
+ return 1;
}
- type = OBJ_TREE;
+ *type = OBJ_TREE;
}
else if (ls_options & LS_TREE_ONLY)
- return 0;
+ return 1;
+ return 0;
+}
It seems that even from its returned value, the caller cannot tell
if *retval was set by the function or not. Perhaps it makes a much
cleaner API to assign 0 to *retval at the beginning of this function,
just like the original did so anyway? ...
@@ -56,23 +56,75 @@ enum {staticintcmdmode=MODE_UNSPECIFIED;-staticintparse_shown_fields(void)+staticconstchar*format;+staticconstchar*default_format="%(mode) %(type) %(object)%x09%(file)";+staticconstchar*long_format="%(mode) %(type) %(object) %(size:padded)%x09%(file)";+staticconstchar*name_only_format="%(file)";+staticconstchar*object_only_format="%(object)";++staticvoidexpand_objectsize(structstrbuf*line,conststructobject_id*oid,+constenumobject_typetype,unsignedintpadded){-if(cmdmode==MODE_NAME_ONLY){-shown_bits=SHOW_FILE_NAME;-return0;+if(type==OBJ_BLOB){+unsignedlongsize;+if(oid_object_info(the_repository,oid,&size)<0)+die(_("could not get object info about '%s'"),+oid_to_hex(oid));+if(padded)+strbuf_addf(line,"%7"PRIuMAX,(uintmax_t)size);+else+strbuf_addf(line,"%"PRIuMAX,(uintmax_t)size);+}elseif(padded){+strbuf_addf(line,"%7s","-");
This, along with two other similar instances, triggers the
`static-analysis` job in the CI failure of `seen`. The suggested diff is:
-- snip --
But I think that the first hunk indicates a deeper issue, as `%7s`
probably meant to pad the dash to seven dashes (which that format won't
accomplish, but `strbuf_addchars()` would)?
Ciao,
Dscho
@@ -137,24 +258,6 @@ static int show_default(struct shown_data *data) return 1; }-static int show_tree_init(enum object_type *type, struct strbuf *base,- const char *pathname, unsigned mode, int *retval)-{- if (S_ISGITLINK(mode)) {- *type = OBJ_COMMIT;- } else if (S_ISDIR(mode)) {- if (show_recursive(base->buf, base->len, pathname)) {- *retval = READ_TREE_RECURSIVE;- if (!(ls_options & LS_SHOW_TREES))- return 1;- }- *type = OBJ_TREE;- }- else if (ls_options & LS_TREE_ONLY)- return 1;- return 0;-}- static int show_tree(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, void *context) {
@@ -196,6 +299,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) struct object_id oid; struct tree *tree; int i, full_tree = 0;+ read_tree_fn_t fn = show_tree; const struct option ls_tree_options[] = { OPT_BIT('d', NULL, &ls_options, N_("only show trees"), LS_TREE_ONLY),
@@ -218,6 +322,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "full-tree", &full_tree, N_("list entire tree; not just current directory " "(implies --full-name)")),+ OPT_STRING_F(0, "format", &format, N_("format"),+ N_("format to use for the output"),+ PARSE_OPT_NONEG), OPT__ABBREV(&abbrev), OPT_END() };
@@ -238,6 +345,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options)) ls_options |= LS_SHOW_TREES;+ if (format && cmdmode)+ usage_msg_opt(+ _("--format can't be combined with other format-altering options"),+ ls_tree_usage, ls_tree_options); if (argc < 1) usage_with_options(ls_tree_usage, ls_tree_options); if (get_oid(argv[0], &oid))
@@ -261,6 +372,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) tree = parse_tree_indirect(&oid); if (!tree) die("not a tree object");- return !!read_tree(the_repository, tree,- &pathspec, show_tree, NULL);++ /*+ * The generic show_tree_fmt() is slower than show_tree(), so+ * take the fast path if possible.+ */+ if (format && (!strcmp(format, default_format) ||+ !strcmp(format, long_format) ||+ !strcmp(format, name_only_format) ||+ !strcmp(format, object_only_format)))+ fn = show_tree;+ else if (format)+ fn = show_tree_fmt;++ return !!read_tree(the_repository, tree, &pathspec, fn, NULL); }
But I think that the first hunk indicates a deeper issue, as `%7s`
probably meant to pad the dash to seven dashes (which that format won't
accomplish, but `strbuf_addchars()` would)?
"strbuf_addf(line, "%7s", "-");" here is used to align the columns
with a width of
seven chars, not repeat one DASH to seven.
A little weird about the fix recommendation of "strbuf_addstr(line, "-");" ,
because it will only add a single DASH here.
It's the identical result which compares to the "master"[1] I think with the
current codes and I tested the "strbuf_addf()" simply and it seems to work
fine.
[1] https://github.com/git/git/blob/master/builtin/ls-tree.c#L106
Thanks.
Johannes Schindelin [off-list ref] 于2022年1月4日周二 22:38写道:
@@ -56,23 +56,75 @@ enum {staticintcmdmode=MODE_UNSPECIFIED;-staticintparse_shown_fields(void)+staticconstchar*format;+staticconstchar*default_format="%(mode) %(type) %(object)%x09%(file)";+staticconstchar*long_format="%(mode) %(type) %(object) %(size:padded)%x09%(file)";+staticconstchar*name_only_format="%(file)";+staticconstchar*object_only_format="%(object)";++staticvoidexpand_objectsize(structstrbuf*line,conststructobject_id*oid,+constenumobject_typetype,unsignedintpadded){-if(cmdmode==MODE_NAME_ONLY){-shown_bits=SHOW_FILE_NAME;-return0;+if(type==OBJ_BLOB){+unsignedlongsize;+if(oid_object_info(the_repository,oid,&size)<0)+die(_("could not get object info about '%s'"),+oid_to_hex(oid));+if(padded)+strbuf_addf(line,"%7"PRIuMAX,(uintmax_t)size);+else+strbuf_addf(line,"%"PRIuMAX,(uintmax_t)size);+}elseif(padded){+strbuf_addf(line,"%7s","-");
This, along with two other similar instances, triggers the
`static-analysis` job in the CI failure of `seen`. The suggested diff is:
-- snip --
But I think that the first hunk indicates a deeper issue, as `%7s`
probably meant to pad the dash to seven dashes (which that format won't
accomplish, but `strbuf_addchars()` would)?
Ciao,
Dscho
@@ -137,24 +258,6 @@ static int show_default(struct shown_data *data) return 1; }-static int show_tree_init(enum object_type *type, struct strbuf *base,- const char *pathname, unsigned mode, int *retval)-{- if (S_ISGITLINK(mode)) {- *type = OBJ_COMMIT;- } else if (S_ISDIR(mode)) {- if (show_recursive(base->buf, base->len, pathname)) {- *retval = READ_TREE_RECURSIVE;- if (!(ls_options & LS_SHOW_TREES))- return 1;- }- *type = OBJ_TREE;- }- else if (ls_options & LS_TREE_ONLY)- return 1;- return 0;-}- static int show_tree(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, void *context) {
@@ -196,6 +299,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) struct object_id oid; struct tree *tree; int i, full_tree = 0;+ read_tree_fn_t fn = show_tree; const struct option ls_tree_options[] = { OPT_BIT('d', NULL, &ls_options, N_("only show trees"), LS_TREE_ONLY),
@@ -218,6 +322,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "full-tree", &full_tree, N_("list entire tree; not just current directory " "(implies --full-name)")),+ OPT_STRING_F(0, "format", &format, N_("format"),+ N_("format to use for the output"),+ PARSE_OPT_NONEG), OPT__ABBREV(&abbrev), OPT_END() };
@@ -238,6 +345,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options)) ls_options |= LS_SHOW_TREES;+ if (format && cmdmode)+ usage_msg_opt(+ _("--format can't be combined with other format-altering options"),+ ls_tree_usage, ls_tree_options); if (argc < 1) usage_with_options(ls_tree_usage, ls_tree_options); if (get_oid(argv[0], &oid))
@@ -261,6 +372,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) tree = parse_tree_indirect(&oid); if (!tree) die("not a tree object");- return !!read_tree(the_repository, tree,- &pathspec, show_tree, NULL);++ /*+ * The generic show_tree_fmt() is slower than show_tree(), so+ * take the fast path if possible.+ */+ if (format && (!strcmp(format, default_format) ||+ !strcmp(format, long_format) ||+ !strcmp(format, name_only_format) ||+ !strcmp(format, object_only_format)))+ fn = show_tree;+ else if (format)+ fn = show_tree_fmt;++ return !!read_tree(the_repository, tree, &pathspec, fn, NULL); }
But I think that the first hunk indicates a deeper issue, as `%7s`
probably meant to pad the dash to seven dashes (which that format won't
accomplish, but `strbuf_addchars()` would)?
"strbuf_addf(line, "%7s", "-");" here is used to align the columns
with a width of seven chars, not repeat one DASH to seven.
Ah. I misremembered and thought that `"% 7s"` would do that, but you're
correct. See below for more on this.
But first, I wonder why the test suite passes with the `strbuf_addstr()`
call... Is this line not covered by any test case?
About the `%7s` thing: The most obvious resolution is to use `" -"`
with `strbuf_addstr()`. And I would argue that this is the best
resolution.
If you disagree (and want to spin up a full `sprintf()` every time, just
to add those six space characters), feel free to integrate the following
into your patch series:
-- snip --
From a390fcf7eec261c7f0e341bda79f2b1f326d151e Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <redacted>
Date: Wed, 5 Jan 2022 14:02:19 +0100
Subject: [PATCH] cocci: allow padding with `strbuf_addf()`
A convenient way to pad strings is to use something like
`strbuf_addf(&buf, "%20s", "Hello, world!")`.
However, the Coccinelle rule that forbids a format `"%s"` with a
constant string argument cast too wide a net, and also forbade such
padding.
Let's be a bit stricter in that Coccinelle rule.
Signed-off-by: Johannes Schindelin <redacted>
---
contrib/coccinelle/strbuf.cocci | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
A little weird about the fix recommendation of "strbuf_addstr(line, "-");" ,
because it will only add a single DASH here.
It's the identical result which compares to the "master"[1] I think with the
current codes and I tested the "strbuf_addf()" simply and it seems to work
fine.
[1] https://github.com/git/git/blob/master/builtin/ls-tree.c#L106
From: Teng Long <hidden> Date: 2022-01-05 16:45:15
On Wed, Jan 5, 2022 at 9:09 PM Johannes Schindelin
[off-list ref] wrote:
Ah. I misremembered and thought that `"% 7s"` would do that, but you're
correct. See below for more on this.
But first, I wonder why the test suite passes with the `strbuf_addstr()`
call... Is this line not covered by any test case?
Definitely, me too.
About the `%7s` thing: The most obvious resolution is to use `" -"`
with `strbuf_addstr()`. And I would argue that this is the best
resolution.
I agree that's a quick fix in that way.
Can you feed me more info about why you think it's the best
resolution?
quoted hunk
If you disagree (and want to spin up a full `sprintf()` every time, just
to add those six space characters), feel free to integrate the following
into your patch series:
-- snip --
From a390fcf7eec261c7f0e341bda79f2b1f326d151e Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <redacted>
Date: Wed, 5 Jan 2022 14:02:19 +0100
Subject: [PATCH] cocci: allow padding with `strbuf_addf()`
A convenient way to pad strings is to use something like
`strbuf_addf(&buf, "%20s", "Hello, world!")`.
However, the Coccinelle rule that forbids a format `"%s"` with a
constant string argument cast too wide a net, and also forbade such
padding.
Let's be a bit stricter in that Coccinelle rule.
Signed-off-by: Johannes Schindelin <redacted>
---
contrib/coccinelle/strbuf.cocci | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
I appreciate the input of 'coccinelle' and the commit.
The current relevant rules of 'strbuf' was added in commit [1], the
purpose of it
seems like to forbid some inefficient use cases and chase the performance
profit as much as possible.
I think "<SP*6>-" and "%7s", they both with the same result, the former
benefits in performance, the later benefits in readability. So let's do a simple
performance test under "linux", then think about which is better for this case:
Benchmark 1: /opt/git/ls-tree-oid-only-addf/bin/git ls-tree -r
--format='> %(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD
Time (mean ± σ): 387.7 ms ± 8.8 ms [User: 357.6 ms,
System: 30.0 ms]
Range (min … max): 377.5 ms … 399.5 ms 10 runs
Benchmark 1: /opt/git/ls-tree-oid-only-addstr/bin/git ls-tree -r
--format='> %(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD
Time (mean ± σ): 388.9 ms ± 9.0 ms [User: 362.7 ms,
System: 26.1 ms]
Range (min … max): 373.4 ms … 399.8 ms 10 runs
It's with a slight performance difference between the two.
So, I decided to integrate your patch as a new commit in the current
patchset and
is it ok for me to mention it's from your guidance in the commit message or
a "helped-by" something like this?
Thanks.
[1] https://github.com/git/git/commit/28c23cd4c3902449aff72cb9a4a703220be0d6ac
From: Teng Long <hidden> Date: 2022-01-06 04:31:51
From: Ævar Arnfjörð Bjarmason <redacted>
Remove code added in f35a6d3bce7 (Teach core object handling functions
about gitlinks, 2007-04-09), later patched in 7d0b18a4da1 (Add output
flushing before fork(), 2008-08-04), and then finally ending up in its
current form in d3bee161fef (tree.c: allow read_tree_recursive() to
traverse gitlink entries, 2009-01-25). All while being commented-out!
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 9 ---------
1 file changed, 9 deletions(-)
From: Teng Long <hidden> Date: 2022-01-06 04:32:00
From: Ævar Arnfjörð Bjarmason <redacted>
Change the ls-tree.c code to use type_name() on the enum instead of
using the string constants. This doesn't matter either way for
performance, but makes this a bit easier to read as we'll no longer
need a strcmp() here.
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
From: Teng Long <hidden> Date: 2022-01-06 04:32:03
From: Ævar Arnfjörð Bjarmason <redacted>
The "struct strbuf"'s "len" member is a "size_t", not an "int", so
let's change our corresponding types accordingly. This also changes
the "len" and "speclen" variables, which are likewise used to store
the return value of strlen(), which returns "size_t", not "int".
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
builtin/ls-tree.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
From: Teng Long <hidden> Date: 2022-01-06 04:32:09
The variable which "show_tree()" return is named "retval", a name that's
a little hard to understand. This commit tries to make the variable
and the related codes more clear in the context.
The change is based on three steps. The first is to rename "retval" to
a more meaningful name.
The second is that there are different "return" cases in "show_tree",
some places use "return retval;", some just directly use "return 0;",
this maybe cause some confusion when reading these "returns". For this
, we change all the "return" cases to the new uniform name.
The last is there are some nested "if" judgments surround the "returns",
this even make the codes here a little hard to understand. So we put
some logic in individual methods, "init_type()" and "init_recursive()".
After the steps, let us look at "show_tree()" again. It has a uniform
return variable name now, and first we init the "type" by "mode", then
call "init_recursive" to init the value of "recursive" which means
whether to go on reading recusively into the "tree". The codes here
become a little bit clearer, so we do not need to take a look at
"read_tree_at()" in "tree.c" to make sure the context of the return
value.
Signed-off-by: Teng Long <redacted>
---
builtin/ls-tree.c | 38 ++++++++++++++++++++++++--------------
1 file changed, 24 insertions(+), 14 deletions(-)
From: Teng Long <hidden> Date: 2022-01-06 04:32:13
We usually pipe the output from `git ls-trees` to tools like
`sed` or `cut` when we only want to extract some fields.
When we want only the pathname component, we can pass
`--name-only` option to omit such a pipeline, but there are no
options for extracting other fields.
Teach the "--object-only" option to the command to only show the
object name. This option cannot be used together with
"--name-only" or "--long" , they are mutually exclusive (actually
"--name-only" and "--long" can be combined together before, this
commit by the way fix this bug).
A simple refactoring was done to the "show_tree" function, intead by
using bitwise operations to recognize the format for printing to
stdout. The reason for doing this is that we don't want to increase
the readability difficulty with the addition of "-object-only",
making this part of the logic easier to read and expand.
In terms of performance, there is no loss comparing to the
"master" (2ae0a9cb8298185a94e5998086f380a355dd8907), here are the
results of the performance tests in my environment based on linux
repository:
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.8 ms ± 2.7 ms [User: 85.7 ms, System: 20.0 ms]
Range (min … max): 101.5 ms … 111.3 ms 28 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.0 ms ± 3.0 ms [User: 83.7 ms, System: 21.2 ms]
Range (min … max): 99.3 ms … 109.5 ms 27 runs
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 337.4 ms ± 10.9 ms [User: 308.3 ms, System: 29.0 ms]
Range (min … max): 323.0 ms … 355.0 ms 10 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 337.6 ms ± 6.2 ms [User: 309.4 ms, System: 28.1 ms]
Range (min … max): 330.4 ms … 349.9 ms 10 runs
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 7 +-
builtin/ls-tree.c | 141 +++++++++++++++++++++++++---------
t/t3104-ls-tree-oid.sh | 51 ++++++++++++
3 files changed, 160 insertions(+), 39 deletions(-)
create mode 100755 t/t3104-ls-tree-oid.sh
@@ -59,6 +59,11 @@ OPTIONS --name-only:: --name-status:: List only filenames (instead of the "long" output), one per line.+ Cannot be combined with `--object-only`.++--object-only::+ List only names of the objects, one per line. Cannot be combined+ with `--name-only` or `--name-status`. --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object
@@ -136,12 +197,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)LS_SHOW_TREES),OPT_SET_INT('z',NULL,&line_termination,N_("terminate entries with NUL byte"),0),-OPT_BIT('l',"long",&ls_options,N_("include object size"),-LS_SHOW_SIZE),-OPT_BIT(0,"name-only",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),-OPT_BIT(0,"name-status",&ls_options,N_("list only filenames"),-LS_NAME_ONLY),+OPT_CMDMODE('l',"long",&cmdmode,N_("include object size"),+MODE_LONG),+OPT_CMDMODE(0,"name-only",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"name-status",&cmdmode,N_("list only filenames"),+MODE_NAME_ONLY),+OPT_CMDMODE(0,"object-only",&cmdmode,N_("list only objects"),+MODE_OBJECT_ONLY),OPT_SET_INT(0,"full-name",&chomp_prefix,N_("use full path names"),0),OPT_BOOL(0,"full-tree",&full_tree,
@@ -172,6 +235,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)if(get_oid(argv[0],&oid))die("Not a valid object name %s",argv[0]);+parse_shown_fields();+/**show_recursive()rollsitsownmatchingcodeandis*generallyignorantof'structpathspec'.Themagicmask
From: Teng Long <hidden> Date: 2022-01-06 04:32:14
"show_tree_data" is a struct that packages the necessary fields for
"show_tree()". This commit is a pre-prepared commit for supporting
"--format" option and it does not affect any existing functionality.
Signed-off-by: Teng Long <redacted>
---
builtin/ls-tree.c | 50 +++++++++++++++++++++++++++++------------------
1 file changed, 31 insertions(+), 19 deletions(-)
From: Teng Long <hidden> Date: 2022-01-06 04:32:22
Add a --format option to ls-tree. It has an existing default output,
and then --long and --name-only options to emit the default output
along with the objectsize and, or to only emit object paths.
Rather than add --type-only, --object-only etc. we can just support a
--format using a strbuf_expand() similar to "for-each-ref
--format". We might still add such options in the future for
convenience.
The --format implementation is slower than the existing code, but this
change does not cause any performance regressions. We'll leave the
existing show_tree() unchanged, and only run show_tree_fmt() in if
a --format different than the hardcoded built-in ones corresponding to
the existing modes is provided.
I.e. something like the "--long" output would be much slower with
this, mainly due to how we need to allocate various things to do with
quote.c instead of spewing the output directly to stdout.
The new option of '--format' comes from Ævar Arnfjörð Bjarmasonn's
idea and suggestion, this commit makes modifications in terms of the
original discussion on community [1].
Here is the statistics about performance tests:
1. Default format (hitten the builtin formats):
"git ls-tree <tree-ish>" vs "--format='%(mode) %(type) %(object)%x09%(file)'"
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD
Time (mean ± σ): 105.2 ms ± 3.3 ms [User: 84.3 ms, System: 20.8 ms]
Range (min … max): 99.2 ms … 113.2 ms 28 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)' HEAD
Time (mean ± σ): 106.4 ms ± 2.7 ms [User: 86.1 ms, System: 20.2 ms]
Range (min … max): 100.2 ms … 110.5 ms 29 runs
2. Default format includes object size (hitten the builtin formats):
"git ls-tree -l <tree-ish>" vs "--format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'"
$hyperfine --warmup=10 "/opt/git/master/bin/git ls-tree -r -l HEAD"
Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD
Time (mean ± σ): 335.1 ms ± 6.5 ms [User: 304.6 ms, System: 30.4 ms]
Range (min … max): 327.5 ms … 348.4 ms 10 runs
$hyperfine --warmup=10 "/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD"
Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)' HEAD
Time (mean ± σ): 337.2 ms ± 8.2 ms [User: 309.2 ms, System: 27.9 ms]
Range (min … max): 328.8 ms … 349.4 ms 10 runs
Links:
[1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/
Signed-off-by: Teng Long <redacted>
---
Documentation/git-ls-tree.txt | 50 ++++++++++-
builtin/ls-tree.c | 158 ++++++++++++++++++++++++++++++----
t/t3105-ls-tree-format.sh | 55 ++++++++++++
3 files changed, 243 insertions(+), 20 deletions(-)
create mode 100755 t/t3105-ls-tree-format.sh
@@ -10,9 +10,9 @@ SYNOPSIS -------- [verse] 'git ls-tree' [-d] [-r] [-t] [-l] [-z]- [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]]- <tree-ish> [<path>...]-+ [--name-only] [--name-status] [--object-only]+ [--full-name] [--full-tree] [--abbrev[=<n>]]+ [--format=<format>] <tree-ish> [<path>...] DESCRIPTION ----------- Lists the contents of a given tree object, like what "/bin/ls -a" does
@@ -79,6 +79,16 @@ OPTIONS Do not limit the listing to the current working directory. Implies --full-name.+--format=<format>::+ A string that interpolates `%(fieldname)` from the result+ being shown. It also interpolates `%%` to `%`, and+ `%xx` where `xx`are hex digits interpolates to character+ with hex code `xx`; for example `%00` interpolates to+ `\0` (NUL), `%09` to `\t` (TAB) and `%0a` to `\n` (LF).+ When specified, `--format` cannot be combined with other+ format-altering options, including `--long`, `--name-only`+ and `--object-only`.+ [<path>...]:: When paths are given, show them (note that this isn't really raw pathnames, but rather a list of patterns to match). Otherwise
@@ -87,6 +97,9 @@ OPTIONS Output Format -------------++Default format:+ <mode> SP <type> SP <object> TAB <file> This output format is compatible with what `--index-info --stdin` of
@@ -105,6 +118,37 @@ quoted as explained for the configuration variable `core.quotePath` (see linkgit:git-config[1]). Using `-z` the filename is output verbatim and the line is terminated by a NUL byte.+Customized format:++It's support to print customized format by `%(fieldname)` with `--format` option.+For example, if you want to only print the <object> and <file> fields with a+JSON style, executing with a specific "--format" like++ git ls-tree --format='{"object":"%(object)", "file":"%(file)"}' <tree-ish>++The output format changes to:++ {"object":"<object>", "file":"<file>"}++FIELD NAMES+-----------++Various values from structured fields can be used to interpolate+into the resulting output. For each outputing line, the following+names can be used:++mode::+ The mode of the object.+type::+ The type of the object (`blob` or `tree`).+object::+ The name of the object.+size[:padded]::+ The size of the object ("-" if it's a tree).+ It also supports a padded format of size with "%(size:padded)".+file::+ The filename of the object.+ GIT --- Part of the linkgit:git[1] suite
@@ -76,6 +82,72 @@ static int parse_shown_fields(void)return1;}+staticvoidexpand_objectsize(structstrbuf*line,conststructobject_id*oid,+constenumobject_typetype,unsignedintpadded)+{+if(type==OBJ_BLOB){+unsignedlongsize;+if(oid_object_info(the_repository,oid,&size)<0)+die(_("could not get object info about '%s'"),+oid_to_hex(oid));+if(padded)+strbuf_addf(line,"%7"PRIuMAX,(uintmax_t)size);+else+strbuf_addf(line,"%"PRIuMAX,(uintmax_t)size);+}elseif(padded){+strbuf_addf(line,"%7s","-");+}else{+strbuf_addstr(line,"-");+}+}++staticsize_texpand_show_tree(structstrbuf*line,constchar*start,+void*context)+{+structshow_tree_data*data=context;+constchar*end;+constchar*p;+unsignedinterrlen;+size_tlen=strbuf_expand_literal_cb(line,start,NULL);++if(len)+returnlen;+if(*start!='(')+die(_("bad ls-tree format: as '%s'"),start);++end=strchr(start+1,')');+if(!end)+die(_("bad ls-tree format: element '%s' does not end in ')'"),start);++len=end-start+1;+if(skip_prefix(start,"(mode)",&p)){+strbuf_addf(line,"%06o",data->mode);+}elseif(skip_prefix(start,"(type)",&p)){+strbuf_addstr(line,type_name(data->type));+}elseif(skip_prefix(start,"(size:padded)",&p)){+expand_objectsize(line,data->oid,data->type,1);+}elseif(skip_prefix(start,"(size)",&p)){+expand_objectsize(line,data->oid,data->type,0);+}elseif(skip_prefix(start,"(object)",&p)){+strbuf_add_unique_abbrev(line,data->oid,abbrev);+}elseif(skip_prefix(start,"(file)",&p)){+constchar*name=data->base->buf;+constchar*prefix=chomp_prefix?ls_tree_prefix:NULL;+structstrbufquoted=STRBUF_INIT;+structstrbufsb=STRBUF_INIT;+strbuf_addstr(data->base,data->pathname);+name=relative_path(data->base->buf,prefix,&sb);+quote_c_style(name,"ed,NULL,0);+strbuf_addbuf(line,"ed);+strbuf_release(&sb);+strbuf_release("ed);+}else{+errlen=(unsignedlong)len;+die(_("bad ls-tree format: %%%.*s"),errlen,start);+}+returnlen;+}+staticintshow_recursive(constchar*base,size_tbaselen,constchar*pathname){
@@ -138,21 +256,6 @@ static int show_default(struct show_tree_data *data)return1;}-staticvoidinit_type(unsignedmode,enumobject_type*type)-{-if(S_ISGITLINK(mode))-*type=OBJ_COMMIT;-elseif(S_ISDIR(mode))-*type=OBJ_TREE;-}--staticvoidinit_recursive(structstrbuf*base,constchar*pathname,-int*recursive)-{-if(show_recursive(base->buf,base->len,pathname))-*recursive=READ_TREE_RECURSIVE;-}-staticintshow_tree(conststructobject_id*oid,structstrbuf*base,constchar*pathname,unsignedmode,void*context){
@@ -200,6 +303,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)structobject_idoid;structtree*tree;inti,full_tree=0;+read_tree_fn_tfn=show_tree;conststructoptionls_tree_options[]={OPT_BIT('d',NULL,&ls_options,N_("only show trees"),LS_TREE_ONLY),
@@ -222,6 +326,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)OPT_BOOL(0,"full-tree",&full_tree,N_("list entire tree; not just current directory ""(implies --full-name)")),+OPT_STRING_F(0,"format",&format,N_("format"),+N_("format to use for the output"),+PARSE_OPT_NONEG),OPT__ABBREV(&abbrev),OPT_END()};
@@ -242,6 +349,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)((LS_TREE_ONLY|LS_RECURSIVE)&ls_options))ls_options|=LS_SHOW_TREES;+if(format&&cmdmode)+usage_msg_opt(+_("--format can't be combined with other format-altering options"),+ls_tree_usage,ls_tree_options);if(argc<1)usage_with_options(ls_tree_usage,ls_tree_options);if(get_oid(argv[0],&oid))
@@ -265,6 +376,19 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)tree=parse_tree_indirect(&oid);if(!tree)die("not a tree object");-return!!read_tree(the_repository,tree,-&pathspec,show_tree,NULL);++/*+*Thegenericshow_tree_fmt()isslowerthanshow_tree(),so+*takethefastpathifpossible.+*/+if(format&&+(!strcmp(format,default_format)||+!strcmp(format,long_format)||+!strcmp(format,name_only_format)||+!strcmp(format,object_only_format)))+fn=show_tree;+elseif(format)+fn=show_tree_fmt;++return!!read_tree(the_repository,tree,&pathspec,fn,NULL);}
From: Teng Long <hidden> Date: 2022-01-06 04:32:24
A convenient way to pad strings is to use something like
`strbuf_addf(&buf, "%20s", "Hello, world!")`.
However, the Coccinelle rule that forbids a format `"%s"` with a
constant string argument cast too wide a net, and also forbade such
padding.
The original rule was introduced by commit:
https://github.com/git/git/commit/28c23cd4c3902449aff72cb9a4a703220be0d6ac
Signed-off-by: Johannes Schindelin <redacted>
Signed-off-by: Teng Long <redacted>
---
contrib/coccinelle/strbuf.cocci | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Johannes Schindelin <hidden> Date: 2022-01-07 13:04:03
Hi Teng,
On Thu, 6 Jan 2022, Teng Long wrote:
A convenient way to pad strings is to use something like
`strbuf_addf(&buf, "%20s", "Hello, world!")`.
However, the Coccinelle rule that forbids a format `"%s"` with a
constant string argument cast too wide a net, and also forbade such
padding.
The original rule was introduced by commit:
https://github.com/git/git/commit/28c23cd4c3902449aff72cb9a4a703220be0d6ac
Doing this in 9/9 is too late, by this time you already introduced the
code site that requires this workaround.
At the same time, I wonder why you want to defend spinning up the
full-blown `printf()` machinery just to pad text that you can easily pad
yourself. It sounds like a lot of trouble to me to introduce this patch
and then use an uncommon method to pad a fixed string at runtime. Too much
trouble for my liking.
Ciao,
Dscho
quoted hunk
Signed-off-by: Johannes Schindelin <redacted>
Signed-off-by: Teng Long <redacted>
---
contrib/coccinelle/strbuf.cocci | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Teng Long <hidden> Date: 2022-01-10 08:23:01
I am not sure whether I have sent the email repeatedly, because it is
not shown on mailist. If so, sorry to bother you.
Johannes Schindelin writes:
Doing this in 9/9 is too late, by this time you already introduced the
code site that requires this workaround.
Yes, you are correct.
Will fixed if the patch is still remained to next one.
At the same time, I wonder why you want to defend spinning up the
full-blown `printf()` machinery just to pad text that you can easily pad
yourself. It sounds like a lot of trouble to me to introduce this patch
and then use an uncommon method to pad a fixed string at runtime. Too much
trouble for my liking.
I may not have explained it clearly in the cover. Sorry for that, I'm going
to explain some more here, please correct me if there is something wrong or
the method is not recommended or is not best practice in community.
Firstly, the patch needs to be introduced I think and it has nothing to do
with using " -" or "%7s" here, because the fix recommandation is not
accurate in terms of the "static-analysis" report if someone just uses the
"addf" api:
- strbuf_addf(line, "%7s", "-");
+ strbuf_addstr(line, "-");
They have different execution results and bring confusion to people.
Then secondly, about the using "strbuf_addf(line, "%7s" , "-");" or
"strbuf_addstr(line, " -");". I think you prefer the later and I prefer
the former, right? (I'm not a native English speaker, so I just want to make
sure I understand whole your meannings).
If I understand everything correctly so far, it's good :)
As I metioned in a previous reply [1], I think there is no performance
issue here..
Why I prefer more of the former that is because, for the single line,
it's more readable I think. Maybe it's not going to modify very often,
but If someone want to know what this is, might have to do a count. So
I don't think this is any more readable than "%7s".
Here's what I think and looking forward to your reply.
Thanks.
[1] https://public-inbox.org/git/CADMgQSRxko6nC0zfDiVVfL2ZkdQVbBq0s59Er+6Nmg9vz4uJKQ@mail.gmail.com/
From: Johannes Schindelin <hidden> Date: 2022-01-10 12:50:18
Hi Teng,
On Mon, 10 Jan 2022, Teng Long wrote:
[...] about the using "strbuf_addf(line, "%7s" , "-");" or
"strbuf_addstr(line, " -");". [...]
Why I prefer more of the former that is because, for the single line,
it's more readable I think.
I strongly disagree. Using a format requires the reader to interpret a
`printf()` format, to remember (if they ever knew) the rules about padding
with `%<number>s` formats, and then to satisfy themselves that the result
is correct.
That's quite the cognitive load you put on the reader for something as
trivial as " -".
Not a fan,
Johannes
From: Teng Long <hidden> Date: 2022-01-10 14:40:56
I strongly disagree. Using a format requires the reader to interpret a
`printf()` format, to remember (if they ever knew) the rules about padding
with `%<number>s` formats, and then to satisfy themselves that the result
is correct.
That's quite the cognitive load you put on the reader for something as
trivial as " -".
Not a fan,
Johannes
Ok. I will modify the next patch according to your opinion, I just
hope to understand the problems and make better contributions in the
future.
Thanks.
On Mon, Jan 10, 2022 at 8:49 PM Johannes Schindelin
[off-list ref] wrote:
Hi Teng,
On Mon, 10 Jan 2022, Teng Long wrote:
quoted
[...] about the using "strbuf_addf(line, "%7s" , "-");" or
"strbuf_addstr(line, " -");". [...]
Why I prefer more of the former that is because, for the single line,
it's more readable I think.
I strongly disagree. Using a format requires the reader to interpret a
`printf()` format, to remember (if they ever knew) the rules about padding
with `%<number>s` formats, and then to satisfy themselves that the result
is correct.
That's quite the cognitive load you put on the reader for something as
trivial as " -".
Not a fan,
Johannes
A convenient way to pad strings is to use something like
`strbuf_addf(&buf, "%20s", "Hello, world!")`.
However, the Coccinelle rule that forbids a format `"%s"` with a
constant string argument cast too wide a net, and also forbade such
padding.
The original rule was introduced by commit:
https://github.com/git/git/commit/28c23cd4c3902449aff72cb9a4a703220be0d6ac
Let's refer to commits like this:
28c23cd4c39 (strbuf.cocci: suggest strbuf_addbuf() to add one strbuf to an other, 2019-01-25)
quoted hunk
Signed-off-by: Johannes Schindelin <redacted>
Signed-off-by: Teng Long <redacted>
---
contrib/coccinelle/strbuf.cocci | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)