1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-05 14:06:15 +02:00

Merge branch 'la/hide-trailer-info' into next

The trailer API has been reshuffled a bit.

* la/hide-trailer-info:
  trailer: retire trailer_info_get() from API
  trailer: make trailer_info struct private
  trailer: make parse_trailers() return trailer_info pointer
  interpret-trailers: access trailer_info with new helpers
  sequencer: use the trailer iterator
  trailer: teach iterator about non-trailer lines
  trailer: add unit tests for trailer iterator
  Makefile: sort UNIT_TEST_PROGRAMS
This commit is contained in:
Junio C Hamano 2024-04-25 10:37:11 -07:00
commit ea23b62d0c
6 changed files with 325 additions and 115 deletions

View File

@ -1348,10 +1348,11 @@ THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%
UNIT_TEST_PROGRAMS += t-basic
UNIT_TEST_PROGRAMS += t-mem-pool
UNIT_TEST_PROGRAMS += t-strbuf
UNIT_TEST_PROGRAMS += t-ctype
UNIT_TEST_PROGRAMS += t-mem-pool
UNIT_TEST_PROGRAMS += t-prio-queue
UNIT_TEST_PROGRAMS += t-strbuf
UNIT_TEST_PROGRAMS += t-trailer
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o

View File

@ -141,7 +141,7 @@ static void interpret_trailers(const struct process_trailer_options *opts,
LIST_HEAD(head);
struct strbuf sb = STRBUF_INIT;
struct strbuf trailer_block = STRBUF_INIT;
struct trailer_info info;
struct trailer_info *info;
FILE *outfile = stdout;
trailer_config_init();
@ -151,13 +151,13 @@ static void interpret_trailers(const struct process_trailer_options *opts,
if (opts->in_place)
outfile = create_in_place_tempfile(file);
parse_trailers(opts, &info, sb.buf, &head);
info = parse_trailers(opts, sb.buf, &head);
/* Print the lines before the trailers */
if (!opts->only_trailers)
fwrite(sb.buf, 1, info.trailer_block_start, outfile);
fwrite(sb.buf, 1, trailer_block_start(info), outfile);
if (!opts->only_trailers && !info.blank_line_before_trailer)
if (!opts->only_trailers && !blank_line_before_trailer_block(info))
fprintf(outfile, "\n");
@ -178,8 +178,8 @@ static void interpret_trailers(const struct process_trailer_options *opts,
/* Print the lines after the trailers as is */
if (!opts->only_trailers)
fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
trailer_info_release(&info);
fwrite(sb.buf + trailer_block_end(info), 1, sb.len - trailer_block_end(info), outfile);
trailer_info_release(info);
if (opts->in_place)
if (rename_tempfile(&trailers_tempfile, file))

View File

@ -359,35 +359,32 @@ static const char *get_todo_path(const struct replay_opts *opts)
static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
size_t ignore_footer)
{
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
struct trailer_info info;
size_t i;
struct trailer_iterator iter;
size_t i = 0;
int found_sob = 0, found_sob_last = 0;
char saved_char;
opts.no_divider = 1;
if (ignore_footer) {
saved_char = sb->buf[sb->len - ignore_footer];
sb->buf[sb->len - ignore_footer] = '\0';
}
trailer_info_get(&opts, sb->buf, &info);
trailer_iterator_init(&iter, sb->buf);
if (ignore_footer)
sb->buf[sb->len - ignore_footer] = saved_char;
if (info.trailer_block_start == info.trailer_block_end)
while (trailer_iterator_advance(&iter)) {
i++;
if (sob && !strncmp(iter.raw, sob->buf, sob->len))
found_sob = i;
}
trailer_iterator_release(&iter);
if (!i)
return 0;
for (i = 0; i < info.trailer_nr; i++)
if (sob && !strncmp(info.trailers[i], sob->buf, sob->len)) {
found_sob = 1;
if (i == info.trailer_nr - 1)
found_sob_last = 1;
}
trailer_info_release(&info);
found_sob_last = (int)i == found_sob;
if (found_sob_last)
return 3;

181
t/unit-tests/t-trailer.c Normal file
View File

@ -0,0 +1,181 @@
#include "test-lib.h"
#include "trailer.h"
static void t_trailer_iterator(const char *msg, size_t num_expected_trailers)
{
struct trailer_iterator iter;
size_t i = 0;
trailer_iterator_init(&iter, msg);
while (trailer_iterator_advance(&iter)) {
i++;
}
trailer_iterator_release(&iter);
check_uint(i, ==, num_expected_trailers);
}
static void run_t_trailer_iterator(void)
{
static struct test_cases {
const char *name;
const char *msg;
size_t num_expected_trailers;
} tc[] = {
{
"empty input",
"",
0
},
{
"no newline at beginning",
"Fixes: x\n"
"Acked-by: x\n"
"Reviewed-by: x\n",
0
},
{
"newline at beginning",
"\n"
"Fixes: x\n"
"Acked-by: x\n"
"Reviewed-by: x\n",
3
},
{
"without body text",
"subject: foo bar\n"
"\n"
"Fixes: x\n"
"Acked-by: x\n"
"Reviewed-by: x\n",
3
},
{
"with body text, without divider",
"my subject\n"
"\n"
"my body which is long\n"
"and contains some special\n"
"chars like : = ? !\n"
"hello\n"
"\n"
"Fixes: x\n"
"Acked-by: x\n"
"Reviewed-by: x\n"
"Signed-off-by: x\n",
4
},
{
"with body text, without divider (second trailer block)",
"my subject\n"
"\n"
"my body which is long\n"
"and contains some special\n"
"chars like : = ? !\n"
"hello\n"
"\n"
"Fixes: x\n"
"Acked-by: x\n"
"Reviewed-by: x\n"
"Signed-off-by: x\n"
"\n"
/*
* Because this is the last trailer block, it takes
* precedence over the first one encountered above.
*/
"Helped-by: x\n"
"Signed-off-by: x\n",
2
},
{
"with body text, with divider",
"my subject\n"
"\n"
"my body which is long\n"
"and contains some special\n"
"chars like : = ? !\n"
"hello\n"
"\n"
"---\n"
"\n"
/*
* This trailer still counts because the iterator
* always ignores the divider.
*/
"Signed-off-by: x\n",
1
},
{
"with non-trailer lines in trailer block",
"subject: foo bar\n"
"\n"
/*
* Even though this trailer block has a non-trailer line
* in it, it's still a valid trailer block because it's
* at least 25% trailers and is Git-generated.
*/
"not a trailer line\n"
"not a trailer line\n"
"not a trailer line\n"
"Signed-off-by: x\n",
/*
* Even though there is only really 1 real "trailer"
* (Signed-off-by), we still have 4 trailer objects
* because we still want to iterate through the entire
* block.
*/
4
},
{
"with non-trailer lines (one too many) in trailer block",
"subject: foo bar\n"
"\n"
/*
* This block has only 20% trailers, so it's below the
* 25% threshold.
*/
"not a trailer line\n"
"not a trailer line\n"
"not a trailer line\n"
"not a trailer line\n"
"Signed-off-by: x\n",
0
},
{
"with non-trailer lines (only 1) in trailer block, but no Git-generated trailers",
"subject: foo bar\n"
"\n"
/*
* This block has only 1 non-trailer out of 10 (IOW, 90%
* trailers) but is not considered a trailer because the
* 25% threshold only applies to cases where there was a
* Git-generated trailer (see git_generated_prefixes[]
* in trailer.c).
*/
"Reviewed-by: x\n"
"Reviewed-by: x\n"
"Reviewed-by: x\n"
"Helped-by: x\n"
"Helped-by: x\n"
"Helped-by: x\n"
"Acked-by: x\n"
"Acked-by: x\n"
"Acked-by: x\n"
"not a trailer line\n",
0
},
};
for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) {
TEST(t_trailer_iterator(tc[i].msg,
tc[i].num_expected_trailers),
"%s", tc[i].name);
}
}
int cmd_main(int argc, const char **argv)
{
run_t_trailer_iterator();
return test_done();
}

169
trailer.c
View File

@ -11,6 +11,27 @@
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
struct trailer_info {
/*
* True if there is a blank line before the location pointed to by
* trailer_block_start.
*/
int blank_line_before_trailer;
/*
* Offsets to the trailer block start and end positions in the input
* string. If no trailer block is found, these are both set to the
* "true" end of the input (find_end_of_log_message()).
*/
size_t trailer_block_start, trailer_block_end;
/*
* Array of trailers found.
*/
char **trailers;
size_t trailer_nr;
};
struct conf_info {
char *name;
char *key;
@ -952,58 +973,16 @@ static void unfold_value(struct strbuf *val)
strbuf_release(&out);
}
/*
* Parse trailers in "str", populating the trailer info and "head"
* linked list structure.
*/
void parse_trailers(const struct process_trailer_options *opts,
struct trailer_info *info,
const char *str,
struct list_head *head)
static struct trailer_info *trailer_info_new(void)
{
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
size_t i;
trailer_info_get(opts, str, info);
for (i = 0; i < info->trailer_nr; i++) {
int separator_pos;
char *trailer = info->trailers[i];
if (starts_with(trailer, comment_line_str))
continue;
separator_pos = find_separator(trailer, separators);
if (separator_pos >= 1) {
parse_trailer(&tok, &val, NULL, trailer,
separator_pos);
if (opts->unfold)
unfold_value(&val);
add_trailer_item(head,
strbuf_detach(&tok, NULL),
strbuf_detach(&val, NULL));
} else if (!opts->only_trailers) {
strbuf_addstr(&val, trailer);
strbuf_strip_suffix(&val, "\n");
add_trailer_item(head,
NULL,
strbuf_detach(&val, NULL));
}
}
struct trailer_info *info = xcalloc(1, sizeof(*info));
return info;
}
void free_trailers(struct list_head *trailers)
{
struct list_head *pos, *p;
list_for_each_safe(pos, p, trailers) {
list_del(pos);
free_trailer_item(list_entry(pos, struct trailer_item, list));
}
}
void trailer_info_get(const struct process_trailer_options *opts,
const char *str,
struct trailer_info *info)
static struct trailer_info *trailer_info_get(const struct process_trailer_options *opts,
const char *str)
{
struct trailer_info *info = trailer_info_new();
size_t end_of_log_message = 0, trailer_block_start = 0;
struct strbuf **trailer_lines, **ptr;
char **trailer_strings = NULL;
@ -1042,6 +1021,73 @@ void trailer_info_get(const struct process_trailer_options *opts,
info->trailer_block_end = end_of_log_message;
info->trailers = trailer_strings;
info->trailer_nr = nr;
return info;
}
/*
* Parse trailers in "str", populating the trailer info and "head"
* linked list structure.
*/
struct trailer_info *parse_trailers(const struct process_trailer_options *opts,
const char *str,
struct list_head *head)
{
struct trailer_info *info;
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
size_t i;
info = trailer_info_get(opts, str);
for (i = 0; i < info->trailer_nr; i++) {
int separator_pos;
char *trailer = info->trailers[i];
if (starts_with(trailer, comment_line_str))
continue;
separator_pos = find_separator(trailer, separators);
if (separator_pos >= 1) {
parse_trailer(&tok, &val, NULL, trailer,
separator_pos);
if (opts->unfold)
unfold_value(&val);
add_trailer_item(head,
strbuf_detach(&tok, NULL),
strbuf_detach(&val, NULL));
} else if (!opts->only_trailers) {
strbuf_addstr(&val, trailer);
strbuf_strip_suffix(&val, "\n");
add_trailer_item(head,
NULL,
strbuf_detach(&val, NULL));
}
}
return info;
}
void free_trailers(struct list_head *trailers)
{
struct list_head *pos, *p;
list_for_each_safe(pos, p, trailers) {
list_del(pos);
free_trailer_item(list_entry(pos, struct trailer_item, list));
}
}
size_t trailer_block_start(struct trailer_info *info)
{
return info->trailer_block_start;
}
size_t trailer_block_end(struct trailer_info *info)
{
return info->trailer_block_end;
}
int blank_line_before_trailer_block(struct trailer_info *info)
{
return info->blank_line_before_trailer;
}
void trailer_info_release(struct trailer_info *info)
@ -1050,6 +1096,7 @@ void trailer_info_release(struct trailer_info *info)
for (i = 0; i < info->trailer_nr; i++)
free(info->trailers[i]);
free(info->trailers);
free(info);
}
void format_trailers(const struct process_trailer_options *opts,
@ -1117,21 +1164,19 @@ void format_trailers_from_commit(const struct process_trailer_options *opts,
struct strbuf *out)
{
LIST_HEAD(trailer_objects);
struct trailer_info info;
parse_trailers(opts, &info, msg, &trailer_objects);
struct trailer_info *info = parse_trailers(opts, msg, &trailer_objects);
/* If we want the whole block untouched, we can take the fast path. */
if (!opts->only_trailers && !opts->unfold && !opts->filter &&
!opts->separator && !opts->key_only && !opts->value_only &&
!opts->key_value_separator) {
strbuf_add(out, msg + info.trailer_block_start,
info.trailer_block_end - info.trailer_block_start);
strbuf_add(out, msg + info->trailer_block_start,
info->trailer_block_end - info->trailer_block_start);
} else
format_trailers(opts, &trailer_objects, out);
free_trailers(&trailer_objects);
trailer_info_release(&info);
trailer_info_release(info);
}
void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
@ -1140,23 +1185,21 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
strbuf_init(&iter->key, 0);
strbuf_init(&iter->val, 0);
opts.no_divider = 1;
trailer_info_get(&opts, msg, &iter->internal.info);
iter->internal.info = trailer_info_get(&opts, msg);
iter->internal.cur = 0;
}
int trailer_iterator_advance(struct trailer_iterator *iter)
{
while (iter->internal.cur < iter->internal.info.trailer_nr) {
char *trailer = iter->internal.info.trailers[iter->internal.cur++];
int separator_pos = find_separator(trailer, separators);
if (separator_pos < 1)
continue; /* not a real trailer */
if (iter->internal.cur < iter->internal.info->trailer_nr) {
char *line = iter->internal.info->trailers[iter->internal.cur++];
int separator_pos = find_separator(line, separators);
iter->raw = line;
strbuf_reset(&iter->key);
strbuf_reset(&iter->val);
parse_trailer(&iter->key, &iter->val, NULL,
trailer, separator_pos);
line, separator_pos);
/* Always unfold values during iteration. */
unfold_value(&iter->val);
return 1;
@ -1166,7 +1209,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
void trailer_iterator_release(struct trailer_iterator *iter)
{
trailer_info_release(&iter->internal.info);
trailer_info_release(iter->internal.info);
strbuf_release(&iter->val);
strbuf_release(&iter->key);
}

View File

@ -4,6 +4,8 @@
#include "list.h"
#include "strbuf.h"
struct trailer_info;
enum trailer_where {
WHERE_DEFAULT,
WHERE_END,
@ -29,27 +31,6 @@ int trailer_set_where(enum trailer_where *item, const char *value);
int trailer_set_if_exists(enum trailer_if_exists *item, const char *value);
int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);
struct trailer_info {
/*
* True if there is a blank line before the location pointed to by
* trailer_block_start.
*/
int blank_line_before_trailer;
/*
* Offsets to the trailer block start and end positions in the input
* string. If no trailer block is found, these are both set to the
* "true" end of the input (find_end_of_log_message()).
*/
size_t trailer_block_start, trailer_block_end;
/*
* Array of trailers found.
*/
char **trailers;
size_t trailer_nr;
};
/*
* A list that represents newly-added trailers, such as those provided
* with the --trailer command line option of git-interpret-trailers.
@ -89,14 +70,13 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head,
void process_trailers_lists(struct list_head *head,
struct list_head *arg_head);
void parse_trailers(const struct process_trailer_options *,
struct trailer_info *,
const char *str,
struct list_head *head);
struct trailer_info *parse_trailers(const struct process_trailer_options *,
const char *str,
struct list_head *head);
void trailer_info_get(const struct process_trailer_options *,
const char *str,
struct trailer_info *);
size_t trailer_block_start(struct trailer_info *);
size_t trailer_block_end(struct trailer_info *);
int blank_line_before_trailer_block(struct trailer_info *);
void trailer_info_release(struct trailer_info *info);
@ -125,12 +105,20 @@ void format_trailers_from_commit(const struct process_trailer_options *,
* trailer_iterator_release(&iter);
*/
struct trailer_iterator {
/*
* Raw line (e.g., "foo: bar baz") before being parsed as a trailer
* key/val pair as part of a trailer block. A trailer block can be
* either 100% trailer lines, or mixed in with non-trailer lines (in
* which case at least 25% must be trailer lines).
*/
const char *raw;
struct strbuf key;
struct strbuf val;
/* private */
struct {
struct trailer_info info;
struct trailer_info *info;
size_t cur;
} internal;
};