ref-filter: add %(raw) atom

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. Reviewed-by: Jacob Keller <jacob.keller@gmail.com> Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Hariom Verma <hariom18599@gmail.com> Helped-by: Bagas Sanjaya <bagasdotme@gmail.com> Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Helped-by: Felipe Contreras <felipe.contreras@gmail.com> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Junio C Hamano <gitster@pobox.com> Based-on-patch-by: Olga Telezhnaya <olyatelezhnaya@gmail.com> Signed-off-by: ZheNing Hu <adlternative@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

ZheNing Hu committed Jul 26, 2021 at 03:26 UTC bd0708c7eb8be43242180a1ea2a0f0acda7a4fbc
3 files changed +340 -28
Documentation/git-for-each-ref.txt
+9
@@ -235,6 +235,15 @@ and `date` to extract the named component. For email fields (`authoremail`,
235 without angle brackets, and `:localpart` to get the part before the `@` symbol
236 out of the trimmed email.
237
238 +The raw data in an object is `raw`.
239 +
240 +raw:size::
241 + The raw data size of the object.
242 +
243 +Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
244 +`--perl` because such language may not support arbitrary binary data in their
245 +string variable type.
246 +
247 The message in a commit or a tag object is `contents`, from which
248 `contents:<part>` can be used to extract various parts out of:
249
ref-filter.c
+115 -28
@@ -144,6 +144,7 @@ enum atom_type {
144 ATOM_BODY,
145 ATOM_TRAILERS,
146 ATOM_CONTENTS,
147 + ATOM_RAW,
148 ATOM_UPSTREAM,
149 ATOM_PUSH,
150 ATOM_SYMREF,
@@ -189,6 +190,9 @@ static struct used_atom {
190 struct process_trailer_options trailer_opts;
191 unsigned int nlines;
192 } contents;
193 + struct {
194 + enum { RAW_BARE, RAW_LENGTH } option;
195 + } raw_data;
196 struct {
197 cmp_status cmp_status;
198 const char *str;
@@ -426,6 +430,18 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato
430 return 0;
431 }
432
433 +static int raw_atom_parser(const struct ref_format *format, struct used_atom *atom,
434 + const char *arg, struct strbuf *err)
435 +{
436 + if (!arg)
437 + atom->u.raw_data.option = RAW_BARE;
438 + else if (!strcmp(arg, "size"))
439 + atom->u.raw_data.option = RAW_LENGTH;
440 + else
441 + return strbuf_addf_ret(err, -1, _("unrecognized %%(raw) argument: %s"), arg);
442 + return 0;
443 +}
444 +
445 static int oid_atom_parser(const struct ref_format *format, struct used_atom *atom,
446 const char *arg, struct strbuf *err)
447 {
@@ -586,6 +602,7 @@ static struct {
602 [ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
603 [ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
604 [ATOM_CONTENTS] = { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
605 + [ATOM_RAW] = { "raw", SOURCE_OBJ, FIELD_STR, raw_atom_parser },
606 [ATOM_UPSTREAM] = { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
607 [ATOM_PUSH] = { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
608 [ATOM_SYMREF] = { "symref", SOURCE_NONE, FIELD_STR, refname_atom_parser },
@@ -620,12 +637,19 @@ struct ref_formatting_state {
637
638 struct atom_value {
639 const char *s;
640 + ssize_t s_size;
641 int (*handler)(struct atom_value *atomv, struct ref_formatting_state *state,
642 struct strbuf *err);
643 uintmax_t value; /* used for sorting when not FIELD_STR */
644 struct used_atom *atom;
645 };
646
647 +#define ATOM_SIZE_UNSPECIFIED (-1)
648 +
649 +#define ATOM_VALUE_INIT { \
650 + .s_size = ATOM_SIZE_UNSPECIFIED \
651 +}
652 +
653 /*
654 * Used to parse format string and sort specifiers
655 */
@@ -644,13 +668,6 @@ static int parse_ref_filter_atom(const struct ref_format *format,
668 return strbuf_addf_ret(err, -1, _("malformed field name: %.*s"),
669 (int)(ep-atom), atom);
670
647 - /* Do we have the atom already used elsewhere? */
648 - for (i = 0; i < used_atom_cnt; i++) {
649 - int len = strlen(used_atom[i].name);
650 - if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
651 - return i;
652 - }
653 -
671 /*
672 * If the atom name has a colon, strip it and everything after
673 * it off - it specifies the format for this entry, and
@@ -660,6 +677,13 @@ static int parse_ref_filter_atom(const struct ref_format *format,
677 arg = memchr(sp, ':', ep - sp);
678 atom_len = (arg ? arg : ep) - sp;
679
680 + /* Do we have the atom already used elsewhere? */
681 + for (i = 0; i < used_atom_cnt; i++) {
682 + int len = strlen(used_atom[i].name);
683 + if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
684 + return i;
685 + }
686 +
687 /* Is the atom a valid one? */
688 for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
689 int len = strlen(valid_atom[i].name);
@@ -709,11 +733,14 @@ static int parse_ref_filter_atom(const struct ref_format *format,
733 return at;
734 }
735
712 -static void quote_formatting(struct strbuf *s, const char *str, int quote_style)
736 +static void quote_formatting(struct strbuf *s, const char *str, ssize_t len, int quote_style)
737 {
738 switch (quote_style) {
739 case QUOTE_NONE:
716 - strbuf_addstr(s, str);
740 + if (len < 0)
741 + strbuf_addstr(s, str);
742 + else
743 + strbuf_add(s, str, len);
744 break;
745 case QUOTE_SHELL:
746 sq_quote_buf(s, str);
@@ -740,9 +767,11 @@ static int append_atom(struct atom_value *v, struct ref_formatting_state *state,
767 * encountered.
768 */
769 if (!state->stack->prev)
743 - quote_formatting(&state->stack->output, v->s, state->quote_style);
744 - else
770 + quote_formatting(&state->stack->output, v->s, v->s_size, state->quote_style);
771 + else if (v->s_size < 0)
772 strbuf_addstr(&state->stack->output, v->s);
773 + else
774 + strbuf_add(&state->stack->output, v->s, v->s_size);
775 return 0;
776 }
777
@@ -842,21 +871,23 @@ static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state
871 return 0;
872 }
873
845 -static int is_empty(const char *s)
874 +static int is_empty(struct strbuf *buf)
875 {
847 - while (*s != '\0') {
848 - if (!isspace(*s))
849 - return 0;
850 - s++;
851 - }
852 - return 1;
853 -}
876 + const char *cur = buf->buf;
877 + const char *end = buf->buf + buf->len;
878 +
879 + while (cur != end && (isspace(*cur)))
880 + cur++;
881 +
882 + return cur == end;
883 + }
884
885 static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
886 struct strbuf *err)
887 {
888 struct ref_formatting_stack *cur = state->stack;
889 struct if_then_else *if_then_else = NULL;
890 + size_t str_len = 0;
891
892 if (cur->at_end == if_then_else_handler)
893 if_then_else = (struct if_then_else *)cur->at_end_data;
@@ -867,18 +898,22 @@ static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
898 if (if_then_else->else_atom_seen)
899 return strbuf_addf_ret(err, -1, _("format: %%(then) atom used after %%(else)"));
900 if_then_else->then_atom_seen = 1;
901 + if (if_then_else->str)
902 + str_len = strlen(if_then_else->str);
903 /*
904 * If the 'equals' or 'notequals' attribute is used then
905 * perform the required comparison. If not, only non-empty
906 * strings satisfy the 'if' condition.
907 */
908 if (if_then_else->cmp_status == COMPARE_EQUAL) {
876 - if (!strcmp(if_then_else->str, cur->output.buf))
909 + if (str_len == cur->output.len &&
910 + !memcmp(if_then_else->str, cur->output.buf, cur->output.len))
911 if_then_else->condition_satisfied = 1;
912 } else if (if_then_else->cmp_status == COMPARE_UNEQUAL) {
879 - if (strcmp(if_then_else->str, cur->output.buf))
913 + if (str_len != cur->output.len ||
914 + memcmp(if_then_else->str, cur->output.buf, cur->output.len))
915 if_then_else->condition_satisfied = 1;
881 - } else if (cur->output.len && !is_empty(cur->output.buf))
916 + } else if (cur->output.len && !is_empty(&cur->output))
917 if_then_else->condition_satisfied = 1;
918 strbuf_reset(&cur->output);
919 return 0;
@@ -924,7 +959,7 @@ static int end_atom_handler(struct atom_value *atomv, struct ref_formatting_stat
959 * only on the topmost supporting atom.
960 */
961 if (!current->prev->prev) {
927 - quote_formatting(&s, current->output.buf, state->quote_style);
962 + quote_formatting(&s, current->output.buf, current->output.len, state->quote_style);
963 strbuf_swap(&current->output, &s);
964 }
965 strbuf_release(&s);
@@ -974,6 +1009,10 @@ int verify_ref_format(struct ref_format *format)
1009 at = parse_ref_filter_atom(format, sp + 2, ep, &err);
1010 if (at < 0)
1011 die("%s", err.buf);
1012 + if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
1013 + used_atom[at].u.raw_data.option == RAW_BARE)
1014 + die(_("--format=%.*s cannot be used with"
1015 + "--python, --shell, --tcl, --perl"), (int)(ep - sp - 2), sp + 2);
1016 cp = ep + 1;
1017
1018 if (skip_prefix(used_atom[at].name, "color:", &color))
@@ -1367,12 +1406,25 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
1406 struct used_atom *atom = &used_atom[i];
1407 const char *name = atom->name;
1408 struct atom_value *v = &val[i];
1409 + enum atom_type atom_type = atom->atom_type;
1410
1411 if (!!deref != (*name == '*'))
1412 continue;
1413 if (deref)
1414 name++;
1415
1416 + if (atom_type == ATOM_RAW) {
1417 + unsigned long buf_size = data->size;
1418 +
1419 + if (atom->u.raw_data.option == RAW_BARE) {
1420 + v->s = xmemdupz(buf, buf_size);
1421 + v->s_size = buf_size;
1422 + } else if (atom->u.raw_data.option == RAW_LENGTH) {
1423 + v->s = xstrfmt("%"PRIuMAX, (uintmax_t)buf_size);
1424 + }
1425 + continue;
1426 + }
1427 +
1428 if ((data->type != OBJ_TAG &&
1429 data->type != OBJ_COMMIT) ||
1430 (strcmp(name, "body") &&
@@ -1460,9 +1512,11 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
1512 break;
1513 case OBJ_TREE:
1514 /* grab_tree_values(val, deref, obj, buf, sz); */
1515 + grab_sub_body_contents(val, deref, data);
1516 break;
1517 case OBJ_BLOB:
1518 /* grab_blob_values(val, deref, obj, buf, sz); */
1519 + grab_sub_body_contents(val, deref, data);
1520 break;
1521 default:
1522 die("Eh? Object of type %d?", obj->type);
@@ -1766,6 +1820,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
1820 const char *refname;
1821 struct branch *branch = NULL;
1822
1823 + v->s_size = ATOM_SIZE_UNSPECIFIED;
1824 v->handler = append_atom;
1825 v->atom = atom;
1826
@@ -2369,6 +2424,19 @@ static int compare_detached_head(struct ref_array_item *a, struct ref_array_item
2424 return 0;
2425 }
2426
2427 +static int memcasecmp(const void *vs1, const void *vs2, size_t n)
2428 +{
2429 + const char *s1 = vs1, *s2 = vs2;
2430 + const char *end = s1 + n;
2431 +
2432 + for (; s1 < end; s1++, s2++) {
2433 + int diff = tolower(*s1) - tolower(*s2);
2434 + if (diff)
2435 + return diff;
2436 + }
2437 + return 0;
2438 +}
2439 +
2440 static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
2441 {
2442 struct atom_value *va, *vb;
@@ -2389,10 +2457,29 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
2457 } else if (s->sort_flags & REF_SORTING_VERSION) {
2458 cmp = versioncmp(va->s, vb->s);
2459 } else if (cmp_type == FIELD_STR) {
2392 - int (*cmp_fn)(const char *, const char *);
2393 - cmp_fn = s->sort_flags & REF_SORTING_ICASE
2394 - ? strcasecmp : strcmp;
2395 - cmp = cmp_fn(va->s, vb->s);
2460 + if (va->s_size < 0 && vb->s_size < 0) {
2461 + int (*cmp_fn)(const char *, const char *);
2462 + cmp_fn = s->sort_flags & REF_SORTING_ICASE
2463 + ? strcasecmp : strcmp;
2464 + cmp = cmp_fn(va->s, vb->s);
2465 + } else {
2466 + size_t a_size = va->s_size < 0 ?
2467 + strlen(va->s) : va->s_size;
2468 + size_t b_size = vb->s_size < 0 ?
2469 + strlen(vb->s) : vb->s_size;
2470 + int (*cmp_fn)(const void *, const void *, size_t);
2471 + cmp_fn = s->sort_flags & REF_SORTING_ICASE
2472 + ? memcasecmp : memcmp;
2473 +
2474 + cmp = cmp_fn(va->s, vb->s, b_size > a_size ?
2475 + a_size : b_size);
2476 + if (!cmp) {
2477 + if (a_size > b_size)
2478 + cmp = 1;
2479 + else if (a_size < b_size)
2480 + cmp = -1;
2481 + }
2482 + }
2483 } else {
2484 if (va->value < vb->value)
2485 cmp = -1;
@@ -2491,7 +2578,7 @@ int format_ref_array_item(struct ref_array_item *info,
2578 append_literal(cp, sp, &state);
2579 }
2580 if (format->need_color_reset_at_eol) {
2494 - struct atom_value resetv;
2581 + struct atom_value resetv = ATOM_VALUE_INIT;
2582 resetv.s = GIT_COLOR_RESET;
2583 if (append_atom(&resetv, &state, error_buf)) {
2584 pop_stack_element(&state.stack);
t/t6300-for-each-ref.sh
+216
@@ -130,6 +130,8 @@ test_atom head parent:short=10 ''
130 test_atom head numparent 0
131 test_atom head object ''
132 test_atom head type ''
133 +test_atom head raw "$(git cat-file commit refs/heads/main)
134 +"
135 test_atom head '*objectname' ''
136 test_atom head '*objecttype' ''
137 test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
@@ -221,6 +223,15 @@ test_atom tag contents 'Tagging at 1151968727
223 '
224 test_atom tag HEAD ' '
225
226 +test_expect_success 'basic atom: refs/tags/testtag *raw' '
227 + git cat-file commit refs/tags/testtag^{} >expected &&
228 + git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
229 + sanitize_pgp <expected >expected.clean &&
230 + echo >>expected.clean &&
231 + sanitize_pgp <actual >actual.clean &&
232 + test_cmp expected.clean actual.clean
233 +'
234 +
235 test_expect_success 'Check invalid atoms names are errors' '
236 test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
237 '
@@ -686,6 +697,15 @@ test_atom refs/tags/signed-empty contents:body ''
697 test_atom refs/tags/signed-empty contents:signature "$sig"
698 test_atom refs/tags/signed-empty contents "$sig"
699
700 +test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '
701 + git cat-file tag refs/tags/signed-empty >expected &&
702 + git for-each-ref --format="%(raw)" refs/tags/signed-empty >actual &&
703 + sanitize_pgp <expected >expected.clean &&
704 + echo >>expected.clean &&
705 + sanitize_pgp <actual >actual.clean &&
706 + test_cmp expected.clean actual.clean
707 +'
708 +
709 test_atom refs/tags/signed-short subject 'subject line'
710 test_atom refs/tags/signed-short subject:sanitize 'subject-line'
711 test_atom refs/tags/signed-short contents:subject 'subject line'
@@ -695,6 +715,15 @@ test_atom refs/tags/signed-short contents:signature "$sig"
715 test_atom refs/tags/signed-short contents "subject line
716 $sig"
717
718 +test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '
719 + git cat-file tag refs/tags/signed-short >expected &&
720 + git for-each-ref --format="%(raw)" refs/tags/signed-short >actual &&
721 + sanitize_pgp <expected >expected.clean &&
722 + echo >>expected.clean &&
723 + sanitize_pgp <actual >actual.clean &&
724 + test_cmp expected.clean actual.clean
725 +'
726 +
727 test_atom refs/tags/signed-long subject 'subject line'
728 test_atom refs/tags/signed-long subject:sanitize 'subject-line'
729 test_atom refs/tags/signed-long contents:subject 'subject line'
@@ -708,6 +737,15 @@ test_atom refs/tags/signed-long contents "subject line
737 body contents
738 $sig"
739
740 +test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '
741 + git cat-file tag refs/tags/signed-long >expected &&
742 + git for-each-ref --format="%(raw)" refs/tags/signed-long >actual &&
743 + sanitize_pgp <expected >expected.clean &&
744 + echo >>expected.clean &&
745 + sanitize_pgp <actual >actual.clean &&
746 + test_cmp expected.clean actual.clean
747 +'
748 +
749 test_expect_success 'set up refs pointing to tree and blob' '
750 git update-ref refs/mytrees/first refs/heads/main^{tree} &&
751 git update-ref refs/myblobs/first refs/heads/main:one
@@ -720,6 +758,16 @@ test_atom refs/mytrees/first contents:body ""
758 test_atom refs/mytrees/first contents:signature ""
759 test_atom refs/mytrees/first contents ""
760
761 +test_expect_success 'basic atom: refs/mytrees/first raw' '
762 + git cat-file tree refs/mytrees/first >expected &&
763 + echo >>expected &&
764 + git for-each-ref --format="%(raw)" refs/mytrees/first >actual &&
765 + test_cmp expected actual &&
766 + git cat-file -s refs/mytrees/first >expected &&
767 + git for-each-ref --format="%(raw:size)" refs/mytrees/first >actual &&
768 + test_cmp expected actual
769 +'
770 +
771 test_atom refs/myblobs/first subject ""
772 test_atom refs/myblobs/first contents:subject ""
773 test_atom refs/myblobs/first body ""
@@ -727,6 +775,174 @@ test_atom refs/myblobs/first contents:body ""
775 test_atom refs/myblobs/first contents:signature ""
776 test_atom refs/myblobs/first contents ""
777
778 +test_expect_success 'basic atom: refs/myblobs/first raw' '
779 + git cat-file blob refs/myblobs/first >expected &&
780 + echo >>expected &&
781 + git for-each-ref --format="%(raw)" refs/myblobs/first >actual &&
782 + test_cmp expected actual &&
783 + git cat-file -s refs/myblobs/first >expected &&
784 + git for-each-ref --format="%(raw:size)" refs/myblobs/first >actual &&
785 + test_cmp expected actual
786 +'
787 +
788 +test_expect_success 'set up refs pointing to binary blob' '
789 + printf "a\0b\0c" >blob1 &&
790 + printf "a\0c\0b" >blob2 &&
791 + printf "\0a\0b\0c" >blob3 &&
792 + printf "abc" >blob4 &&
793 + printf "\0 \0 \0 " >blob5 &&
794 + printf "\0 \0a\0 " >blob6 &&
795 + printf " " >blob7 &&
796 + >blob8 &&
797 + obj=$(git hash-object -w blob1) &&
798 + git update-ref refs/myblobs/blob1 "$obj" &&
799 + obj=$(git hash-object -w blob2) &&
800 + git update-ref refs/myblobs/blob2 "$obj" &&
801 + obj=$(git hash-object -w blob3) &&
802 + git update-ref refs/myblobs/blob3 "$obj" &&
803 + obj=$(git hash-object -w blob4) &&
804 + git update-ref refs/myblobs/blob4 "$obj" &&
805 + obj=$(git hash-object -w blob5) &&
806 + git update-ref refs/myblobs/blob5 "$obj" &&
807 + obj=$(git hash-object -w blob6) &&
808 + git update-ref refs/myblobs/blob6 "$obj" &&
809 + obj=$(git hash-object -w blob7) &&
810 + git update-ref refs/myblobs/blob7 "$obj" &&
811 + obj=$(git hash-object -w blob8) &&
812 + git update-ref refs/myblobs/blob8 "$obj"
813 +'
814 +
815 +test_expect_success 'Verify sorts with raw' '
816 + cat >expected <<-EOF &&
817 + refs/myblobs/blob8
818 + refs/myblobs/blob5
819 + refs/myblobs/blob6
820 + refs/myblobs/blob3
821 + refs/myblobs/blob7
822 + refs/mytrees/first
823 + refs/myblobs/first
824 + refs/myblobs/blob1
825 + refs/myblobs/blob2
826 + refs/myblobs/blob4
827 + refs/heads/main
828 + EOF
829 + git for-each-ref --format="%(refname)" --sort=raw \
830 + refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
831 + test_cmp expected actual
832 +'
833 +
834 +test_expect_success 'Verify sorts with raw:size' '
835 + cat >expected <<-EOF &&
836 + refs/myblobs/blob8
837 + refs/myblobs/first
838 + refs/myblobs/blob7
839 + refs/heads/main
840 + refs/myblobs/blob4
841 + refs/myblobs/blob1
842 + refs/myblobs/blob2
843 + refs/myblobs/blob3
844 + refs/myblobs/blob5
845 + refs/myblobs/blob6
846 + refs/mytrees/first
847 + EOF
848 + git for-each-ref --format="%(refname)" --sort=raw:size \
849 + refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
850 + test_cmp expected actual
851 +'
852 +
853 +test_expect_success 'validate raw atom with %(if:equals)' '
854 + cat >expected <<-EOF &&
855 + not equals
856 + not equals
857 + not equals
858 + not equals
859 + not equals
860 + not equals
861 + refs/myblobs/blob4
862 + not equals
863 + not equals
864 + not equals
865 + not equals
866 + not equals
867 + EOF
868 + git for-each-ref --format="%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)" \
869 + refs/myblobs/ refs/heads/ >actual &&
870 + test_cmp expected actual
871 +'
872 +
873 +test_expect_success 'validate raw atom with %(if:notequals)' '
874 + cat >expected <<-EOF &&
875 + refs/heads/ambiguous
876 + refs/heads/main
877 + refs/heads/newtag
878 + refs/myblobs/blob1
879 + refs/myblobs/blob2
880 + refs/myblobs/blob3
881 + equals
882 + refs/myblobs/blob5
883 + refs/myblobs/blob6
884 + refs/myblobs/blob7
885 + refs/myblobs/blob8
886 + refs/myblobs/first
887 + EOF
888 + git for-each-ref --format="%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)" \
889 + refs/myblobs/ refs/heads/ >actual &&
890 + test_cmp expected actual
891 +'
892 +
893 +test_expect_success 'empty raw refs with %(if)' '
894 + cat >expected <<-EOF &&
895 + refs/myblobs/blob1 not empty
896 + refs/myblobs/blob2 not empty
897 + refs/myblobs/blob3 not empty
898 + refs/myblobs/blob4 not empty
899 + refs/myblobs/blob5 not empty
900 + refs/myblobs/blob6 not empty
901 + refs/myblobs/blob7 empty
902 + refs/myblobs/blob8 empty
903 + refs/myblobs/first not empty
904 + EOF
905 + git for-each-ref --format="%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)" \
906 + refs/myblobs/ >actual &&
907 + test_cmp expected actual
908 +'
909 +
910 +test_expect_success '%(raw) with --python must fail' '
911 + test_must_fail git for-each-ref --format="%(raw)" --python
912 +'
913 +
914 +test_expect_success '%(raw) with --tcl must fail' '
915 + test_must_fail git for-each-ref --format="%(raw)" --tcl
916 +'
917 +
918 +test_expect_success '%(raw) with --perl must fail' '
919 + test_must_fail git for-each-ref --format="%(raw)" --perl
920 +'
921 +
922 +test_expect_success '%(raw) with --shell must fail' '
923 + test_must_fail git for-each-ref --format="%(raw)" --shell
924 +'
925 +
926 +test_expect_success '%(raw) with --shell and --sort=raw must fail' '
927 + test_must_fail git for-each-ref --format="%(raw)" --sort=raw --shell
928 +'
929 +
930 +test_expect_success '%(raw:size) with --shell' '
931 + git for-each-ref --format="%(raw:size)" | while read line
932 + do
933 + echo "'\''$line'\''" >>expect
934 + done &&
935 + git for-each-ref --format="%(raw:size)" --shell >actual &&
936 + test_cmp expect actual
937 +'
938 +
939 +test_expect_success 'for-each-ref --format compare with cat-file --batch' '
940 + git rev-parse refs/mytrees/first | git cat-file --batch >expected &&
941 + git for-each-ref --format="%(objectname) %(objecttype) %(objectsize)
942 +%(raw)" refs/mytrees/first >actual &&
943 + test_cmp expected actual
944 +'
945 +
946 test_expect_success 'set up multiple-sort tags' '
947 for when in 100000 200000
948 do