cat-file: reuse ref-filter logic
PATCH 1/15 ref-filter: add obj-type check in grab contents
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.
Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.
--- ref-filter.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +15 -9
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size }/* See grab_values */ -static void grab_sub_body_contents (struct atom_value *val,int deref,void *buf) +static void grab_sub_body_contents (struct atom_value *val,int deref,struct expand_data *data) {int i;const char *subpos =NULL , *bodypos =NULL , *sigpos =NULL ;size_t sublen =0 , bodylen =0 , nonsiglen =0 , siglen =0 ; +void *buf = data->content;for (i =0 ; i < used_atom_cnt; i++) {struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)continue ;if (deref) name++; -if (strcmp (name,"body" ) && - !starts_with (name,"subject" ) && - !starts_with (name,"trailers" ) && - !starts_with (name,"contents" )) + +if ((data->type !=OBJ_TAG && + data->type !=OBJ_COMMIT ) || + (strcmp (name,"body" ) && + !starts_with (name,"subject" ) && + !starts_with (name,"trailers" ) && + !starts_with (name,"contents" )))continue ;if (!subpos) find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)* pointed at by the ref itself; otherwise it is the object the * ref (which is a tag) refers to. */ -static void grab_values (struct atom_value *val,int deref,struct object *obj,void *buf) +static void grab_values (struct atom_value *val,int deref,struct object *obj,struct expand_data *data) { +void *buf = data->content; +switch (obj->type) {case OBJ_TAG :grab_tag_values (val, deref, obj); -grab_sub_body_contents (val, deref, buf); +grab_sub_body_contents (val, deref, data);grab_person ("tagger" , val, deref, buf);break ;case OBJ_COMMIT :grab_commit_values (val, deref, obj); -grab_sub_body_contents (val, deref, buf); +grab_sub_body_contents (val, deref, data);grab_person ("author" , val, deref, buf);grab_person ("committer" , val, deref, buf);break ;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **objreturn strbuf_addf_ret (err,-1 ,_ ("parse_object_buffer failed on %s for %s" ),oid_to_hex (&oi->oid), ref->refname); } -grab_values (ref->value, deref, *obj, oi->content); +grab_values (ref->value, deref, *obj, oi); }grab_common_values (ref->value, deref, oi);
Signature · 1 line
gitgitgadget
cover cat-file: reuse ref-filter logic
This patch series make cat-file reuse ref-filter logic.
Change from last version:
1. Amend part of the description of git for-each-ref.txt.
2. Modify commit messages about why cat-file reject some atoms.
3. Resolve conflicts with the 'seen' branch, rebase this patch on (bb9a3a8f77 Merge branch 'zh/cat-file-batch-fix' into jch).
ZheNing Hu (15):
[GSOC] ref-filter: add obj-type check in grab contents [GSOC] ref-filter: add %(raw) atom
[GSOC] ref-filter: --format=%(raw) re-support --perl [GSOC] ref-filter: use non-const ref_format in *_atom_parser() [GSOC] ref-filter: add %(rest) atom
[GSOC] ref-filter: pass get_object() return value to their callers [GSOC] ref-filter: introduce free_ref_array_item_value() function [GSOC] ref-filter: add cat_file_mode in struct ref_format [GSOC] ref-filter: modify the error message and value in get_object [GSOC] cat-file: add has_object_file() check
[GSOC] cat-file: change batch_objects parameter name [GSOC] cat-file: reuse ref-filter logic
[GSOC] cat-file: reuse err buf in batch_object_write() [GSOC] cat-file: re-implement --textconv, --filters options [GSOC] ref-filter: remove grab_oid() function
Documentation/git-cat-file.txt | 6 + Documentation/git-for-each-ref.txt | 9 + builtin/cat-file.c | 275 ++++++---------------- builtin/tag.c | 2 +- quote.c | 17 ++ quote.h | 1 + ref-filter.c | 357 ++++++++++++++++++++++------- ref-filter.h | 14 +- t/t1006-cat-file.sh | 251 ++++++++++++++++++++ t/t3203-branch-output.sh | 4 + t/t6300-for-each-ref.sh | 235 +++++++++++++++++++ t/t6301-for-each-ref-errors.sh | 2 +- t/t7004-tag.sh | 4 + t/t7030-verify-tag.sh | 4 + 14 files changed, 887 insertions(+), 294 deletions(-)
base-commit: bb9a3a8f77e13b7a414cf64bf294c3f59f2444c9
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-989%2Fadlternative%2Fcat-file-batch-refactor-rebase-version-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-989/adlternative/cat-file-batch-refactor-rebase-version-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/989
Signature · 1 line
gitgitgadget
PATCH 3/15 ref-filter: --format=%(raw) re-support --perl
From: ZheNing Hu <[email protected]>
Because the perl language can handle binary data correctly, add the function perl_quote_buf_with_len(), which can specify the length of the data and prevent the data from being truncated at '\0' to help --format="%(raw)" re-support --perl.
--- Documentation/git-for-each-ref.txt | 4 ++-- quote.c | 17 +++++++++++++++++ quote.h | 1 + ref-filter.c | 15 +++++++++++---- t/t6300-for-each-ref.sh | 19 +++++++++++++++++-- 5 files changed, 48 insertions(+), 8 deletions(-)
Documentation/git-for-each-ref.txt +2 -2
@@ -241,8 +241,8 @@ raw:size:: The raw data size of the object. Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`, -`--perl` because such language may not support arbitrary binary data in their -string variable type. +because such language may not support arbitrary binary data in their string +variable type. The message in a commit or a tag object is `contents`, from which `contents:<part>` can be used to extract various parts out of:
quote.c +17 -0
@@ -471,6 +471,23 @@ void perl_quote_buf(struct strbuf *sb, const char *src)strbuf_addch (sb, sq); } +void perl_quote_buf_with_len (struct strbuf *sb,const char *src,size_t len) +{ +const char sq ='\'' ; +const char bq ='\\' ; +const char *c = src; +const char *end = src + len; + +strbuf_addch (sb, sq); +while (c != end) { +if (*c == sq || *c == bq) +strbuf_addch (sb, bq); +strbuf_addch (sb, *c); + c++; + } +strbuf_addch (sb, sq); +} +void python_quote_buf (struct strbuf *sb,const char *src) {const char sq ='\'' ;
quote.h +1 -0
@@ -94,6 +94,7 @@ char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigne/* quoting as a string literal for other languages */ void perl_quote_buf (struct strbuf *sb,const char *src); +void perl_quote_buf_with_len (struct strbuf *sb,const char *src,size_t len);void python_quote_buf (struct strbuf *sb,const char *src);void tcl_quote_buf (struct strbuf *sb,const char *src);void basic_regex_quote_buf (struct strbuf *sb,const char *src);
ref-filter.c +11 -4
@@ -742,7 +742,10 @@ static void quote_formatting(struct strbuf *s, const char *str, size_t len, intsq_quote_buf (s, str);break ;case QUOTE_PERL : -perl_quote_buf (s, str); +if (len !=ATOM_VALUE_S_SIZE_INIT ) +perl_quote_buf_with_len (s, str, len); +else +perl_quote_buf (s, str);break ;case QUOTE_PYTHON :python_quote_buf (s, str);
@@ -1006,10 +1009,14 @@ int verify_ref_format(struct ref_format *format) at =parse_ref_filter_atom (format, sp +2 , ep, &err);if (at <0 )die ("%s" , err.buf); -if (format->quote_style && used_atom[at].atom_type ==ATOM_RAW && - used_atom[at].u.raw_data.option ==RAW_BARE ) + +if ((format->quote_style ==QUOTE_PYTHON || + format->quote_style ==QUOTE_SHELL || + format->quote_style ==QUOTE_TCL ) && + used_atom[at].atom_type ==ATOM_RAW && + used_atom[at].u.raw_data.option ==RAW_BARE )die (_ ("--format=%.*s cannot be used with" -"--python, --shell, --tcl, --perl" ), (int )(ep - sp -2 ), sp +2 ); +"--python, --shell, --tcl" ), (int )(ep - sp -2 ), sp +2 ); cp = ep +1 ;if (skip_prefix (used_atom[at].name,"color:" , &color))
t/t6300-for-each-ref.sh +17 -2
@@ -915,8 +915,23 @@ test_expect_success '%(raw) with --tcl must fail' ' test_must_fail git for-each-ref --format="%(raw)" --tcl ' -test_expect_success '%(raw) with --perl must fail' ' - test_must_fail git for-each-ref --format="%(raw)" --perl +test_expect_success '%(raw) with --perl' ' + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob1 --perl | perl > actual && + cmp blob1 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob3 --perl | perl > actual && + cmp blob3 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob8 --perl | perl > actual && + cmp blob8 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/first --perl | perl > actual && + cmp one actual && + git cat-file tree refs/mytrees/first > expected && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/mytrees/first --perl | perl > actual && + cmp expected actual ' test_expect_success '%(raw) with --shell must fail' '
Signature · 1 line
gitgitgadget
PATCH 2/15 ref-filter: add %(raw) atom
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.
The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).
E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.
Therefore, we need to find a way to record the length of the atom_value's member s. Although strbuf can already record the string and its length, if we want to replace the type of atom_value's member s with strbuf, many places in ref-filter that are filled with dynamically allocated mermory in v->s are not easy to replace. At the same time, we need to check if v->s == NULL in populate_value(), and strbuf cannot easily distinguish NULL and empty strings, but c-style "const char *" can do it. So add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.
Note that --format=%(raw) cannot be used with --python, --shell, --tcl, and --perl because if the binary raw data is passed to a variable in such languages, these may not support arbitrary binary data in their string variable type.
--- Documentation/git-for-each-ref.txt | 9 ++ ref-filter.c | 139 +++++++++++++++---- t/t6300-for-each-ref.sh | 216 +++++++++++++++++++++++++++++ 3 files changed, 337 insertions(+), 27 deletions(-)
Documentation/git-for-each-ref.txt +9 -0
@@ -235,6 +235,15 @@ and `date` to extract the named component. For email fields (`authoremail`, without angle brackets, and `:localpart` to get the part before the `@` symbol out of the trimmed email. +The raw data in an object is `raw`. + +raw:size:: + The raw data size of the object. + +Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`, +`--perl` because such language may not support arbitrary binary data in their +string variable type. + The message in a commit or a tag object is `contents`, from which `contents:<part>` can be used to extract various parts out of:
ref-filter.c +112 -27
293 lines not on this page · raw message
t/t6300-for-each-ref.sh +216 -0
258 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 5/15 ref-filter: add %(rest) atom
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
In order to let "cat-file --batch=%(rest)" use the ref-filter interface, add %(rest) atom for ref-filter. "git for-each-ref", "git branch", "git tag" and "git verify-tag" will reject %(rest) by default.
--- ref-filter.c | 21 +++++++++++++++++++++ ref-filter.h | 5 ++++- t/t3203-branch-output.sh | 4 ++++ t/t6300-for-each-ref.sh | 4 ++++ t/t7004-tag.sh | 4 ++++ t/t7030-verify-tag.sh | 4 ++++ 6 files changed, 41 insertions(+), 1 deletion(-)
ref-filter.c +21 -0
@@ -157,6 +157,7 @@ enum atom_type {ATOM_IF ,ATOM_THEN ,ATOM_ELSE , + ATOM_REST, };/*
@@ -559,6 +560,15 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,return 0 ; } +static int rest_atom_parser (struct ref_format *format,struct used_atom *atom, +const char *arg,struct strbuf *err) +{ +if (arg) +return strbuf_addf_ret (err,-1 ,_ ("%%(rest) does not take arguments" )); + format->use_rest =1 ; +return 0 ; +} +static int head_atom_parser (struct ref_format *format,struct used_atom *atom,const char *arg,struct strbuf *unused_err) {
@@ -615,6 +625,7 @@ static struct { [ATOM_IF] = { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, [ATOM_THEN] = { "then", SOURCE_NONE }, [ATOM_ELSE] = { "else", SOURCE_NONE }, + [ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },/* * Please update $__git_ref_fieldlist in git-completion.bash * when you add new atoms
@@ -1010,6 +1021,9 @@ int verify_ref_format(struct ref_format *format)if (at <0 )die ("%s" , err.buf); +if (used_atom[at].atom_type ==ATOM_REST ) +die ("this command reject atom %%(%.*s)" , (int )(ep - sp -2 ), sp +2 ); + if ((format->quote_style == QUOTE_PYTHON || format->quote_style == QUOTE_SHELL || format->quote_style == QUOTE_TCL) &&
@@ -1927,6 +1941,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) v->handler = else_atom_handler; v->s =xstrdup ("" );continue ; + }else if (atom_type ==ATOM_REST ) { +if (ref->rest) + v->s =xstrdup (ref->rest); +else + v->s =xstrdup ("" ); +continue ; }else continue ;
@@ -2144,6 +2164,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,FLEX_ALLOC_STR (ref, refname, refname);oidcpy (&ref->objectname, oid); + ref->rest =NULL ;return ref; }
ref-filter.h +4 -1
@@ -38,6 +38,7 @@ struct ref_sorting {struct ref_array_item {struct object_id objectname; +const char *rest;int flag;unsigned int kind;const char *symref;
@@ -76,14 +77,16 @@ struct ref_format {* verify_ref_format() afterwards to finalize. */ const char *format; +const char *rest;int quote_style; +int use_rest;int use_color;/* Internal state to ref-filter */ int need_color_reset_at_eol; }; -#define REF_FORMAT_INIT { NULL, 0, -1 } +#define REF_FORMAT_INIT { .use_color = -1 }/* Macros for checking --merged and --no-merged options */ #define _OPT_MERGED_NO_MERGED (option, filter, h) \
t/t3203-branch-output.sh +4 -0
@@ -340,6 +340,10 @@ test_expect_success 'git branch --format option' ' test_cmp expect actual ' +test_expect_success 'git branch with --format=%(rest) must fail' ' + test_must_fail git branch --format="%(rest)" >actual +' + test_expect_success 'worktree colors correct' ' cat >expect <<-EOF && * <GREEN>(HEAD detached from fromtag)<RESET>
t/t6300-for-each-ref.sh +4 -0
@@ -1211,6 +1211,10 @@ test_expect_success 'basic atom: head contents:trailers' ' test_cmp expect actual.clean ' +test_expect_success 'basic atom: rest must fail' ' + test_must_fail git for-each-ref --format="%(rest)" refs/heads/main +' + test_expect_success 'trailer parsing not fooled by --- line' ' git commit --allow-empty -F - <<-\EOF && this is the subject
t/t7004-tag.sh +4 -0
@@ -1998,6 +1998,10 @@ test_expect_success '--format should list tags as per format given' ' test_cmp expect actual ' +test_expect_success 'git tag -l with --format="%(rest)" must fail' ' + test_must_fail git tag -l --format="%(rest)" "v1*" +' + test_expect_success "set up color tests" ' echo "<RED>v1.0<RESET>" >expect.color && echo "v1.0" >expect.bare &&
t/t7030-verify-tag.sh +4 -0
@@ -194,6 +194,10 @@ test_expect_success GPG 'verifying tag with --format' ' test_cmp expect actual ' +test_expect_success GPG 'verifying tag with --format="%(rest)" must fail' ' + test_must_fail git verify-tag --format="%(rest)" "fourth-signed" +' + test_expect_success GPG 'verifying a forged tag with --format should fail silently' ' test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged && test_must_be_empty actual-forged
Signature · 1 line
gitgitgadget
PATCH 4/15 ref-filter: use non-const ref_format in *_atom_parser()
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Use non-const ref_format in *_atom_parser(), which can help us modify the members of ref_format in *_atom_parser().
--- builtin/tag.c | 2 +- ref-filter.c | 44 ++++++++++++++++++++++---------------------- ref-filter.h | 4 ++-- 3 files changed, 25 insertions(+), 25 deletions(-)
builtin/tag.c +1 -1
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref, const struct object_id *oid, void *cb_data) { int flags; -const struct ref_format *format = cb_data; +struct ref_format *format = cb_data; flags =GPG_VERIFY_VERBOSE ;if (format->format)
ref-filter.c +22 -22
164 lines not on this page · raw message
ref-filter.h +2 -2
@@ -116,7 +116,7 @@ void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);void ref_sorting_set_sort_flags_all (struct ref_sorting *sorting,unsigned int mask,int on);/* Based on the given format and quote_style, fill the strbuf */ int format_ref_array_item (struct ref_array_item *info, -const struct ref_format *format, +struct ref_format *format,struct strbuf *final_buf,struct strbuf *error_buf);/* Parse a single sort specifier and add it to the list */
@@ -137,7 +137,7 @@ void setup_ref_filter_porcelain_msg(void);* name must be a fully qualified refname. */ void pretty_print_ref (const char *name,const struct object_id *oid, -const struct ref_format *format); +struct ref_format *format);/* * Push a single ref onto the array; this can be used to construct your own
Signature · 1 line
gitgitgadget
PATCH 6/15 ref-filter: pass get_object() return value to their callers
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Because in the refactor of git cat-file --batch later, oid_object_info_extended() in get_object() will be used to obtain the info of an object with it's oid. When the object cannot be obtained in the git repository, cat-file --batch expects to output "<oid> missing" and continue the next oid query instead of letting Git exit. In other error conditions, Git should exit normally. So we can achieve this function by passing the return value of get_object().
--- ref-filter.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
ref-filter.c +11 -6
@@ -1816,6 +1816,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) {struct object *obj;int i; +int ret;struct object_info empty =OBJECT_INFO_INIT ;CALLOC_ARRAY (ref->value, used_atom_cnt);
@@ -1972,8 +1973,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) oi.oid = ref->objectname; -if (get_object (ref,0 , &obj, &oi, err)) -return -1 ; + ret =get_object (ref,0 , &obj, &oi, err); +if (ret) +return ret;/* * If there is no atom that wants to know about tagged
@@ -2005,8 +2007,10 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v, struct strbuf *err) {if (!ref->value) { -if (populate_value (ref, err)) -return -1 ; +int ret =populate_value (ref, err); + +if (ret) +return ret;fill_missing_values (ref->value); } *v = &ref->value[atom];
@@ -2580,6 +2584,7 @@ int format_ref_array_item(struct ref_array_item *info, {const char *cp, *sp, *ep;struct ref_formatting_state state =REF_FORMATTING_STATE_INIT ; +int ret; state.quote_style = format->quote_style;push_stack_element (&state.stack);
@@ -2592,10 +2597,10 @@ int format_ref_array_item(struct ref_array_item *info,if (cp < sp)append_literal (cp, sp, &state); pos =parse_ref_filter_atom (format, sp +2 , ep, error_buf); -if (pos <0 ||get_ref_atom_value (info, pos, &atomv, error_buf) || +if (pos <0 || (ret =get_ref_atom_value (info, pos, &atomv, error_buf)) || atomv->handler (atomv, &state, error_buf)) {pop_stack_element (&state.stack); -return -1 ; +return ret ? ret :-1 ; } }if (*cp) {
Signature · 1 line
gitgitgadget
PATCH 8/15 ref-filter: add cat_file_mode in struct ref_format
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Add cat_file_mode member in struct ref_format, when cat-file --batch use ref-filter logic later, it can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...
--- ref-filter.c | 11 +++++++++-- ref-filter.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-)
ref-filter.c +9 -2
@@ -1021,8 +1021,15 @@ int verify_ref_format(struct ref_format *format)if (at <0 )die ("%s" , err.buf); -if (used_atom[at].atom_type ==ATOM_REST ) -die ("this command reject atom %%(%.*s)" , (int )(ep - sp -2 ), sp +2 ); +if ((!format->cat_file_mode && used_atom[at].atom_type ==ATOM_REST ) || + (format->cat_file_mode && (used_atom[at].atom_type ==ATOM_FLAG || + used_atom[at].atom_type ==ATOM_HEAD || + used_atom[at].atom_type ==ATOM_PUSH || + used_atom[at].atom_type ==ATOM_REFNAME || + used_atom[at].atom_type ==ATOM_SYMREF || + used_atom[at].atom_type ==ATOM_UPSTREAM || + used_atom[at].atom_type ==ATOM_WORKTREEPATH ))) +die (_ ("this command reject atom %%(%.*s)" ), (int )(ep - sp -2 ), sp +2 ); if ((format->quote_style == QUOTE_PYTHON || format->quote_style == QUOTE_SHELL ||
ref-filter.h +1 -0
@@ -78,6 +78,7 @@ struct ref_format {*/ const char *format;const char *rest; +int cat_file_mode;int quote_style;int use_rest;int use_color;
Signature · 1 line
gitgitgadget
PATCH 7/15 ref-filter: introduce free_ref_array_item_value() function
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
When we use ref_array_item which is not dynamically allocated and want to free the space of its member "value" after the end of use, free_array_item() does not meet our needs, because it tries to free ref_array_item itself and its member "symref".
Introduce free_ref_array_item_value() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's value memory later.
--- ref-filter.c | 11 ++++++++--- ref-filter.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-)
ref-filter.c +8 -3
@@ -2291,16 +2291,21 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,return 0 ; } -/* Free memory allocated for a ref_array_item */ -static void free_array_item (struct ref_array_item *item) +void free_ref_array_item_value (struct ref_array_item *item) { -free ((char *)item->symref);if (item->value) {int i;for (i =0 ; i < used_atom_cnt; i++)free ((char *)item->value[i].s);free (item->value); } +} + +/* Free memory allocated for a ref_array_item */ +static void free_array_item (struct ref_array_item *item) +{ +free ((char *)item->symref); +free_ref_array_item_value (item);free (item); }
ref-filter.h +2 -0
@@ -111,6 +111,8 @@ struct ref_format {int filter_refs (struct ref_array *array,struct ref_filter *filter,unsigned int type);/* Clear all memory allocated to ref_array */ void ref_array_clear (struct ref_array *array); +/* Free ref_array_item's value */ +void free_ref_array_item_value (struct ref_array_item *item);/* Used to verify if the given format is correct and to parse out the used atoms */ int verify_ref_format (struct ref_format *format);/* Sort the given ref_array as per the ref_sorting provided */
Signature · 1 line
gitgitgadget
PATCH 9/15 ref-filter: modify the error message and value in get_object
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>" if oid_object_info_extended() unable to find the data corresponding to oid. When cat-file --batch use ref-filter logic later it can help format_ref_array_item() just report that the object is missing without letting Git exit.
--- ref-filter.c | 4 ++-- t/t6301-for-each-ref-errors.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
ref-filter.c +2 -2
@@ -1749,8 +1749,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj }if (oid_object_info_extended (the_repository, &oi->oid, &oi->info,OBJECT_INFO_LOOKUP_REPLACE )) -return strbuf_addf_ret (err,-1 ,_ ("missing object %s for %s" ), -oid_to_hex (&oi->oid), ref->refname); +return strbuf_addf_ret (err,1 ,_ ("%s missing" ), +oid_to_hex (&oi->oid));if (oi->info.disk_sizep && oi->disk_size <0 )BUG ("Object size is less than zero." );
t/t6301-for-each-ref-errors.sh +1 -1
@@ -41,7 +41,7 @@ test_expect_success 'Missing objects are reported correctly' ' r=refs/heads/missing && echo $MISSING >.git/$r && test_when_finished "rm -f .git/$r" && - echo "fatal: missing object $MISSING for $r" >missing-err && + echo "fatal: $MISSING missing" >missing-err && test_must_fail git for-each-ref 2>err && test_cmp missing-err err && (
Signature · 1 line
gitgitgadget
PATCH 11/15 cat-file: change batch_objects parameter name
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Because later cat-file reuses ref-filter logic that will add parameter "const struct option *options" to batch_objects(), the two synonymous parameters of "opt" and "options" may confuse readers, so change batch_options parameter of batch_objects() from "opt" to "batch".
--- builtin/cat-file.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
builtin/cat-file.c +10 -10
@@ -495,7 +495,7 @@ static int batch_unordered_packed(const struct object_id *oid,return batch_unordered_object (oid, data); } -static int batch_objects (struct batch_options *opt) +static int batch_objects (struct batch_options *batch) {struct strbuf input =STRBUF_INIT ;struct strbuf output =STRBUF_INIT ;
@@ -503,8 +503,8 @@ static int batch_objects(struct batch_options *opt)int save_warning;int retval =0 ; -if (!opt->format) - opt->format ="%(objectname) %(objecttype) %(objectsize)" ; +if (!batch->format) + batch->format ="%(objectname) %(objecttype) %(objectsize)" ;/* * Expand once with our special mark_query flag, which will prime the
@@ -513,20 +513,20 @@ static int batch_objects(struct batch_options *opt)*/ memset (&data,0 ,sizeof (data)); data.mark_query =1 ; -strbuf_expand (&output, opt->format, expand_format, &data); +strbuf_expand (&output, batch->format, expand_format, &data); data.mark_query =0 ;strbuf_release (&output); -if (opt->cmdmode) +if (batch->cmdmode) data.split_on_whitespace =1 ;/* * If we are printing out the object, then always fill in the type, * since we will want to decide whether or not to stream. */ -if (opt->print_contents) +if (batch->print_contents) data.info.typep = &data.type; -if (opt->all_objects) { +if (batch->all_objects) {struct object_cb_data cb;struct object_info empty =OBJECT_INFO_INIT ;
@@ -536,11 +536,11 @@ static int batch_objects(struct batch_options *opt)if (has_promisor_remote ())warning ("This repository uses promisor remotes. Some objects may not be loaded." ); - cb.opt = opt; + cb.opt = batch; cb.expand = &data; cb.scratch = &output; -if (opt->unordered) { +if (batch->unordered) {struct oidset seen =OIDSET_INIT ; cb.seen = &seen;
@@ -590,7 +590,7 @@ static int batch_objects(struct batch_options *opt) data.rest = p; } -batch_one_object (input.buf, &output, opt, &data); +batch_one_object (input.buf, &output, batch, &data); }strbuf_release (&input);
Signature · 1 line
gitgitgadget
PATCH 12/15 cat-file: reuse ref-filter logic
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
In order to let cat-file use ref-filter logic, let's do the following:
1. Change the type of member format in struct batch_options to ref_format, we will pass it to ref-filter later.
2. Let batch_objects() add atoms to format, and use verify_ref_format() to check atoms.
3. Use format_ref_array_item() in batch_object_write() to get the formatted data corresponding to the object. If the return value of format_ref_array_item() is equals to zero, use batch_write() to print object data; else if the return value is less than zero, use die() to print the error message and exit; else if return value is greater than zero, only print the error message, but don't exit.
4. Use free_ref_array_item_value() to free ref_array_item's value.
Most of the atoms in for-each-ref --format are now supported, such as %(tree), %(parent), %(author), %(tagger), %(if), %(then), %(else), %(end). But these atoms will be rejected:%(refname), %(symref), %(upstream), %(push), %(worktreepath), %(flag), %(HEAD), because these atoms are unique to those objects that pointed to by a ref, "for-each-ref"'s family can naturally use these atoms, but not all objects are pointed to be a ref, so "cat-file" will not be able to use them.
The performance for git cat-file --batch-all-objects --batch-check on the Git repository itself with performance testing tool hyperfine changes from 669.4 ms ± 31.1 ms to 1.134 s ± 0.063 s.
The performance for `git cat-file --batch-all-objects --batch
Quoted 1 line
/dev/null` on the Git repository itself with performance testing
tool time change from "27.37s user 0.29s system 98% cpu 28.089 total" to "33.69s user 1.54s system 87% cpu 40.258 total".
--- Documentation/git-cat-file.txt | 6 + builtin/cat-file.c | 242 ++++++------------------------- t/t1006-cat-file.sh | 251 +++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+), 195 deletions(-)
Documentation/git-cat-file.txt +6 -0
@@ -226,6 +226,12 @@ newline. The available atoms are: after that first run of whitespace (i.e., the "rest" of the line) are output in place of the `%(rest)` atom. +Note that most of the atoms in `for-each-ref --format` are now supported, +such as `%(tree)`, `%(parent)`, `%(author)`, `%(tagger)`, `%(if)`, +`%(then)`, `%(else)`, `%(end)`. But these atoms will be rejected: +`%(refname)`, `%(symref)`, `%(upstream)`, `%(push)`, `%(worktreepath)`, +`%(flag)`, `%(HEAD)`. See linkgit:git-for-each-ref[1]. + If no format is specified, the default format is `%(objectname) %(objecttype) %(objectsize)`.
builtin/cat-file.c +47 -195
340 lines not on this page · raw message
t/t1006-cat-file.sh +251 -0
256 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 10/15 cat-file: add has_object_file() check
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Use has_object_file() in batch_one_object() to check whether the input object exists. This can help us reject the missing oid when we let cat-file --batch use ref-filter logic later.
--- builtin/cat-file.c | 7 +++++++ 1 file changed, 7 insertions(+)
builtin/cat-file.c +7 -0
@@ -428,6 +428,13 @@ static void batch_one_object(const char *obj_name,return ; } +if (!has_object_file (&data->oid)) { +printf ("%s missing\n" , + obj_name ? obj_name :oid_to_hex (&data->oid)); +fflush (stdout); +return ; + } +batch_object_write (obj_name, scratch, opt, data); }
Signature · 1 line
gitgitgadget
PATCH 13/15 cat-file: reuse err buf in batch_object_write()
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Reuse the err buffer in batch_object_write(), as the buffer scratch does. This will reduce the overhead of multiple allocations of memory of the err buffer.
--- builtin/cat-file.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
builtin/cat-file.c +14 -8
@@ -212,35 +212,36 @@ static void batch_write(struct batch_options *opt, const void *data, int len)static void batch_object_write (const char *obj_name,struct strbuf *scratch, +struct strbuf *err,struct batch_options *opt,struct expand_data *data) {int ret; -struct strbuf err =STRBUF_INIT ;struct ref_array_item item = { data->oid, data->rest };strbuf_reset (scratch); +strbuf_reset (err); - ret =format_ref_array_item (&item, &opt->format, scratch, &err); + ret =format_ref_array_item (&item, &opt->format, scratch, err);if (ret <0 ) -die ("%s\n" , err.buf); +die ("%s\n" , err->buf);if (ret) {/* ret > 0 means when the object corresponding to oid * cannot be found in format_ref_array_item(), we only print * the error message. */ -printf ("%s\n" , err.buf); +printf ("%s\n" , err->buf);fflush (stdout); }else {strbuf_addch (scratch,'\n' );batch_write (opt, scratch->buf, scratch->len); }free_ref_array_item_value (&item); -strbuf_release (&err); }static void batch_one_object (const char *obj_name,struct strbuf *scratch, +struct strbuf *err,struct batch_options *opt,struct expand_data *data) {
@@ -294,7 +295,7 @@ static void batch_one_object(const char *obj_name,return ; } -batch_object_write (obj_name, scratch, opt, data); +batch_object_write (obj_name, scratch, err, opt, data); }struct object_cb_data {
@@ -302,13 +303,14 @@ struct object_cb_data {struct expand_data *expand;struct oidset *seen;struct strbuf *scratch; +struct strbuf *err; };static int batch_object_cb (const struct object_id *oid,void *vdata) {struct object_cb_data *data = vdata;oidcpy (&data->expand->oid, oid); -batch_object_write (NULL , data->scratch, data->opt, data->expand); +batch_object_write (NULL , data->scratch, data->err, data->opt, data->expand);return 0 ; }
@@ -364,6 +366,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio {struct strbuf input =STRBUF_INIT ;struct strbuf output =STRBUF_INIT ; +struct strbuf err =STRBUF_INIT ;struct strbuf format =STRBUF_INIT ;struct expand_data data;int save_warning;
@@ -392,6 +395,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio cb.opt = batch; cb.expand = &data; cb.scratch = &output; + cb.err = &err;if (batch->unordered) {struct oidset seen =OIDSET_INIT ;
@@ -416,6 +420,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optiostrbuf_release (&format);strbuf_release (&output); +strbuf_release (&err);return 0 ; }
@@ -444,12 +449,13 @@ static int batch_objects(struct batch_options *batch, const struct option *optio data.rest = p; } -batch_one_object (input.buf, &output, batch, &data); +batch_one_object (input.buf, &output, &err, batch, &data); }strbuf_release (&format);strbuf_release (&input);strbuf_release (&output); +strbuf_release (&err); warn_on_object_refname_ambiguity = save_warning;return retval; }
Signature · 1 line
gitgitgadget
PATCH 14/15 cat-file: re-implement --textconv, --filters options
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.
Add members use_textconv and use_filters in struct ref_format, and use global variables use_filters and use_textconv in ref-filter.c, so that we can filter the content of the object in get_object(). Use actual_oi to record the real expand_data:
it may point to the original oi or the act_oi processed by textconv_object() or convert_to_working_tree(). grab_values() will grab the contents of actual_oi and grab_common_values() to grab the contents of origin oi, this ensures that %(objectsize) still uses the size of the unfiltered data.
In get_object(), we made an optimization: Firstly, get the size and type of the object instead of directly getting the object data. If using --textconv, after successfully obtaining the filtered object data, an extra oid_object_info_extended() will be skipped, which can reduce the cost of object data copy; If using --filter, the data of the object first will be getted first, and then convert_to_working_tree() will be used to get the filtered object data.
--- builtin/cat-file.c | 6 +++++ ref-filter.c | 59 ++++++++++++++++++++++++++++++++++++++++++++-- ref-filter.h | 2 ++ 3 files changed, 65 insertions(+), 2 deletions(-)
builtin/cat-file.c +6 -0
@@ -380,6 +380,12 @@ static int batch_objects(struct batch_options *batch, const struct option *optioif (batch->print_contents)strbuf_addstr (&format,"\n%(raw)" ); batch->format.format = format.buf; + +if (batch->cmdmode =='c' ) + batch->format.use_textconv =1 ; +else if (batch->cmdmode =='w' ) + batch->format.use_filters =1 ; +if (verify_ref_format (&batch->format))usage_with_options (cat_file_usage, options);
ref-filter.c +57 -2
101 lines not on this page · raw message
ref-filter.h +2 -0
@@ -80,6 +80,8 @@ struct ref_format {const char *rest;int cat_file_mode;int quote_style; +int use_textconv; +int use_filters;int use_rest;int use_color;
Signature · 1 line
gitgitgadget
PATCH 15/15 ref-filter: remove grab_oid() function
In reply to ZheNing Hu via GitGitGadgetFrom: ZheNing Hu <[email protected]>
Because "atom_type == ATOM_OBJECTNAME" implies the condition of starts_with(name, "objectname"), "atom_type == ATOM_TREE" implies the condition of starts_with(name, "tree"), so the check for starts_with(name, field) in grab_oid() is redundant.
So Remove the grab_oid() from ref-filter, to reduce repeated check.
--- ref-filter.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-)
ref-filter.c +9 -17
@@ -1071,16 +1071,6 @@ static const char *do_grab_oid(const char *field, const struct object_id *oid, } } -static int grab_oid (const char *name,const char *field,const struct object_id *oid, -struct atom_value *v,struct used_atom *atom) -{ -if (starts_with (name, field)) { - v->s =xstrdup (do_grab_oid (field, oid, atom)); -return 1 ; - } -return 0 ; -} -/* See grab_values */ static void grab_common_values (struct atom_value *val,int deref,struct expand_data *oi) {
@@ -1106,8 +1096,9 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_ } }else if (atom_type ==ATOM_DELTABASE ) v->s =xstrdup (oid_to_hex (&oi->delta_base_oid)); -else if (atom_type ==ATOM_OBJECTNAME && deref) -grab_oid (name,"objectname" , &oi->oid, v, &used_atom[i]); +else if (atom_type ==ATOM_OBJECTNAME && deref) { + v->s =xstrdup (do_grab_oid ("objectname" , &oi->oid, &used_atom[i])); + } } }
@@ -1148,9 +1139,10 @@ static void grab_commit_values(struct atom_value *val, int deref, struct objectcontinue ;if (deref) name++; -if (atom_type ==ATOM_TREE && -grab_oid (name,"tree" ,get_commit_tree_oid (commit), v, &used_atom[i])) +if (atom_type ==ATOM_TREE ) { + v->s =xstrdup (do_grab_oid ("tree" ,get_commit_tree_oid (commit), &used_atom[i]));continue ; + }if (atom_type ==ATOM_NUMPARENT ) { v->value =commit_list_count (commit->parents); v->s =xstrfmt ("%lu" , (unsigned long )v->value);
@@ -1971,9 +1963,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) v->s =xstrdup (buf +1 ); }continue ; - }else if (!deref && atom_type ==ATOM_OBJECTNAME && -grab_oid (name,"objectname" , &ref->objectname, v, atom)) { -continue ; + }else if (!deref && atom_type ==ATOM_OBJECTNAME ) { + v->s =xstrdup (do_grab_oid ("objectname" , &ref->objectname, atom)); +continue ; }else if (atom_type ==ATOM_HEAD ) {if (atom->u.head && !strcmp (ref->refname, atom->u.head)) v->s =xstrdup ("*" );
Signature · 1 line
gitgitgadget
Re: [PATCH 14/15] [GSOC] cat-file: re-implement --textconv, --filters options
In reply to ZheNing Hu via GitGitGadgetref-filter.c static struct expand_data { · line 89
Quoted 10 lines · ZheNing Hu via GitGitGadget
ref-filter.c +3
-0+#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" #include "parse-options.h" @@ -84,6 +85,9 @@ static struct expand_data {struct object_info info; } oi, oi_deref; +int use_filters; +int use_textconv;
With this commit in, "make sparse" dies like so:
SP ref-filter.c
ref-filter.c:88:5: error: symbol 'use_filters' was not declared. Should it be static? ref-filter.c:89:5: error: symbol 'use_textconv' was not declared. Should it be static?
Re: [PATCH 14/15] [GSOC] cat-file: re-implement --textconv, --filters options
In reply to ZheNing Hu via GitGitGadgetQuoted 9 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.
Add members
use_textconvanduse_filtersin structref_format, and use global variablesuse_filtersanduse_textconvinref-filter.c, so that we can filter the content of the object inget_object().
These global variables are problematic. The places that check them are quite isolated, and it makes me wonder why we cannot pass a format instance through the callchain down to get_object().
Re: [PATCH 14/15] [GSOC] cat-file: re-implement --textconv, --filters options
Quoted 8 lines · Junio C Hamano <[email protected]> 于2021年7月2日周五 上午4:11写道:
Quoted 5 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.
Quoted 8 lines
Quoted 4 lines
Add members
use_textconvanduse_filtersin structref_format, and use global variablesuse_filtersanduse_textconvinref-filter.c, so that we can filter the content of the object inget_object().These global variables are problematic. The places that check them are quite isolated, and it makes me wonder why we cannot pass a format instance through the callchain down to
get_object().
If we pass such two options In such a deep call chain, it seems not worth to do it.
format_ref_array_item()->get_ref_atom_value()->populate_value()-->get_object()
But if we puts them in struct ref_array_item, It may be a solution, but it may not be
very good.
Or just use "static" prefix?
Quoted 2 lines
Thanks.
--
ZheNing Hu
Re: [PATCH 02/15] [GSOC] ref-filter: add %(raw) atom
In reply to ZheNing Hu via GitGitGadgetref-filter.c static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp · line 1401
Quoted 5 lines · ZheNing Hu via GitGitGadget
ref-filter.c +1
-0@@ -1362,17 +1398,29 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct expconst char *subpos =NULL , *bodypos =NULL , *sigpos =NULL ;size_t sublen =0 , bodylen =0 , nonsiglen =0 , siglen =0 ;void *buf = data->content; +unsigned long buf_size = data->size;
Let's declare this.
ref-filter.c static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp · line 1414
Quoted 12 lines
ref-filter.c +2
-0for (i =0 ; i < used_atom_cnt; i++) {struct used_atom *atom = &used_atom[i];const char *name = atom->name;struct atom_value *v = &val[i]; +enum atom_type atom_type = atom->atom_type;if (!!deref != (*name =='*' ))continue ;if (deref) name++; +if (atom_type ==ATOM_RAW ) {
Here, where it's going to be used.
ref-filter.c static int compare_detached_head(struct ref_array_item *a, struct ref_array_item · line 2434
Quoted 12 lines
ref-filter.c +12
-0+static int memcasecmp (const void *vs1,const void *vs2,size_t n) +{ +const char *s1 = vs1, *s2 = vs2; +const char *end = s1 + n; + +for (; s1 < end; s1++, s2++) { +int diff =tolower (*s1) -tolower (*s2); +if (diff) +return diff; + } +return 0 ; +}
I thought we had such a thing already, but clearly not...
t/t6300-for-each-ref.sh test_atom head parent:short=10 '' · line 134
Quoted 10 lines
t/t6300-for-each-ref.sh +2
-0@@ -130,6 +130,8 @@ test_atom head parent:short=10 '' test_atom head numparent 0 test_atom head object '' test_atom head type '' +test_atom head raw "$(git cat-file commit refs/heads/main) +"
Extra stay line here, is the \n intentional?
Re: [PATCH 03/15] [GSOC] ref-filter: --format=%(raw) re-support --perl
In reply to ZheNing Hu via GitGitGadgetQuoted 9 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
Because the perl language can handle binary data correctly, add the function
perl_quote_buf_with_len(), which can specify the length of the data and prevent the data from being truncated at '\0' to help--format="%(raw)"re-support--perl.Signed-off-by ZheNing Hu <[email protected]>
Thanks!
Quoted 12 lines
t/t6300-for-each-ref.sh +10
-2-test_expect_success '%(raw) with --perl must fail' ' - test_must_fail git for-each-ref --format="%(raw)" --perl +test_expect_success '%(raw) with --perl' ' + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob1 --perl | perl > actual && + cmp blob1 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob3 --perl | perl > actual && + cmp blob3 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob8 --perl | perl > actual && + cmp blob8 actual &&
t/t6300-for-each-ref.sh test_expect_success '%(raw) with --tcl must fail' ' · line 937
Quoted 10 lines
t/t6300-for-each-ref.sh +7
-0+ git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/first --perl | perl > actual && + cmp one actual && + git cat-file tree refs/mytrees/first > expected && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/mytrees/first --perl | perl > actual && + cmp expected actual ' test_expect_success '%(raw) with --shell must fail' '
Nit: Formatting can be "| perl >actual", not "| perl > actual", like the usual style...
Re: [PATCH 08/15] [GSOC] ref-filter: add cat_file_mode in struct ref_format
In reply to ZheNing Hu via GitGitGadgetQuoted 24 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
Add
cat_file_modemember in structref_format, whencat-file --batchuse ref-filter logic later, it can help us reject atoms inverify_ref_format()which cat-file cannot use, e.g.%(refname),%(push),%(upstream)...Mentored-by Christian Couder <[email protected]>Mentored-by Hariom Verma <[email protected]>Signed-off-by ZheNing Hu <[email protected]>--- ref-filter.c | 11 +++++++++-- ref-filter.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-)ref-filter.c +0
-1@@ -1021,8 +1021,15 @@ int verify_ref_format(struct ref_format *format)if (at <0 )die ("%s" , err.buf); -if (used_atom[at].atom_type ==ATOM_REST )
ref-filter.c int verify_ref_format(struct ref_format *format) · line 1032
Quoted 10 lines
ref-filter.c +9
-1-die ("this command reject atom %%(%.*s)" , (int )(ep - sp -2 ), sp +2 ); +if ((!format->cat_file_mode && used_atom[at].atom_type ==ATOM_REST ) || + (format->cat_file_mode && (used_atom[at].atom_type ==ATOM_FLAG || + used_atom[at].atom_type ==ATOM_HEAD || + used_atom[at].atom_type ==ATOM_PUSH || + used_atom[at].atom_type ==ATOM_REFNAME || + used_atom[at].atom_type ==ATOM_SYMREF || + used_atom[at].atom_type ==ATOM_UPSTREAM || + used_atom[at].atom_type ==ATOM_WORKTREEPATH ))) +die (_ ("this command reject atom %%(%.*s)" ), (int )(ep - sp -2 ), sp +2 );
Maybe it was brought up already, but those ATOM_* are mostly continuous where they're declared, Maybe worth making it so having a comment like:
/* These need to be in order, used in verify_ref_format */
And then just doing:
used_atom[at].atom_type >= FIRST_OF_THOSE &&
used_atom[at].atom_type >= LAST_OF_THOSE
Maybe it's too magical and peeking under the hood of an enum...
Re: [PATCH 10/15] [GSOC] cat-file: add has_object_file() check
In reply to ZheNing Hu via GitGitGadgetQuoted 21 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
Use
has_object_file()inbatch_one_object()to check whether the input object exists. This can help us reject the missing oid when we letcat-file --batchuse ref-filter logic later.Mentored-by Christian Couder <[email protected]>Mentored-by Hariom Verma <[email protected]>Signed-off-by ZheNing Hu <[email protected]>--- builtin/cat-file.c | 7 +++++++ 1 file changed, 7 insertions(+)builtin/cat-file.c +0
-0@@ -428,6 +428,13 @@ static void batch_one_object(const char *obj_name,return ; }
builtin/cat-file.c static void batch_one_object(const char *obj_name, · line 439
Quoted 10 lines
builtin/cat-file.c +7
-0+if (!has_object_file (&data->oid)) { +printf ("%s missing\n" , + obj_name ? obj_name :oid_to_hex (&data->oid)); +fflush (stdout); +return ; + } +batch_object_write (obj_name, scratch, opt, data); }
In 12/15 the old "missing" is removed, so is this strictly redundant to what's in batch_object_write() for now, does it change anything?
Quoted 33 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
In order to let cat-file use ref-filter logic, let's do the following:
1. Change the type of member
formatin structbatch_optionstoref_format, we will pass it to ref-filter later.
2. Letbatch_objects()add atoms to format, and useverify_ref_format()to check atoms.
3. Useformat_ref_array_item()inbatch_object_write()to get the formatted data corresponding to the object. If the return value offormat_ref_array_item()is equals to zero, usebatch_write()to print object data; else if the return value is less than zero, usedie()to print the error message and exit; else if return value is greater than zero, only print the error message, but don't exit.
4. Usefree_ref_array_item_value()to free ref_array_item's value.Most of the atoms in
for-each-ref --formatare now supported, such as%(tree),%(parent),%(author),%(tagger),%(if),%(then),%(else),%(end). But these atoms will be rejected:%(refname),%(symref),%(upstream),%(push),%(worktreepath),%(flag),%(HEAD), because these atoms are unique to those objects that pointed to by a ref, "for-each-ref"'s family can naturally use these atoms, but not all objects are pointed to be a ref, so "cat-file" will not be able to use them.The performance for
git cat-file --batch-all-objects --batch-checkon the Git repository itself with performance testing toolhyperfinechanges from 669.4 ms ± 31.1 ms to 1.134 s ± 0.063 s.
Quoted 4 lines
The performance for `git cat-file --batch-all-objects --batch
Quoted 1 line
/dev/null` on the Git repository itself with performance testing
tool
timechange from "27.37s user 0.29s system 98% cpu 28.089 total" to "33.69s user 1.54s system 87% cpu 40.258 total".
This new feature is really nice, but that's a really bad performance regression. A lot of software in the wild relies on "cat-file --batch" to be *the* performant interface to git for mass-extrction of object data.
That's in increase of ~70% and ~20%, respectively. Have you dug into (e.g. with a profiler) where we're now spending all this time?
Quoted 13 lines · Ævar Arnfjörð Bjarmason
Quoted 9 lines · ZheNing Hu via GitGitGadget
The performance for
git cat-file --batch-all-objects --batch-checkon the Git repository itself with performance testing toolhyperfinechanges from 669.4 ms ± 31.1 ms to 1.134 s ± 0.063 s.The performance for `git cat-file --batch-all-objects --batch
Quoted 1 line
/dev/null` on the Git repository itself with performance testing
tool
timechange from "27.37s user 0.29s system 98% cpu 28.089 total" to "33.69s user 1.54s system 87% cpu 40.258 total".
Quoted 7 lines
This new feature is really nice, but that's a really bad performance regression. A lot of software in the wild relies on "cat-file --batch" to be *the* performant interface to git for mass-extrction of object data.
That's in increase of ~70% and ~20%, respectively. Have you dug into (e.g. with a profiler) where we're now spending all this time?
Yeah, I think it's better to discuss this performance issue as part of the discussion of this series, instead of as part of the discussion of the blog posts.
Re: [PATCH 14/15] [GSOC] cat-file: re-implement --textconv, --filters options
In reply to ZheNing HuQuoted 6 lines · ZheNing Hu
Quoted 3 lines
These global variables are problematic. The places that check them are quite isolated, and it makes me wonder why we cannot pass a format instance through the callchain down to
get_object().If we pass such two options In such a deep call chain, it seems not worth to do it.
OK. After all, ref-filter.c has too many existing global variables (starting from the used_atom[] array) that makes it impossible to be a proper API that can be used to handle more than one invocations at once, so two more globals aren't making it any more hopeless than it currently is. If we wanted to make the API reentrant, we'd need a bigger change anyway.
Quoted 1 line
Or just use "static" prefix?
Yes, that is needed regardless to pass "make sparse".
TShanks.
Re: [PATCH 08/15] [GSOC] ref-filter: add cat_file_mode in struct ref_format
In reply to Ævar Arnfjörð BjarmasonQuoted 14 lines · Ævar Arnfjörð Bjarmason
ref-filter.c int verify_ref_format(struct ref_format *format) · line 1032
Quoted 9 lines · ZheNing Hu via GitGitGadget
ref-filter.c +9
-0+if ((!format->cat_file_mode && used_atom[at].atom_type ==ATOM_REST ) || + (format->cat_file_mode && (used_atom[at].atom_type ==ATOM_FLAG || + used_atom[at].atom_type ==ATOM_HEAD || + used_atom[at].atom_type ==ATOM_PUSH || + used_atom[at].atom_type ==ATOM_REFNAME || + used_atom[at].atom_type ==ATOM_SYMREF || + used_atom[at].atom_type ==ATOM_UPSTREAM || + used_atom[at].atom_type ==ATOM_WORKTREEPATH ))) +die (_ ("this command reject atom %%(%.*s)" ), (int )(ep - sp -2 ), sp +2 );Maybe it was brought up already, but those ATOM_* are mostly continuous where they're declared, Maybe worth making it so having a comment like:
Quoted 8 lines
/* These need to be in order, used in verify_ref_format */And then just doing:
used_atom[at].atom_type >=
FIRST_OF_THOSE&&
used_atom[at].atom_type >=LAST_OF_THOSEMaybe it's too magical and peeking under the hood of an enum...
Aside from the potential maintenance burden of the atom_type >= ... && atom_type <= ... approach, another problem is that it increases cognitive load. The reader has to go reference the enum to fully understand the cases to which this code applies. On the other hand, the way the patch mentions the enumeration items explicitly, it is obvious at-a-glance to which cases the code applies. An additional downside of the suggestion is that the range specified by >= and <= may cause some readers to think that there is some sort of implicit relationship between the items in the range, which doesn't seem to be the case. So, I find the way it's done in the patch presently easier to comprehend.
Re: [PATCH 08/15] [GSOC] ref-filter: add cat_file_mode in struct ref_format
Quoted 12 lines · Eric Sunshine
Aside from the potential maintenance burden of the
atom_type >= ... && atom_type <= ...approach, another problem is that it increases cognitive load. The reader has to go reference the enum to fully understand the cases to which this code applies. On the other hand, the way the patch mentions the enumeration items explicitly, it is obvious at-a-glance to which cases the code applies. An additional downside of the suggestion is that the range specified by>=and<=may cause some readers to think that there is some sort of implicit relationship between the items in the range, which doesn't seem to be the case. So, I find the way it's done in the patch presently easier to comprehend.
I agree that it's less cognitive load, but maybe it could be improved using a separate function like:
static int reject_atom(int cat_file_mode, enum atom_type atom_type)
{
if (!cat_file_mode)
return atom_type == ATOM_REST;
/* cat_file_mode */
switch (atom_type) {
case ATOM_FLAG:
case ATOM_HEAD:
case ATOM_PUSH:
case ATOM_REFNAME:
case ATOM_SYMREF:
case ATOM_UPSTREAM:
case ATOM_WORKTREEPATH:
return 1;
default:
return 0;
}
}Re: [PATCH 02/15] [GSOC] ref-filter: add %(raw) atom
In reply to Ævar Arnfjörð BjarmasonQuoted 18 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年7月2日周五 下午9:27写道:
ref-filter.c static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp · line 1401
Quoted 5 lines · ZheNing Hu via GitGitGadget
ref-filter.c +1
-0@@ -1362,17 +1398,29 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct expconst char *subpos =NULL , *bodypos =NULL , *sigpos =NULL ;size_t sublen =0 , bodylen =0 , nonsiglen =0 , siglen =0 ;void *buf = data->content; +unsigned long buf_size = data->size;Let's declare this.
Quoted 6 lines
ref-filter.c +1
-0for (i =0 ; i < used_atom_cnt; i++) {struct used_atom *atom = &used_atom[i];const char *name = atom->name;struct atom_value *v = &val[i]; +enum atom_type atom_type = atom->atom_type;
Quoted 9 lines
ref-filter.c static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp · line 1414
Quoted 6 lines
ref-filter.c +1
-0if (!!deref != (*name =='*' ))continue ;if (deref) name++; +if (atom_type ==ATOM_RAW ) {Here, where it's going to be used.
Yeah, buf_size is only used here.
Quoted 4 lines
Quoted 4 lines
t/t6300-for-each-ref.sh +0
-0
Quoted 8 lines
t/t6300-for-each-ref.sh test_atom head parent:short=10 '' · line 134
Quoted 6 lines
t/t6300-for-each-ref.sh +2
-0@@ -130,6 +130,8 @@ test_atom head parent:short=10 '' test_atom head numparent 0 test_atom head object '' test_atom head type '' +test_atom head raw "$(git cat-file commit refs/heads/main) +"Extra stay line here, is the \n intentional?
I think so. \n is necessary.
In cmd_for_each_ref(), there have a extern "putchar('\n');" In cat_one_file(), write_or_die() output origin data of the object.
Thanks.
--
ZheNing Hu
Re: [PATCH 03/15] [GSOC] ref-filter: --format=%(raw) re-support --perl
In reply to Ævar Arnfjörð BjarmasonQuoted 37 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年7月2日周五 下午9:30写道:
Quoted 9 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
Because the perl language can handle binary data correctly, add the function
perl_quote_buf_with_len(), which can specify the length of the data and prevent the data from being truncated at '\0' to help--format="%(raw)"re-support--perl.Signed-off-by ZheNing Hu <[email protected]>Thanks!
Quoted 21 lines
t/t6300-for-each-ref.sh +17
-2-test_expect_success '%(raw) with --perl must fail' ' - test_must_fail git for-each-ref --format="%(raw)" --perl +test_expect_success '%(raw) with --perl' ' + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob1 --perl | perl > actual && + cmp blob1 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob3 --perl | perl > actual && + cmp blob3 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob8 --perl | perl > actual && + cmp blob8 actual && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/first --perl | perl > actual && + cmp one actual && + git cat-file tree refs/mytrees/first > expected && + git for-each-ref --format="\$name= %(raw); +print \"\$name\"" refs/mytrees/first --perl | perl > actual && + cmp expected actual '
Quoted 5 lines
Quoted 1 line
test_expect_success'%(raw) with --shell must fail' 'Nit: Formatting can be "| perl >actual", not "| perl > actual", like the usual style...
Yeah, thanks.
--
ZheNing Hu
Re: [PATCH 10/15] [GSOC] cat-file: add has_object_file() check
In reply to Ævar Arnfjörð BjarmasonQuoted 26 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年7月2日周五 下午9:34写道:
Quoted 22 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
Use
has_object_file()inbatch_one_object()to check whether the input object exists. This can help us reject the missing oid when we letcat-file --batchuse ref-filter logic later.Mentored-by Christian Couder <[email protected]>Mentored-by Hariom Verma <[email protected]>Signed-off-by ZheNing Hu <[email protected]>--- builtin/cat-file.c | 7 +++++++ 1 file changed, 7 insertions(+)builtin/cat-file.c +0
-0@@ -428,6 +428,13 @@ static void batch_one_object(const char *obj_name,return ; }
Quoted 12 lines
builtin/cat-file.c static void batch_one_object(const char *obj_name, · line 439
Quoted 9 lines
builtin/cat-file.c +7
-0+if (!has_object_file (&data->oid)) { +printf ("%s missing\n" , + obj_name ? obj_name :oid_to_hex (&data->oid)); +fflush (stdout); +return ; + } +batch_object_write (obj_name, scratch, opt, data); }In 12/15 the old "missing" is removed, so is this strictly redundant to what's in
batch_object_write()for now, does it change anything?
Both of these commits are used to pass some tests for checking missing objects.
If we don't have this commit, "empty --batch-check notices missing object" in "t/t1006-cat-file.sh"
will fail;
If we don't have the commit "[GSOC] ref-filter: modify the error message and value in get_object",
"bogus OFS_DELTA in packfile" in "t/t5313-pack-bounds-checks.sh" will fail.
Thanks.
--
ZheNing Hu
Re: [PATCH 08/15] [GSOC] ref-filter: add cat_file_mode in struct ref_format
In reply to Christian CouderQuoted 29 lines · Christian Couder <[email protected]> 于2021年7月3日周六 上午6:11写道:
Quoted 12 lines · Eric Sunshine
Aside from the potential maintenance burden of the
atom_type >= ... && atom_type <= ...approach, another problem is that it increases cognitive load. The reader has to go reference the enum to fully understand the cases to which this code applies. On the other hand, the way the patch mentions the enumeration items explicitly, it is obvious at-a-glance to which cases the code applies. An additional downside of the suggestion is that the range specified by>=and<=may cause some readers to think that there is some sort of implicit relationship between the items in the range, which doesn't seem to be the case. So, I find the way it's done in the patch presently easier to comprehend.I agree that it's less cognitive load, but maybe it could be improved using a separate function like:
static int
reject_atom(int cat_file_mode, enum atom_type atom_type){ if (!cat_file_mode) return atom_type == ATOM_REST; /* cat_file_mode */ switch (atom_type) { case ATOM_FLAG: case ATOM_HEAD: case ATOM_PUSH: case ATOM_REFNAME:
Quoted 8 lines
case ATOM_SYMREF:
case ATOM_UPSTREAM:
case ATOM_WORKTREEPATH:
return 1;
default:
return 0;
}
}I agree. It is clearer to use a function to wrap origin reject atoms step.
Thanks.
--
ZheNIng Hu
Re: [PATCH 14/15] [GSOC] cat-file: re-implement --textconv, --filters options
In reply to Junio C HamanoQuoted 10 lines · Junio C Hamano <[email protected]> 于2021年7月2日周五 下午11:27写道:
Quoted 6 lines · ZheNing Hu
Quoted 3 lines
These global variables are problematic. The places that check them are quite isolated, and it makes me wonder why we cannot pass a format instance through the callchain down to
get_object().If we pass such two options In such a deep call chain, it seems not worth to do it.
Quoted 7 lines
OK. After all,
ref-filter.chas too many existing global variables (starting from the used_atom[] array) that makes it impossible to be a proper API that can be used to handle more than one invocations at once, so two more globals aren't making it any more hopeless than it currently is. If we wanted to make the API reentrant, we'd need a bigger change anyway.
Yes, Using global variables may make the code non-reentrant. Now I think because the "rest" is already in struct ref_array_item, we can emulate "rest", adding these two flags "use_textconv" and "use_filter" to struct ref_array_item. Then we can use them in get_object().
Quoted 5 lines
Quoted 1 line
Or just use "static" prefix?
Yes, that is needed regardless to pass "make sparse".
TShanks.
Thanks.
--
ZheNIng Hu
Quoted 38 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年7月2日周五 下午9:39写道:
Quoted 34 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
In order to let cat-file use ref-filter logic, let's do the following:
1. Change the type of member
formatin structbatch_optionstoref_format, we will pass it to ref-filter later.
2. Letbatch_objects()add atoms to format, and useverify_ref_format()to check atoms.
3. Useformat_ref_array_item()inbatch_object_write()to get the formatted data corresponding to the object. If the return value offormat_ref_array_item()is equals to zero, usebatch_write()to print object data; else if the return value is less than zero, usedie()to print the error message and exit; else if return value is greater than zero, only print the error message, but don't exit.
4. Usefree_ref_array_item_value()to free ref_array_item's value.Most of the atoms in
for-each-ref --formatare now supported, such as%(tree),%(parent),%(author),%(tagger),%(if),%(then),%(else),%(end). But these atoms will be rejected:%(refname),%(symref),%(upstream),%(push),%(worktreepath),%(flag),%(HEAD), because these atoms are unique to those objects that pointed to by a ref, "for-each-ref"'s family can naturally use these atoms, but not all objects are pointed to be a ref, so "cat-file" will not be able to use them.The performance for
git cat-file --batch-all-objects --batch-checkon the Git repository itself with performance testing toolhyperfinechanges from 669.4 ms ± 31.1 ms to 1.134 s ± 0.063 s.The performance for `git cat-file --batch-all-objects --batch
Quoted 9 lines
Quoted 3 lines
Quoted 1 line
/dev/null` on the Git repository itself with performance testing
tool
timechange from "27.37s user 0.29s system 98% cpu 28.089 total" to "33.69s user 1.54s system 87% cpu 40.258 total".This new feature is really nice, but that's a really bad performance regression. A lot of software in the wild relies on "cat-file --batch" to be *the* performant interface to git for mass-extrction of object data.
Thanks, this performance is indeed worrying.
Quoted 2 lines
That's in increase of ~70% and ~20%, respectively. Have you dug into (e.g. with a profiler) where we're now spending all this time?
See this two attachment about performance flame graph, oid_object_info_extended() in get_object() is the key to performance limitations.
--
ZheNing Hu
Quoted 5 lines · ZheNing Hu <[email protected]> 于2021年7月3日周六 下午7:45写道:
See this two attachment about performance flame graph,
oid_object_info_extended()inget_object()is the key to performance limitations.
Well, from the current point of view, The time of oid_object_info_extended() before and after reconstruction is similar. But after refactoring, there are a lot
of additional detections in ref-filter.
E.g. append_literal() 1.68% append_atom 1.63% ...
--
ZheNing Hu
Quoted 49 lines · ZheNing Hu
Quoted 38 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年7月2日周五 下午9:39写道:
Quoted 34 lines · ZheNing Hu via GitGitGadget
From: ZheNing Hu <[email protected]>
In order to let cat-file use ref-filter logic, let's do the following:
1. Change the type of member
formatin structbatch_optionstoref_format, we will pass it to ref-filter later.
2. Letbatch_objects()add atoms to format, and useverify_ref_format()to check atoms.
3. Useformat_ref_array_item()inbatch_object_write()to get the formatted data corresponding to the object. If the return value offormat_ref_array_item()is equals to zero, usebatch_write()to print object data; else if the return value is less than zero, usedie()to print the error message and exit; else if return value is greater than zero, only print the error message, but don't exit.
4. Usefree_ref_array_item_value()to free ref_array_item's value.Most of the atoms in
for-each-ref --formatare now supported, such as%(tree),%(parent),%(author),%(tagger),%(if),%(then),%(else),%(end). But these atoms will be rejected:%(refname),%(symref),%(upstream),%(push),%(worktreepath),%(flag),%(HEAD), because these atoms are unique to those objects that pointed to by a ref, "for-each-ref"'s family can naturally use these atoms, but not all objects are pointed to be a ref, so "cat-file" will not be able to use them.The performance for
git cat-file --batch-all-objects --batch-checkon the Git repository itself with performance testing toolhyperfinechanges from 669.4 ms ± 31.1 ms to 1.134 s ± 0.063 s.The performance for `git cat-file --batch-all-objects --batch
Quoted 9 lines
Quoted 3 lines
Quoted 1 line
/dev/null` on the Git repository itself with performance testing
tool
timechange from "27.37s user 0.29s system 98% cpu 28.089 total" to "33.69s user 1.54s system 87% cpu 40.258 total".This new feature is really nice, but that's a really bad performance regression. A lot of software in the wild relies on "cat-file --batch" to be *the* performant interface to git for mass-extrction of object data.
Quoted 8 lines
Thanks, this performance is indeed worrying.
Quoted 2 lines
That's in increase of ~70% and ~20%, respectively. Have you dug into (e.g. with a profiler) where we're now spending all this time?
See this two attachment about performance flame graph,
oid_object_info_extended()inget_object()is the key to performance limitations.
Most of the problem, although this may not entirely fix the performance regression, is that you're either looking up everything twice now, or taking a much more expensive path.
I think using gprof is probably much more handy here. See [1. I did a git rev-list --all >rla and ran that piped into 'git cat-file --batch' with/without your pathces. Results:
$ gprof ./git-master ./gmon-master.out | head -n 10
Flat profile: Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
14.29 0.02 0.02 475186 0.00 0.00 nth_packed_object_offset
14.29 0.04 0.02 237835 0.00 0.00 hash_to_hex_algop_r
7.14 0.05 0.01 5220425 0.00 0.00 hashcmp_algop
7.14 0.06 0.01 4757120 0.00 0.00 hex2chr
7.14 0.07 0.01 1732023 0.00 0.00 find_entry_ptrAnd:
$ gprof ./git-new ./gmon-new.out |head -n 10
Flat profile: Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
7.32 0.06 0.06 764570 0.00 0.00 lookup_object
7.32 0.12 0.06 237835 0.00 0.00 parse_commit_date
4.88 0.16 0.04 712779 0.00 0.00 nth_packed_object_offset
3.66 0.19 0.03 964574 0.00 0.00 bsearch_hash
3.66 0.22 0.03 237835 0.00 0.00 grab_sub_body_contentsIf you e.g. make lookup_object() simply die when it's called you'll see that before we don't call it at all, after your patch it's our #1 function.
Before when we have the simplest case of writing out an object this is our callstack:
(gdb) bt
#0 batch_write (opt=0x7fffffffde50, data=0x555555ab9470, len=52) at builtin/cat-file.c:298
#1 0x000055555558b160 in batch_object_write (obj_name=0x55555597cef0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd8c0,
opt=0x7fffffffde50, data=0x7fffffffd7f0) at builtin/cat-file.c:375
#2 0x000055555558b36e in batch_one_object (obj_name=0x55555597cef0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd8c0, opt=0x7fffffffde50,
data=0x7fffffffd7f0) at builtin/cat-file.c:431
#3 0x000055555558b8ed in batch_objects (opt=0x7fffffffde50) at builtin/cat-file.c:588
#4 0x000055555558c0d3 in cmd_cat_file (argc=0, argv=0x7fffffffe1e0, prefix=0x0) at builtin/cat-file.c:716
#5 0x0000555555573adb in run_builtin (p=0x555555941870 <commands+240>, argc=2, argv=0x7fffffffe1e0) at git.c:461
#6 0x0000555555573f00 in handle_builtin (argc=2, argv=0x7fffffffe1e0) at git.c:714
#7 0x0000555555574182 in run_argv (argcp=0x7fffffffe08c, argv=0x7fffffffe080) at git.c:781
#8 0x000055555557460f in cmd_main (argc=2, argv=0x7fffffffe1e0) at git.c:912
#9 0x000055555565b508 in main (argc=3, argv=0x7fffffffe1d8) at common-main.c:52After (well, here we're not even to writing it, just looking it up), the BUG() is my addition:
(gdb) bt
#0 BUG_fl (file=0x5555558ade71 "object.c", line=91, fmt=0x5555558ade6e "yo") at usage.c:290
#1 0x00005555557441ca in lookup_object (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>) at object.c:91
#2 0x000055555569dfc8 in lookup_commit (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>) at commit.c:62
#3 0x00005555557445f5 in parse_object_buffer (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>, type=OBJ_COMMIT, size=342, buffer=0x555555ab48e0,
eaten_p=0x7fffffffd36c) at object.c:215
#4 0x0000555555785094 in get_object (ref=0x7fffffffd6b0, deref=0, obj=0x7fffffffd520, oi=0x555555975160 <oi>, err=0x7fffffffd860) at ref-filter.c:1803
#5 0x0000555555785c99 in populate_value (ref=0x7fffffffd6b0, err=0x7fffffffd860) at ref-filter.c:2030
#6 0x0000555555785d7b in get_ref_atom_value (ref=0x7fffffffd6b0, atom=0, v=0x7fffffffd628, err=0x7fffffffd860) at ref-filter.c:2064
#7 0x000055555578742f in format_ref_array_item (info=0x7fffffffd6b0, format=0x7fffffffde30, final_buf=0x7fffffffd880, error_buf=0x7fffffffd860)
at ref-filter.c:2659
#8 0x000055555558ab1c in batch_object_write (obj_name=0x55555597e3f0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd880,
err=0x7fffffffd860, opt=0x7fffffffde10, data=0x7fffffffd800) at builtin/cat-file.c:225
#9 0x000055555558ade5 in batch_one_object (obj_name=0x55555597e3f0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd880, err=0x7fffffffd860,
opt=0x7fffffffde10, data=0x7fffffffd800) at builtin/cat-file.c:298
#10 0x000055555558b394 in batch_objects (batch=0x7fffffffde10, options=0x7fffffffd900) at builtin/cat-file.c:458
#11 0x000055555558bbd5 in cmd_cat_file (argc=0, argv=0x7fffffffe1d0, prefix=0x0) at builtin/cat-file.c:585
#12 0x0000555555573adb in run_builtin (p=0x555555942850 <commands+240>, argc=2, argv=0x7fffffffe1d0) at git.c:461
#13 0x0000555555573f00 in handle_builtin (argc=2, argv=0x7fffffffe1d0) at git.c:714
#14 0x0000555555574182 in run_argv (argcp=0x7fffffffe07c, argv=0x7fffffffe070) at git.c:781
#15 0x000055555557460f in cmd_main (argc=2, argv=0x7fffffffe1d0) at git.c:912
#16 0x000055555565afc1 in main (argc=3, argv=0x7fffffffe1c8) at common-main.c:52I.e. before in batch_object_write() we could use a cheap path of doing oid_object_info_extended() and directly emitting the content. With your version we're all the way down to parse_object_buffer(). Meaning that we're going to be creating a "struct commit" or whatever if we're looking at a commit, just to print out the raw contents.
I think the best next step here is to add a t/perf/t1006-cat-file.sh test to stress these various cases, i.e. a plain --batch without a format, with format, with --batch-all-objects etc. Try to then run that on each of your commits against the preceding one and see if/when you have regressions.
Aside from any double-lookups etc, the problem is also that you're trying to handle a really general case (e.g. with textconv) in a codepath that needs to be really fast. If anything we should be inserting some more more optimization shortcuts for common cases into it. E.g. I was able to trivially speed up 'cat-file --batch-check' on "master" by hardcoding a path for our default format (patch at the end of this mail):
# passed all 2 test(s)
1..2
Test origin/master HEAD
-------------------------------------------------------------------------
1006.2: cat-file --batch-check 0.60(0.37+0.23) 0.35(0.33+0.02) -41.7%Anything that needs to handle general format patching is going to be slower. I think /some/ performance regression if we're using something that's not just the current light strbuf_expand() probably can't be avoided, but we could/should try to make up the difference at least for the common case of --batch or --batch-check without --textconv and perhaps hardcode (and document that it's faster) a path for the default formats).
1. https://sourceware.org/binutils/docs/gprof/Output.html
builtin/cat-file.c +19 -8
@@ -360,6 +360,11 @@ static void batch_object_write(const char *obj_name,struct batch_options *opt, struct expand_data *data) { + int default_format = !strcmp(opt->format, "%(objectname) %(objecttype) %(objectsize)"); +struct strbuf type_name =STRBUF_INIT ; +if (default_format) + data->info.type_name = &type_name; +if (!data->skip_object_info &&oid_object_info_extended (the_repository, &data->oid, &data->info,OBJECT_INFO_LOOKUP_REPLACE ) <0 ) {
@@ -369,14 +374,20 @@ static void batch_object_write(const char *obj_name,return ; } -strbuf_reset (scratch); -strbuf_expand (scratch, opt->format, expand_format, data); -strbuf_addch (scratch,'\n' ); -batch_write (opt, scratch->buf, scratch->len); - -if (opt->print_contents) { -print_object_or_die (opt, data); -batch_write (opt,"\n" ,1 ); +if (default_format && !opt->print_contents) { +fprintf (stdout,"%s %s %" PRIuMAX"\n" ,oid_to_hex (&data->oid), + data->info.type_name->buf, + (uintmax_t)*data->info.sizep); + }else { +strbuf_reset (scratch); +strbuf_expand (scratch, opt->format, expand_format, data); +strbuf_addch (scratch,'\n' ); +batch_write (opt, scratch->buf, scratch->len); + +if (opt->print_contents) { +print_object_or_die (opt, data); +batch_write (opt,"\n" ,1 ); + } } }
t/perf/p1006-cat-file.sh new file +16 -0
@@ -0,0 +1,16 @@ +#!/bin/sh + +test_description='Basic sort performance tests' +. ./perf-lib.sh + +test_perf_default_repo + +test_expect_success 'setup' ' + git rev-list --all >rla +' + +test_perf 'cat-file --batch-check' ' + git cat-file --batch-check <rla +' + +test_done
Quoted 4 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年7月3日周六 下午10:18写道:
Most of the problem, although this may not entirely fix the performance regression, is that you're either looking up everything twice now, or taking a much more expensive path.
Yeah, In get_object(), oid_object_info_extended() is called twice when we want print object's contents. The original intention is to reduce the call of oid_object_info_extended() once when using --textconv. Should we make --textconv and --filters have the same logic? In this way, without using --textconv and --filters, we can call oid_object_info_extended() only once.
Quoted 21 lines
I think using gprof is probably much more handy here. See [1. I did a
git rev-list --all >rlaand ran that piped into 'git cat-file --batch' with/without your pathces. Results:$ gprof ./git-master ./gmon-master.out | head -n 10 Flat profile:Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 14.29 0.02 0.02 475186 0.00 0.00 nth_packed_object_offset 14.29 0.04 0.02 237835 0.00 0.00 hash_to_hex_algop_r 7.14 0.05 0.01 5220425 0.00 0.00 hashcmp_algop 7.14 0.06 0.01 4757120 0.00 0.00 hex2chr 7.14 0.07 0.01 1732023 0.00 0.00 find_entry_ptrAnd:
$ gprof ./git-new ./gmon-new.out |head -n 10 Flat profile:
Quoted 9 lines
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
7.32 0.06 0.06 764570 0.00 0.00 lookup_object
7.32 0.12 0.06 237835 0.00 0.00 parse_commit_date
4.88 0.16 0.04 712779 0.00 0.00 nth_packed_object_offset
3.66 0.19 0.03 964574 0.00 0.00 bsearch_hash
3.66 0.22 0.03 237835 0.00 0.00 grab_sub_body_contentsIt seems that lookup_object() took a lot of time with the patch of my version .
Quoted 38 lines
If you e.g. make
lookup_object()simply die when it's called you'll see that before we don't call it at all, after your patch it's our #1 function.Before when we have the simplest case of writing out an object this is our callstack:
(gdb) bt #0 batch_write (opt=0x7fffffffde50, data=0x555555ab9470, len=52) at builtin/cat-file.c:298 #1 0x000055555558b160 in batch_object_write (obj_name=0x55555597cef0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd8c0, opt=0x7fffffffde50, data=0x7fffffffd7f0) at builtin/cat-file.c:375 #2 0x000055555558b36e in batch_one_object (obj_name=0x55555597cef0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd8c0, opt=0x7fffffffde50, data=0x7fffffffd7f0) at builtin/cat-file.c:431 #3 0x000055555558b8ed in batch_objects (opt=0x7fffffffde50) at builtin/cat-file.c:588 #4 0x000055555558c0d3 in cmd_cat_file (argc=0, argv=0x7fffffffe1e0, prefix=0x0) at builtin/cat-file.c:716 #5 0x0000555555573adb in run_builtin (p=0x555555941870 <commands+240>, argc=2, argv=0x7fffffffe1e0) at git.c:461 #6 0x0000555555573f00 in handle_builtin (argc=2, argv=0x7fffffffe1e0) at git.c:714 #7 0x0000555555574182 in run_argv (argcp=0x7fffffffe08c, argv=0x7fffffffe080) at git.c:781 #8 0x000055555557460f in cmd_main (argc=2, argv=0x7fffffffe1e0) at git.c:912 #9 0x000055555565b508 in main (argc=3, argv=0x7fffffffe1d8) at common-main.c:52After (well, here we're not even to writing it, just looking it up), the
BUG()is my addition:(gdb) bt #0 BUG_fl (file=0x5555558ade71 "object.c", line=91, fmt=0x5555558ade6e "yo") at usage.c:290 #1 0x00005555557441ca in lookup_object (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>) at object.c:91 #2 0x000055555569dfc8 in lookup_commit (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>) at commit.c:62 #3 0x00005555557445f5 in parse_object_buffer (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>, type=OBJ_COMMIT, size=342, buffer=0x555555ab48e0, eaten_p=0x7fffffffd36c) at object.c:215 #4 0x0000555555785094 in get_object (ref=0x7fffffffd6b0, deref=0, obj=0x7fffffffd520, oi=0x555555975160 <oi>, err=0x7fffffffd860) at ref-filter.c:1803 #5 0x0000555555785c99 in populate_value (ref=0x7fffffffd6b0, err=0x7fffffffd860) at ref-filter.c:2030 #6 0x0000555555785d7b in get_ref_atom_value (ref=0x7fffffffd6b0, atom=0, v=0x7fffffffd628, err=0x7fffffffd860) at ref-filter.c:2064 #7 0x000055555578742f in format_ref_array_item (info=0x7fffffffd6b0, format=0x7fffffffde30, final_buf=0x7fffffffd880, error_buf=0x7fffffffd860) at ref-filter.c:2659 #8 0x000055555558ab1c in batch_object_write (obj_name=0x55555597e3f0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd880, err=0x7fffffffd860, opt=0x7fffffffde10, data=0x7fffffffd800) at builtin/cat-file.c:225 #9 0x000055555558ade5 in batch_one_object (obj_name=0x55555597e3f0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd880, err=0x7fffffffd860,
Quoted 9 lines
opt=0x7fffffffde10, data=0x7fffffffd800) at builtin/cat-file.c:298
#10 0x000055555558b394 in batch_objects (batch=0x7fffffffde10, options=0x7fffffffd900) at builtin/cat-file.c:458
#11 0x000055555558bbd5 in cmd_cat_file (argc=0, argv=0x7fffffffe1d0, prefix=0x0) at builtin/cat-file.c:585
#12 0x0000555555573adb in run_builtin (p=0x555555942850 <commands+240>, argc=2, argv=0x7fffffffe1d0) at git.c:461
#13 0x0000555555573f00 in handle_builtin (argc=2, argv=0x7fffffffe1d0) at git.c:714
#14 0x0000555555574182 in run_argv (argcp=0x7fffffffe07c, argv=0x7fffffffe070) at git.c:781
#15 0x000055555557460f in cmd_main (argc=2, argv=0x7fffffffe1d0) at git.c:912
#16 0x000055555565afc1 in main (argc=3, argv=0x7fffffffe1c8) at common-main.c:52It seems that the call stack is very deep after using my version.
Quoted 6 lines
I.e. before in
batch_object_write()we could use a cheap path of doingoid_object_info_extended()and directly emitting the content. With your version we're all the way down toparse_object_buffer(). Meaning that we're going to be creating a "struct commit" or whatever if we're looking at a commit, just to print out the raw contents.
I agree that the logic in ref-filter is too complicated in order to be able to print
the structured object data.
Quoted 6 lines
I think the best next step here is to add a
t/perf/t1006-cat-file.shtest to stress these various cases, i.e. a plain --batch without a format, with format, with --batch-all-objects etc. Try to then run that on each of your commits against the preceding one and see if/when you have regressions.
Make sence.
Quoted 2 lines
Aside from any double-lookups etc, the problem is also that you're trying to handle a really general case (e.g. with textconv) in a
Well, "--textconv" is a common situation? Here I may not be sure which scenarios where the upper application calls "git cat-file --batch" are the most common.
Quoted 12 lines
codepath that needs to be really fast. If anything we should be inserting some more more optimization shortcuts for common cases into it. E.g. I was able to trivially speed up 'cat-file --batch-check' on "master" by hardcoding a path for our default format (patch at the end of this mail):
# passed all 2 test(s) 1..2 Test origin/master HEAD ------------------------------------------------------------------------- 1006.2: cat-file --batch-check 0.60(0.37+0.23) 0.35(0.33+0.02) -41.7%
Quoted 8 lines
Anything that needs to handle general format patching is going to be slower. I think /some/ performance regression if we're using something that's not just the current light
strbuf_expand()probably can't be avoided, but we could/should try to make up the difference at least for the common case of --batch or --batch-check without --textconv and perhaps hardcode (and document that it's faster) a path for the default formats).
Yeah, Like the hardcode in your patch may be a solution to the performance degradation. This will indeed help those upper-level applications that use the
common case.
Quoted 59 lines
1. https://sourceware.org/binutils/docs/gprof/Output.html
builtin/cat-file.c +19
-8@@ -360,6 +360,11 @@ static void batch_object_write(const char *obj_name,struct batch_options *opt, struct expand_data *data) { + int default_format = !strcmp(opt->format, "%(objectname) %(objecttype) %(objectsize)"); +struct strbuf type_name =STRBUF_INIT ; +if (default_format) + data->info.type_name = &type_name; +if (!data->skip_object_info &&oid_object_info_extended (the_repository, &data->oid, &data->info,OBJECT_INFO_LOOKUP_REPLACE ) <0 ) {@@ -369,14 +374,20 @@ static void batch_object_write(const char *obj_name,return ; } -strbuf_reset (scratch); -strbuf_expand (scratch, opt->format, expand_format, data); -strbuf_addch (scratch,'\n' ); -batch_write (opt, scratch->buf, scratch->len); - -if (opt->print_contents) { -print_object_or_die (opt, data); -batch_write (opt,"\n" ,1 ); +if (default_format && !opt->print_contents) { +fprintf (stdout,"%s %s %" PRIuMAX"\n" ,oid_to_hex (&data->oid), + data->info.type_name->buf, + (uintmax_t)*data->info.sizep); + }else { +strbuf_reset (scratch); +strbuf_expand (scratch, opt->format, expand_format, data); +strbuf_addch (scratch,'\n' ); +batch_write (opt, scratch->buf, scratch->len); + +if (opt->print_contents) { +print_object_or_die (opt, data); +batch_write (opt,"\n" ,1 ); + } } }t/perf/p1006-cat-file.sh new file +6
-0@@ -0,0 +1,16 @@ +#!/bin/sh + +test_description='Basic sort performance tests' +. ./perf-lib.sh + +test_perf_default_repo
t/perf/p1006-cat-file.sh line 16
Quoted 10 lines
t/perf/p1006-cat-file.sh new file +10
-0+ +test_expect_success 'setup' ' + git rev-list --all >rla +' + +test_perf 'cat-file --batch-check' ' + git cat-file --batch-check <rla +' + +test_done
Thanks.
--
ZheNing Hu
Re: [PATCH 08/15] [GSOC] ref-filter: add cat_file_mode in struct ref_format
In reply to ZheNing HuQuoted 39 lines · ZheNing Hu
Quoted 29 lines · Christian Couder <[email protected]> 于2021年7月3日周六 上午6:11写道:
Quoted 12 lines · Eric Sunshine
Aside from the potential maintenance burden of the
atom_type >= ... && atom_type <= ...approach, another problem is that it increases cognitive load. The reader has to go reference the enum to fully understand the cases to which this code applies. On the other hand, the way the patch mentions the enumeration items explicitly, it is obvious at-a-glance to which cases the code applies. An additional downside of the suggestion is that the range specified by>=and<=may cause some readers to think that there is some sort of implicit relationship between the items in the range, which doesn't seem to be the case. So, I find the way it's done in the patch presently easier to comprehend.I agree that it's less cognitive load, but maybe it could be improved using a separate function like:
static int
reject_atom(int cat_file_mode, enum atom_type atom_type){ if (!cat_file_mode) return atom_type == ATOM_REST; /* cat_file_mode */ switch (atom_type) { case ATOM_FLAG: case ATOM_HEAD: case ATOM_PUSH: case ATOM_REFNAME:Quoted 8 lines
case ATOM_SYMREF: case ATOM_UPSTREAM: case ATOM_WORKTREEPATH: return 1; default: return 0; } }
Quoted 3 lines
I agree. It is clearer to use a function to wrap origin reject atoms step.
Thanks.
Yeah I guess you're both right, I just found the dense giant if to be hard to read, but having a helper is even better.
Quoted 103 lines · ZheNing Hu
Quoted 4 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年7月3日周六 下午10:18写道:
Most of the problem, although this may not entirely fix the performance regression, is that you're either looking up everything twice now, or taking a much more expensive path.
Yeah, In
get_object(),oid_object_info_extended()is called twice when we want print object's contents. The original intention is to reduce the call ofoid_object_info_extended()once when using --textconv. Should we make --textconv and --filters have the same logic? In this way, without using --textconv and --filters, we can calloid_object_info_extended()only once.Quoted 21 lines
I think using gprof is probably much more handy here. See [1. I did a
git rev-list --all >rlaand ran that piped into 'git cat-file --batch' with/without your pathces. Results:$ gprof ./git-master ./gmon-master.out | head -n 10 Flat profile:Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 14.29 0.02 0.02 475186 0.00 0.00 nth_packed_object_offset 14.29 0.04 0.02 237835 0.00 0.00 hash_to_hex_algop_r 7.14 0.05 0.01 5220425 0.00 0.00 hashcmp_algop 7.14 0.06 0.01 4757120 0.00 0.00 hex2chr 7.14 0.07 0.01 1732023 0.00 0.00 find_entry_ptrAnd:
$ gprof ./git-new ./gmon-new.out |head -n 10 Flat profile:Quoted 9 lines
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 7.32 0.06 0.06 764570 0.00 0.00 lookup_object 7.32 0.12 0.06 237835 0.00 0.00 parse_commit_date 4.88 0.16 0.04 712779 0.00 0.00 nth_packed_object_offset 3.66 0.19 0.03 964574 0.00 0.00 bsearch_hash 3.66 0.22 0.03 237835 0.00 0.00 grab_sub_body_contentsIt seems that
lookup_object()took a lot of time with the patch of my version .Quoted 38 lines
If you e.g. make
lookup_object()simply die when it's called you'll see that before we don't call it at all, after your patch it's our #1 function.Before when we have the simplest case of writing out an object this is our callstack:
(gdb) bt #0 batch_write (opt=0x7fffffffde50, data=0x555555ab9470, len=52) at builtin/cat-file.c:298 #1 0x000055555558b160 in batch_object_write (obj_name=0x55555597cef0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd8c0, opt=0x7fffffffde50, data=0x7fffffffd7f0) at builtin/cat-file.c:375 #2 0x000055555558b36e in batch_one_object (obj_name=0x55555597cef0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd8c0, opt=0x7fffffffde50, data=0x7fffffffd7f0) at builtin/cat-file.c:431 #3 0x000055555558b8ed in batch_objects (opt=0x7fffffffde50) at builtin/cat-file.c:588 #4 0x000055555558c0d3 in cmd_cat_file (argc=0, argv=0x7fffffffe1e0, prefix=0x0) at builtin/cat-file.c:716 #5 0x0000555555573adb in run_builtin (p=0x555555941870 <commands+240>, argc=2, argv=0x7fffffffe1e0) at git.c:461 #6 0x0000555555573f00 in handle_builtin (argc=2, argv=0x7fffffffe1e0) at git.c:714 #7 0x0000555555574182 in run_argv (argcp=0x7fffffffe08c, argv=0x7fffffffe080) at git.c:781 #8 0x000055555557460f in cmd_main (argc=2, argv=0x7fffffffe1e0) at git.c:912 #9 0x000055555565b508 in main (argc=3, argv=0x7fffffffe1d8) at common-main.c:52After (well, here we're not even to writing it, just looking it up), the
BUG()is my addition:(gdb) bt #0 BUG_fl (file=0x5555558ade71 "object.c", line=91, fmt=0x5555558ade6e "yo") at usage.c:290 #1 0x00005555557441ca in lookup_object (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>) at object.c:91 #2 0x000055555569dfc8 in lookup_commit (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>) at commit.c:62 #3 0x00005555557445f5 in parse_object_buffer (r=0x5555559755c0 <the_repo>, oid=0x555555975160 <oi>, type=OBJ_COMMIT, size=342, buffer=0x555555ab48e0, eaten_p=0x7fffffffd36c) at object.c:215 #4 0x0000555555785094 in get_object (ref=0x7fffffffd6b0, deref=0, obj=0x7fffffffd520, oi=0x555555975160 <oi>, err=0x7fffffffd860) at ref-filter.c:1803 #5 0x0000555555785c99 in populate_value (ref=0x7fffffffd6b0, err=0x7fffffffd860) at ref-filter.c:2030 #6 0x0000555555785d7b in get_ref_atom_value (ref=0x7fffffffd6b0, atom=0, v=0x7fffffffd628, err=0x7fffffffd860) at ref-filter.c:2064 #7 0x000055555578742f in format_ref_array_item (info=0x7fffffffd6b0, format=0x7fffffffde30, final_buf=0x7fffffffd880, error_buf=0x7fffffffd860) at ref-filter.c:2659 #8 0x000055555558ab1c in batch_object_write (obj_name=0x55555597e3f0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd880, err=0x7fffffffd860, opt=0x7fffffffde10, data=0x7fffffffd800) at builtin/cat-file.c:225 #9 0x000055555558ade5 in batch_one_object (obj_name=0x55555597e3f0 "504fe6b39f7747be6427f28d9ca97decf5e6cecf", scratch=0x7fffffffd880, err=0x7fffffffd860,Quoted 9 lines
opt=0x7fffffffde10, data=0x7fffffffd800) at builtin/cat-file.c:298 #10 0x000055555558b394 in batch_objects (batch=0x7fffffffde10, options=0x7fffffffd900) at builtin/cat-file.c:458 #11 0x000055555558bbd5 in cmd_cat_file (argc=0, argv=0x7fffffffe1d0, prefix=0x0) at builtin/cat-file.c:585 #12 0x0000555555573adb in run_builtin (p=0x555555942850 <commands+240>, argc=2, argv=0x7fffffffe1d0) at git.c:461 #13 0x0000555555573f00 in handle_builtin (argc=2, argv=0x7fffffffe1d0) at git.c:714 #14 0x0000555555574182 in run_argv (argcp=0x7fffffffe07c, argv=0x7fffffffe070) at git.c:781 #15 0x000055555557460f in cmd_main (argc=2, argv=0x7fffffffe1d0) at git.c:912 #16 0x000055555565afc1 in main (argc=3, argv=0x7fffffffe1c8) at common-main.c:52It seems that the call stack is very deep after using my version.
Quoted 6 lines
I.e. before in
batch_object_write()we could use a cheap path of doingoid_object_info_extended()and directly emitting the content. With your version we're all the way down toparse_object_buffer(). Meaning that we're going to be creating a "struct commit" or whatever if we're looking at a commit, just to print out the raw contents.
Quoted 3 lines
I agree that the logic in ref-filter is too complicated in order to be able to print
the structured object data.
Well, maybe it isn't. I think the main problem with your performance is just looking things up again from the object store needlessly, and possibly redundantly copying things around. E.g. if you have a buffer with a commit object and you need to xstrdup() that, and xstrdup() the answer etc, it adds up.
Maybe if all that's gone the formatting will still slow things down, but I bet it's going to be to a much smaller extent.
Quoted 7 lines
Quoted 6 lines
I think the best next step here is to add a
t/perf/t1006-cat-file.shtest to stress these various cases, i.e. a plain --batch without a format, with format, with --batch-all-objects etc. Try to then run that on each of your commits against the preceding one and see if/when you have regressions.
Quoted 8 lines
Make sence.
Quoted 2 lines
Aside from any double-lookups etc, the problem is also that you're trying to handle a really general case (e.g. with textconv) in a
Well, "--textconv" is a common situation? Here I may not be sure which scenarios where the upper application calls "git cat-file --batch" are the most common.
I think --textconv is much rarer. I think it's used for API purposes e.g. for people who are doing \n translation, or maybe for the likes of git-lfs (or was that --filters)? I'm not very familiar with it.
For what it's worth 'git cat-file --batch' is very performance sensitive in backend use-cases like e.g. gitlab.com's gitaly (which executes all git commands for you on that site). Anything that looks up .. any object pretty much .. is prining that object line to a persistent "cat-file --batch" process.
There was a case where some very small (unrelated to cat-file per-se) slowdown in that codepath ended up slowing down overall API requests for some calls by >100%, because it's executed in a tight loop. E.g. if you're showing N commits per page that might be N cat-file --batch calls.