git

Форк
0
/
trailer.c 
1229 строк · 31.4 Кб
1
#define USE_THE_REPOSITORY_VARIABLE
2

3
#include "git-compat-util.h"
4
#include "config.h"
5
#include "environment.h"
6
#include "gettext.h"
7
#include "string-list.h"
8
#include "run-command.h"
9
#include "commit.h"
10
#include "trailer.h"
11
#include "list.h"
12
/*
13
 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
14
 */
15

16
struct trailer_info {
17
	/*
18
	 * True if there is a blank line before the location pointed to by
19
	 * trailer_block_start.
20
	 */
21
	int blank_line_before_trailer;
22

23
	/*
24
	 * Offsets to the trailer block start and end positions in the input
25
	 * string. If no trailer block is found, these are both set to the
26
	 * "true" end of the input (find_end_of_log_message()).
27
	 */
28
	size_t trailer_block_start, trailer_block_end;
29

30
	/*
31
	 * Array of trailers found.
32
	 */
33
	char **trailers;
34
	size_t trailer_nr;
35
};
36

37
struct conf_info {
38
	char *name;
39
	char *key;
40
	char *command;
41
	char *cmd;
42
	enum trailer_where where;
43
	enum trailer_if_exists if_exists;
44
	enum trailer_if_missing if_missing;
45
};
46

47
static struct conf_info default_conf_info;
48

49
struct trailer_item {
50
	struct list_head list;
51
	/*
52
	 * If this is not a trailer line, the line is stored in value
53
	 * (excluding the terminating newline) and token is NULL.
54
	 */
55
	char *token;
56
	char *value;
57
};
58

59
struct arg_item {
60
	struct list_head list;
61
	char *token;
62
	char *value;
63
	struct conf_info conf;
64
};
65

66
static LIST_HEAD(conf_head);
67

68
static const char *separators = ":";
69

70
static int configured;
71

72
#define TRAILER_ARG_STRING "$ARG"
73

74
static const char *git_generated_prefixes[] = {
75
	"Signed-off-by: ",
76
	"(cherry picked from commit ",
77
	NULL
78
};
79

80
/* Iterate over the elements of the list. */
81
#define list_for_each_dir(pos, head, is_reverse) \
82
	for (pos = is_reverse ? (head)->prev : (head)->next; \
83
		pos != (head); \
84
		pos = is_reverse ? pos->prev : pos->next)
85

86
static int after_or_end(enum trailer_where where)
87
{
88
	return (where == WHERE_AFTER) || (where == WHERE_END);
89
}
90

91
/*
92
 * Return the length of the string not including any final
93
 * punctuation. E.g., the input "Signed-off-by:" would return
94
 * 13, stripping the trailing punctuation but retaining
95
 * internal punctuation.
96
 */
97
static size_t token_len_without_separator(const char *token, size_t len)
98
{
99
	while (len > 0 && !isalnum(token[len - 1]))
100
		len--;
101
	return len;
102
}
103

104
static int same_token(struct trailer_item *a, struct arg_item *b)
105
{
106
	size_t a_len, b_len, min_len;
107

108
	if (!a->token)
109
		return 0;
110

111
	a_len = token_len_without_separator(a->token, strlen(a->token));
112
	b_len = token_len_without_separator(b->token, strlen(b->token));
113
	min_len = (a_len > b_len) ? b_len : a_len;
114

115
	return !strncasecmp(a->token, b->token, min_len);
116
}
117

118
static int same_value(struct trailer_item *a, struct arg_item *b)
119
{
120
	return !strcasecmp(a->value, b->value);
121
}
122

123
static int same_trailer(struct trailer_item *a, struct arg_item *b)
124
{
125
	return same_token(a, b) && same_value(a, b);
126
}
127

128
static inline int is_blank_line(const char *str)
129
{
130
	const char *s = str;
131
	while (*s && *s != '\n' && isspace(*s))
132
		s++;
133
	return !*s || *s == '\n';
134
}
135

136
static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
137
{
138
	const char *ptr = strstr(sb->buf, a);
139
	if (ptr)
140
		strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
141
}
142

143
static void free_trailer_item(struct trailer_item *item)
144
{
145
	free(item->token);
146
	free(item->value);
147
	free(item);
148
}
149

150
static void free_arg_item(struct arg_item *item)
151
{
152
	free(item->conf.name);
153
	free(item->conf.key);
154
	free(item->conf.command);
155
	free(item->conf.cmd);
156
	free(item->token);
157
	free(item->value);
158
	free(item);
159
}
160

161
static char last_non_space_char(const char *s)
162
{
163
	int i;
164
	for (i = strlen(s) - 1; i >= 0; i--)
165
		if (!isspace(s[i]))
166
			return s[i];
167
	return '\0';
168
}
169

170
static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok)
171
{
172
	struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
173
	new_item->token = arg_tok->token;
174
	new_item->value = arg_tok->value;
175
	arg_tok->token = arg_tok->value = NULL;
176
	free_arg_item(arg_tok);
177
	return new_item;
178
}
179

180
static void add_arg_to_input_list(struct trailer_item *on_tok,
181
				  struct arg_item *arg_tok)
182
{
183
	int aoe = after_or_end(arg_tok->conf.where);
184
	struct trailer_item *to_add = trailer_from_arg(arg_tok);
185
	if (aoe)
186
		list_add(&to_add->list, &on_tok->list);
187
	else
188
		list_add_tail(&to_add->list, &on_tok->list);
189
}
190

191
static int check_if_different(struct trailer_item *in_tok,
192
			      struct arg_item *arg_tok,
193
			      int check_all,
194
			      struct list_head *head)
195
{
196
	enum trailer_where where = arg_tok->conf.where;
197
	struct list_head *next_head;
198
	do {
199
		if (same_trailer(in_tok, arg_tok))
200
			return 0;
201
		/*
202
		 * if we want to add a trailer after another one,
203
		 * we have to check those before this one
204
		 */
205
		next_head = after_or_end(where) ? in_tok->list.prev
206
						: in_tok->list.next;
207
		if (next_head == head)
208
			break;
209
		in_tok = list_entry(next_head, struct trailer_item, list);
210
	} while (check_all);
211
	return 1;
212
}
213

214
static char *apply_command(struct conf_info *conf, const char *arg)
215
{
216
	struct strbuf cmd = STRBUF_INIT;
217
	struct strbuf buf = STRBUF_INIT;
218
	struct child_process cp = CHILD_PROCESS_INIT;
219
	char *result;
220

221
	if (conf->cmd) {
222
		strbuf_addstr(&cmd, conf->cmd);
223
		strvec_push(&cp.args, cmd.buf);
224
		if (arg)
225
			strvec_push(&cp.args, arg);
226
	} else if (conf->command) {
227
		strbuf_addstr(&cmd, conf->command);
228
		if (arg)
229
			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
230
		strvec_push(&cp.args, cmd.buf);
231
	}
232
	strvec_pushv(&cp.env, (const char **)local_repo_env);
233
	cp.no_stdin = 1;
234
	cp.use_shell = 1;
235

236
	if (capture_command(&cp, &buf, 1024)) {
237
		error(_("running trailer command '%s' failed"), cmd.buf);
238
		strbuf_release(&buf);
239
		result = xstrdup("");
240
	} else {
241
		strbuf_trim(&buf);
242
		result = strbuf_detach(&buf, NULL);
243
	}
244

245
	strbuf_release(&cmd);
246
	return result;
247
}
248

249
static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
250
{
251
	if (arg_tok->conf.command || arg_tok->conf.cmd) {
252
		const char *arg;
253
		if (arg_tok->value && arg_tok->value[0]) {
254
			arg = arg_tok->value;
255
		} else {
256
			if (in_tok && in_tok->value)
257
				arg = xstrdup(in_tok->value);
258
			else
259
				arg = xstrdup("");
260
		}
261
		arg_tok->value = apply_command(&arg_tok->conf, arg);
262
		free((char *)arg);
263
	}
264
}
265

266
static void apply_arg_if_exists(struct trailer_item *in_tok,
267
				struct arg_item *arg_tok,
268
				struct trailer_item *on_tok,
269
				struct list_head *head)
270
{
271
	switch (arg_tok->conf.if_exists) {
272
	case EXISTS_DO_NOTHING:
273
		free_arg_item(arg_tok);
274
		break;
275
	case EXISTS_REPLACE:
276
		apply_item_command(in_tok, arg_tok);
277
		add_arg_to_input_list(on_tok, arg_tok);
278
		list_del(&in_tok->list);
279
		free_trailer_item(in_tok);
280
		break;
281
	case EXISTS_ADD:
282
		apply_item_command(in_tok, arg_tok);
283
		add_arg_to_input_list(on_tok, arg_tok);
284
		break;
285
	case EXISTS_ADD_IF_DIFFERENT:
286
		apply_item_command(in_tok, arg_tok);
287
		if (check_if_different(in_tok, arg_tok, 1, head))
288
			add_arg_to_input_list(on_tok, arg_tok);
289
		else
290
			free_arg_item(arg_tok);
291
		break;
292
	case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
293
		apply_item_command(in_tok, arg_tok);
294
		if (check_if_different(on_tok, arg_tok, 0, head))
295
			add_arg_to_input_list(on_tok, arg_tok);
296
		else
297
			free_arg_item(arg_tok);
298
		break;
299
	default:
300
		BUG("trailer.c: unhandled value %d",
301
		    arg_tok->conf.if_exists);
302
	}
303
}
304

305
static void apply_arg_if_missing(struct list_head *head,
306
				 struct arg_item *arg_tok)
307
{
308
	enum trailer_where where;
309
	struct trailer_item *to_add;
310

311
	switch (arg_tok->conf.if_missing) {
312
	case MISSING_DO_NOTHING:
313
		free_arg_item(arg_tok);
314
		break;
315
	case MISSING_ADD:
316
		where = arg_tok->conf.where;
317
		apply_item_command(NULL, arg_tok);
318
		to_add = trailer_from_arg(arg_tok);
319
		if (after_or_end(where))
320
			list_add_tail(&to_add->list, head);
321
		else
322
			list_add(&to_add->list, head);
323
		break;
324
	default:
325
		BUG("trailer.c: unhandled value %d",
326
		    arg_tok->conf.if_missing);
327
	}
328
}
329

330
static int find_same_and_apply_arg(struct list_head *head,
331
				   struct arg_item *arg_tok)
332
{
333
	struct list_head *pos;
334
	struct trailer_item *in_tok;
335
	struct trailer_item *on_tok;
336

337
	enum trailer_where where = arg_tok->conf.where;
338
	int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
339
	int backwards = after_or_end(where);
340
	struct trailer_item *start_tok;
341

342
	if (list_empty(head))
343
		return 0;
344

345
	start_tok = list_entry(backwards ? head->prev : head->next,
346
			       struct trailer_item,
347
			       list);
348

349
	list_for_each_dir(pos, head, backwards) {
350
		in_tok = list_entry(pos, struct trailer_item, list);
351
		if (!same_token(in_tok, arg_tok))
352
			continue;
353
		on_tok = middle ? in_tok : start_tok;
354
		apply_arg_if_exists(in_tok, arg_tok, on_tok, head);
355
		return 1;
356
	}
357
	return 0;
358
}
359

360
void process_trailers_lists(struct list_head *head,
361
			    struct list_head *arg_head)
362
{
363
	struct list_head *pos, *p;
364
	struct arg_item *arg_tok;
365

366
	list_for_each_safe(pos, p, arg_head) {
367
		int applied = 0;
368
		arg_tok = list_entry(pos, struct arg_item, list);
369

370
		list_del(pos);
371

372
		applied = find_same_and_apply_arg(head, arg_tok);
373

374
		if (!applied)
375
			apply_arg_if_missing(head, arg_tok);
376
	}
377
}
378

379
int trailer_set_where(enum trailer_where *item, const char *value)
380
{
381
	if (!value)
382
		*item = WHERE_DEFAULT;
383
	else if (!strcasecmp("after", value))
384
		*item = WHERE_AFTER;
385
	else if (!strcasecmp("before", value))
386
		*item = WHERE_BEFORE;
387
	else if (!strcasecmp("end", value))
388
		*item = WHERE_END;
389
	else if (!strcasecmp("start", value))
390
		*item = WHERE_START;
391
	else
392
		return -1;
393
	return 0;
394
}
395

396
int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
397
{
398
	if (!value)
399
		*item = EXISTS_DEFAULT;
400
	else if (!strcasecmp("addIfDifferent", value))
401
		*item = EXISTS_ADD_IF_DIFFERENT;
402
	else if (!strcasecmp("addIfDifferentNeighbor", value))
403
		*item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
404
	else if (!strcasecmp("add", value))
405
		*item = EXISTS_ADD;
406
	else if (!strcasecmp("replace", value))
407
		*item = EXISTS_REPLACE;
408
	else if (!strcasecmp("doNothing", value))
409
		*item = EXISTS_DO_NOTHING;
410
	else
411
		return -1;
412
	return 0;
413
}
414

415
int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)
416
{
417
	if (!value)
418
		*item = MISSING_DEFAULT;
419
	else if (!strcasecmp("doNothing", value))
420
		*item = MISSING_DO_NOTHING;
421
	else if (!strcasecmp("add", value))
422
		*item = MISSING_ADD;
423
	else
424
		return -1;
425
	return 0;
426
}
427

428
static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
429
{
430
	*dst = *src;
431
	dst->name = xstrdup_or_null(src->name);
432
	dst->key = xstrdup_or_null(src->key);
433
	dst->command = xstrdup_or_null(src->command);
434
	dst->cmd = xstrdup_or_null(src->cmd);
435
}
436

437
static struct arg_item *get_conf_item(const char *name)
438
{
439
	struct list_head *pos;
440
	struct arg_item *item;
441

442
	/* Look up item with same name */
443
	list_for_each(pos, &conf_head) {
444
		item = list_entry(pos, struct arg_item, list);
445
		if (!strcasecmp(item->conf.name, name))
446
			return item;
447
	}
448

449
	/* Item does not already exists, create it */
450
	CALLOC_ARRAY(item, 1);
451
	duplicate_conf(&item->conf, &default_conf_info);
452
	item->conf.name = xstrdup(name);
453

454
	list_add_tail(&item->list, &conf_head);
455

456
	return item;
457
}
458

459
enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
460
			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
461

462
static struct {
463
	const char *name;
464
	enum trailer_info_type type;
465
} trailer_config_items[] = {
466
	{ "key", TRAILER_KEY },
467
	{ "command", TRAILER_COMMAND },
468
	{ "cmd", TRAILER_CMD },
469
	{ "where", TRAILER_WHERE },
470
	{ "ifexists", TRAILER_IF_EXISTS },
471
	{ "ifmissing", TRAILER_IF_MISSING }
472
};
473

474
static int git_trailer_default_config(const char *conf_key, const char *value,
475
				      const struct config_context *ctx UNUSED,
476
				      void *cb UNUSED)
477
{
478
	const char *trailer_item, *variable_name;
479

480
	if (!skip_prefix(conf_key, "trailer.", &trailer_item))
481
		return 0;
482

483
	variable_name = strrchr(trailer_item, '.');
484
	if (!variable_name) {
485
		if (!strcmp(trailer_item, "where")) {
486
			if (trailer_set_where(&default_conf_info.where,
487
					      value) < 0)
488
				warning(_("unknown value '%s' for key '%s'"),
489
					value, conf_key);
490
		} else if (!strcmp(trailer_item, "ifexists")) {
491
			if (trailer_set_if_exists(&default_conf_info.if_exists,
492
						  value) < 0)
493
				warning(_("unknown value '%s' for key '%s'"),
494
					value, conf_key);
495
		} else if (!strcmp(trailer_item, "ifmissing")) {
496
			if (trailer_set_if_missing(&default_conf_info.if_missing,
497
						   value) < 0)
498
				warning(_("unknown value '%s' for key '%s'"),
499
					value, conf_key);
500
		} else if (!strcmp(trailer_item, "separators")) {
501
			if (!value)
502
				return config_error_nonbool(conf_key);
503
			separators = xstrdup(value);
504
		}
505
	}
506
	return 0;
507
}
508

509
static int git_trailer_config(const char *conf_key, const char *value,
510
			      const struct config_context *ctx UNUSED,
511
			      void *cb UNUSED)
512
{
513
	const char *trailer_item, *variable_name;
514
	struct arg_item *item;
515
	struct conf_info *conf;
516
	char *name = NULL;
517
	enum trailer_info_type type;
518
	int i;
519

520
	if (!skip_prefix(conf_key, "trailer.", &trailer_item))
521
		return 0;
522

523
	variable_name = strrchr(trailer_item, '.');
524
	if (!variable_name)
525
		return 0;
526

527
	variable_name++;
528
	for (i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {
529
		if (strcmp(trailer_config_items[i].name, variable_name))
530
			continue;
531
		name = xstrndup(trailer_item,  variable_name - trailer_item - 1);
532
		type = trailer_config_items[i].type;
533
		break;
534
	}
535

536
	if (!name)
537
		return 0;
538

539
	item = get_conf_item(name);
540
	conf = &item->conf;
541
	free(name);
542

543
	switch (type) {
544
	case TRAILER_KEY:
545
		if (conf->key)
546
			warning(_("more than one %s"), conf_key);
547
		if (!value)
548
			return config_error_nonbool(conf_key);
549
		conf->key = xstrdup(value);
550
		break;
551
	case TRAILER_COMMAND:
552
		if (conf->command)
553
			warning(_("more than one %s"), conf_key);
554
		if (!value)
555
			return config_error_nonbool(conf_key);
556
		conf->command = xstrdup(value);
557
		break;
558
	case TRAILER_CMD:
559
		if (conf->cmd)
560
			warning(_("more than one %s"), conf_key);
561
		if (!value)
562
			return config_error_nonbool(conf_key);
563
		conf->cmd = xstrdup(value);
564
		break;
565
	case TRAILER_WHERE:
566
		if (trailer_set_where(&conf->where, value))
567
			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
568
		break;
569
	case TRAILER_IF_EXISTS:
570
		if (trailer_set_if_exists(&conf->if_exists, value))
571
			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
572
		break;
573
	case TRAILER_IF_MISSING:
574
		if (trailer_set_if_missing(&conf->if_missing, value))
575
			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
576
		break;
577
	default:
578
		BUG("trailer.c: unhandled type %d", type);
579
	}
580
	return 0;
581
}
582

583
void trailer_config_init(void)
584
{
585
	if (configured)
586
		return;
587

588
	/* Default config must be setup first */
589
	default_conf_info.where = WHERE_END;
590
	default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
591
	default_conf_info.if_missing = MISSING_ADD;
592
	git_config(git_trailer_default_config, NULL);
593
	git_config(git_trailer_config, NULL);
594
	configured = 1;
595
}
596

597
static const char *token_from_item(struct arg_item *item, char *tok)
598
{
599
	if (item->conf.key)
600
		return item->conf.key;
601
	if (tok)
602
		return tok;
603
	return item->conf.name;
604
}
605

606
static int token_matches_item(const char *tok, struct arg_item *item, size_t tok_len)
607
{
608
	if (!strncasecmp(tok, item->conf.name, tok_len))
609
		return 1;
610
	return item->conf.key ? !strncasecmp(tok, item->conf.key, tok_len) : 0;
611
}
612

613
/*
614
 * If the given line is of the form
615
 * "<token><optional whitespace><separator>..." or "<separator>...", return the
616
 * location of the separator. Otherwise, return -1.  The optional whitespace
617
 * is allowed there primarily to allow things like "Bug #43" where <token> is
618
 * "Bug" and <separator> is "#".
619
 *
620
 * The separator-starts-line case (in which this function returns 0) is
621
 * distinguished from the non-well-formed-line case (in which this function
622
 * returns -1) because some callers of this function need such a distinction.
623
 */
624
static ssize_t find_separator(const char *line, const char *separators)
625
{
626
	int whitespace_found = 0;
627
	const char *c;
628
	for (c = line; *c; c++) {
629
		if (strchr(separators, *c))
630
			return c - line;
631
		if (!whitespace_found && (isalnum(*c) || *c == '-'))
632
			continue;
633
		if (c != line && (*c == ' ' || *c == '\t')) {
634
			whitespace_found = 1;
635
			continue;
636
		}
637
		break;
638
	}
639
	return -1;
640
}
641

642
/*
643
 * Obtain the token, value, and conf from the given trailer.
644
 *
645
 * separator_pos must not be 0, since the token cannot be an empty string.
646
 *
647
 * If separator_pos is -1, interpret the whole trailer as a token.
648
 */
649
static void parse_trailer(struct strbuf *tok, struct strbuf *val,
650
			 const struct conf_info **conf, const char *trailer,
651
			 ssize_t separator_pos)
652
{
653
	struct arg_item *item;
654
	size_t tok_len;
655
	struct list_head *pos;
656

657
	if (separator_pos != -1) {
658
		strbuf_add(tok, trailer, separator_pos);
659
		strbuf_trim(tok);
660
		strbuf_addstr(val, trailer + separator_pos + 1);
661
		strbuf_trim(val);
662
	} else {
663
		strbuf_addstr(tok, trailer);
664
		strbuf_trim(tok);
665
	}
666

667
	/* Lookup if the token matches something in the config */
668
	tok_len = token_len_without_separator(tok->buf, tok->len);
669
	if (conf)
670
		*conf = &default_conf_info;
671
	list_for_each(pos, &conf_head) {
672
		item = list_entry(pos, struct arg_item, list);
673
		if (token_matches_item(tok->buf, item, tok_len)) {
674
			char *tok_buf = strbuf_detach(tok, NULL);
675
			if (conf)
676
				*conf = &item->conf;
677
			strbuf_addstr(tok, token_from_item(item, tok_buf));
678
			free(tok_buf);
679
			break;
680
		}
681
	}
682
}
683

684
static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
685
					     char *val)
686
{
687
	struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
688
	new_item->token = tok;
689
	new_item->value = val;
690
	list_add_tail(&new_item->list, head);
691
	return new_item;
692
}
693

694
static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
695
			 const struct conf_info *conf,
696
			 const struct new_trailer_item *new_trailer_item)
697
{
698
	struct arg_item *new_item = xcalloc(1, sizeof(*new_item));
699
	new_item->token = tok;
700
	new_item->value = val;
701
	duplicate_conf(&new_item->conf, conf);
702
	if (new_trailer_item) {
703
		if (new_trailer_item->where != WHERE_DEFAULT)
704
			new_item->conf.where = new_trailer_item->where;
705
		if (new_trailer_item->if_exists != EXISTS_DEFAULT)
706
			new_item->conf.if_exists = new_trailer_item->if_exists;
707
		if (new_trailer_item->if_missing != MISSING_DEFAULT)
708
			new_item->conf.if_missing = new_trailer_item->if_missing;
709
	}
710
	list_add_tail(&new_item->list, arg_head);
711
}
712

713
void parse_trailers_from_config(struct list_head *config_head)
714
{
715
	struct arg_item *item;
716
	struct list_head *pos;
717

718
	/* Add an arg item for each configured trailer with a command */
719
	list_for_each(pos, &conf_head) {
720
		item = list_entry(pos, struct arg_item, list);
721
		if (item->conf.command)
722
			add_arg_item(config_head,
723
				     xstrdup(token_from_item(item, NULL)),
724
				     xstrdup(""),
725
				     &item->conf, NULL);
726
	}
727
}
728

729
void parse_trailers_from_command_line_args(struct list_head *arg_head,
730
					   struct list_head *new_trailer_head)
731
{
732
	struct strbuf tok = STRBUF_INIT;
733
	struct strbuf val = STRBUF_INIT;
734
	const struct conf_info *conf;
735
	struct list_head *pos;
736

737
	/*
738
	 * In command-line arguments, '=' is accepted (in addition to the
739
	 * separators that are defined).
740
	 */
741
	char *cl_separators = xstrfmt("=%s", separators);
742

743
	/* Add an arg item for each trailer on the command line */
744
	list_for_each(pos, new_trailer_head) {
745
		struct new_trailer_item *tr =
746
			list_entry(pos, struct new_trailer_item, list);
747
		ssize_t separator_pos = find_separator(tr->text, cl_separators);
748

749
		if (separator_pos == 0) {
750
			struct strbuf sb = STRBUF_INIT;
751
			strbuf_addstr(&sb, tr->text);
752
			strbuf_trim(&sb);
753
			error(_("empty trailer token in trailer '%.*s'"),
754
			      (int) sb.len, sb.buf);
755
			strbuf_release(&sb);
756
		} else {
757
			parse_trailer(&tok, &val, &conf, tr->text,
758
				      separator_pos);
759
			add_arg_item(arg_head,
760
				     strbuf_detach(&tok, NULL),
761
				     strbuf_detach(&val, NULL),
762
				     conf, tr);
763
		}
764
	}
765

766
	free(cl_separators);
767
}
768

769
static const char *next_line(const char *str)
770
{
771
	const char *nl = strchrnul(str, '\n');
772
	return nl + !!*nl;
773
}
774

775
/*
776
 * Return the position of the start of the last line. If len is 0, return -1.
777
 */
778
static ssize_t last_line(const char *buf, size_t len)
779
{
780
	ssize_t i;
781
	if (len == 0)
782
		return -1;
783
	if (len == 1)
784
		return 0;
785
	/*
786
	 * Skip the last character (in addition to the null terminator),
787
	 * because if the last character is a newline, it is considered as part
788
	 * of the last line anyway.
789
	 */
790
	i = len - 2;
791

792
	for (; i >= 0; i--) {
793
		if (buf[i] == '\n')
794
			return i + 1;
795
	}
796
	return 0;
797
}
798

799
/*
800
 * Find the end of the log message as an offset from the start of the input
801
 * (where callers of this function are interested in looking for a trailers
802
 * block in the same input). We have to consider two categories of content that
803
 * can come at the end of the input which we want to ignore (because they don't
804
 * belong in the log message):
805
 *
806
 * (1) the "patch part" which begins with a "---" divider and has patch
807
 * information (like the output of git-format-patch), and
808
 *
809
 * (2) any trailing comment lines, blank lines like in the output of "git
810
 * commit -v", or stuff below the "cut" (scissor) line.
811
 *
812
 * As a formula, the situation looks like this:
813
 *
814
 *     INPUT = LOG MESSAGE + IGNORED
815
 *
816
 * where IGNORED can be either of the two categories described above. It may be
817
 * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
818
 * contains a trailer block, but that's not the concern of this function.
819
 */
820
static size_t find_end_of_log_message(const char *input, int no_divider)
821
{
822
	size_t end;
823
	const char *s;
824

825
	/* Assume the naive end of the input is already what we want. */
826
	end = strlen(input);
827

828
	/* Optionally skip over any patch part ("---" line and below). */
829
	if (!no_divider) {
830
		for (s = input; *s; s = next_line(s)) {
831
			const char *v;
832

833
			if (skip_prefix(s, "---", &v) && isspace(*v)) {
834
				end = s - input;
835
				break;
836
			}
837
		}
838
	}
839

840
	/* Skip over other ignorable bits. */
841
	return end - ignored_log_message_bytes(input, end);
842
}
843

844
/*
845
 * Return the position of the first trailer line or len if there are no
846
 * trailers.
847
 */
848
static size_t find_trailer_block_start(const char *buf, size_t len)
849
{
850
	const char *s;
851
	ssize_t end_of_title, l;
852
	int only_spaces = 1;
853
	int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
854
	/*
855
	 * Number of possible continuation lines encountered. This will be
856
	 * reset to 0 if we encounter a trailer (since those lines are to be
857
	 * considered continuations of that trailer), and added to
858
	 * non_trailer_lines if we encounter a non-trailer (since those lines
859
	 * are to be considered non-trailers).
860
	 */
861
	int possible_continuation_lines = 0;
862

863
	/* The first paragraph is the title and cannot be trailers */
864
	for (s = buf; s < buf + len; s = next_line(s)) {
865
		if (starts_with_mem(s, buf + len - s, comment_line_str))
866
			continue;
867
		if (is_blank_line(s))
868
			break;
869
	}
870
	end_of_title = s - buf;
871

872
	/*
873
	 * Get the start of the trailers by looking starting from the end for a
874
	 * blank line before a set of non-blank lines that (i) are all
875
	 * trailers, or (ii) contains at least one Git-generated trailer and
876
	 * consists of at least 25% trailers.
877
	 */
878
	for (l = last_line(buf, len);
879
	     l >= end_of_title;
880
	     l = last_line(buf, l)) {
881
		const char *bol = buf + l;
882
		const char **p;
883
		ssize_t separator_pos;
884

885
		if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
886
			non_trailer_lines += possible_continuation_lines;
887
			possible_continuation_lines = 0;
888
			continue;
889
		}
890
		if (is_blank_line(bol)) {
891
			if (only_spaces)
892
				continue;
893
			non_trailer_lines += possible_continuation_lines;
894
			if (recognized_prefix &&
895
			    trailer_lines * 3 >= non_trailer_lines)
896
				return next_line(bol) - buf;
897
			else if (trailer_lines && !non_trailer_lines)
898
				return next_line(bol) - buf;
899
			return len;
900
		}
901
		only_spaces = 0;
902

903
		for (p = git_generated_prefixes; *p; p++) {
904
			if (starts_with(bol, *p)) {
905
				trailer_lines++;
906
				possible_continuation_lines = 0;
907
				recognized_prefix = 1;
908
				goto continue_outer_loop;
909
			}
910
		}
911

912
		separator_pos = find_separator(bol, separators);
913
		if (separator_pos >= 1 && !isspace(bol[0])) {
914
			struct list_head *pos;
915

916
			trailer_lines++;
917
			possible_continuation_lines = 0;
918
			if (recognized_prefix)
919
				continue;
920
			list_for_each(pos, &conf_head) {
921
				struct arg_item *item;
922
				item = list_entry(pos, struct arg_item, list);
923
				if (token_matches_item(bol, item,
924
						       separator_pos)) {
925
					recognized_prefix = 1;
926
					break;
927
				}
928
			}
929
		} else if (isspace(bol[0]))
930
			possible_continuation_lines++;
931
		else {
932
			non_trailer_lines++;
933
			non_trailer_lines += possible_continuation_lines;
934
			possible_continuation_lines = 0;
935
		}
936
continue_outer_loop:
937
		;
938
	}
939

940
	return len;
941
}
942

943
static int ends_with_blank_line(const char *buf, size_t len)
944
{
945
	ssize_t ll = last_line(buf, len);
946
	if (ll < 0)
947
		return 0;
948
	return is_blank_line(buf + ll);
949
}
950

951
static void unfold_value(struct strbuf *val)
952
{
953
	struct strbuf out = STRBUF_INIT;
954
	size_t i;
955

956
	strbuf_grow(&out, val->len);
957
	i = 0;
958
	while (i < val->len) {
959
		char c = val->buf[i++];
960
		if (c == '\n') {
961
			/* Collapse continuation down to a single space. */
962
			while (i < val->len && isspace(val->buf[i]))
963
				i++;
964
			strbuf_addch(&out, ' ');
965
		} else {
966
			strbuf_addch(&out, c);
967
		}
968
	}
969

970
	/* Empty lines may have left us with whitespace cruft at the edges */
971
	strbuf_trim(&out);
972

973
	/* output goes back to val as if we modified it in-place */
974
	strbuf_swap(&out, val);
975
	strbuf_release(&out);
976
}
977

978
static struct trailer_info *trailer_info_new(void)
979
{
980
	struct trailer_info *info = xcalloc(1, sizeof(*info));
981
	return info;
982
}
983

984
static struct trailer_info *trailer_info_get(const struct process_trailer_options *opts,
985
					     const char *str)
986
{
987
	struct trailer_info *info = trailer_info_new();
988
	size_t end_of_log_message = 0, trailer_block_start = 0;
989
	struct strbuf **trailer_lines, **ptr;
990
	char **trailer_strings = NULL;
991
	size_t nr = 0, alloc = 0;
992
	char **last = NULL;
993

994
	trailer_config_init();
995

996
	end_of_log_message = find_end_of_log_message(str, opts->no_divider);
997
	trailer_block_start = find_trailer_block_start(str, end_of_log_message);
998

999
	trailer_lines = strbuf_split_buf(str + trailer_block_start,
1000
					 end_of_log_message - trailer_block_start,
1001
					 '\n',
1002
					 0);
1003
	for (ptr = trailer_lines; *ptr; ptr++) {
1004
		if (last && isspace((*ptr)->buf[0])) {
1005
			struct strbuf sb = STRBUF_INIT;
1006
			strbuf_attach(&sb, *last, strlen(*last), strlen(*last));
1007
			strbuf_addbuf(&sb, *ptr);
1008
			*last = strbuf_detach(&sb, NULL);
1009
			continue;
1010
		}
1011
		ALLOC_GROW(trailer_strings, nr + 1, alloc);
1012
		trailer_strings[nr] = strbuf_detach(*ptr, NULL);
1013
		last = find_separator(trailer_strings[nr], separators) >= 1
1014
			? &trailer_strings[nr]
1015
			: NULL;
1016
		nr++;
1017
	}
1018
	strbuf_list_free(trailer_lines);
1019

1020
	info->blank_line_before_trailer = ends_with_blank_line(str,
1021
							       trailer_block_start);
1022
	info->trailer_block_start = trailer_block_start;
1023
	info->trailer_block_end = end_of_log_message;
1024
	info->trailers = trailer_strings;
1025
	info->trailer_nr = nr;
1026

1027
	return info;
1028
}
1029

1030
/*
1031
 * Parse trailers in "str", populating the trailer info and "trailer_objects"
1032
 * linked list structure.
1033
 */
1034
struct trailer_info *parse_trailers(const struct process_trailer_options *opts,
1035
				    const char *str,
1036
				    struct list_head *trailer_objects)
1037
{
1038
	struct trailer_info *info;
1039
	struct strbuf tok = STRBUF_INIT;
1040
	struct strbuf val = STRBUF_INIT;
1041
	size_t i;
1042

1043
	info = trailer_info_get(opts, str);
1044

1045
	for (i = 0; i < info->trailer_nr; i++) {
1046
		int separator_pos;
1047
		char *trailer = info->trailers[i];
1048
		if (starts_with(trailer, comment_line_str))
1049
			continue;
1050
		separator_pos = find_separator(trailer, separators);
1051
		if (separator_pos >= 1) {
1052
			parse_trailer(&tok, &val, NULL, trailer,
1053
				      separator_pos);
1054
			if (opts->unfold)
1055
				unfold_value(&val);
1056
			add_trailer_item(trailer_objects,
1057
					 strbuf_detach(&tok, NULL),
1058
					 strbuf_detach(&val, NULL));
1059
		} else if (!opts->only_trailers) {
1060
			strbuf_addstr(&val, trailer);
1061
			strbuf_strip_suffix(&val, "\n");
1062
			add_trailer_item(trailer_objects,
1063
					 NULL,
1064
					 strbuf_detach(&val, NULL));
1065
		}
1066
	}
1067

1068
	return info;
1069
}
1070

1071
void free_trailers(struct list_head *trailers)
1072
{
1073
	struct list_head *pos, *p;
1074
	list_for_each_safe(pos, p, trailers) {
1075
		list_del(pos);
1076
		free_trailer_item(list_entry(pos, struct trailer_item, list));
1077
	}
1078
}
1079

1080
size_t trailer_block_start(struct trailer_info *info)
1081
{
1082
	return info->trailer_block_start;
1083
}
1084

1085
size_t trailer_block_end(struct trailer_info *info)
1086
{
1087
	return info->trailer_block_end;
1088
}
1089

1090
int blank_line_before_trailer_block(struct trailer_info *info)
1091
{
1092
	return info->blank_line_before_trailer;
1093
}
1094

1095
void trailer_info_release(struct trailer_info *info)
1096
{
1097
	size_t i;
1098
	for (i = 0; i < info->trailer_nr; i++)
1099
		free(info->trailers[i]);
1100
	free(info->trailers);
1101
	free(info);
1102
}
1103

1104
void format_trailers(const struct process_trailer_options *opts,
1105
		     struct list_head *trailers,
1106
		     struct strbuf *out)
1107
{
1108
	size_t origlen = out->len;
1109
	struct list_head *pos;
1110
	struct trailer_item *item;
1111

1112
	list_for_each(pos, trailers) {
1113
		item = list_entry(pos, struct trailer_item, list);
1114
		if (item->token) {
1115
			struct strbuf tok = STRBUF_INIT;
1116
			struct strbuf val = STRBUF_INIT;
1117
			strbuf_addstr(&tok, item->token);
1118
			strbuf_addstr(&val, item->value);
1119

1120
			/*
1121
			 * Skip key/value pairs where the value was empty. This
1122
			 * can happen from trailers specified without a
1123
			 * separator, like `--trailer "Reviewed-by"` (no
1124
			 * corresponding value).
1125
			 */
1126
			if (opts->trim_empty && !strlen(item->value))
1127
				continue;
1128

1129
			if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
1130
				if (opts->separator && out->len != origlen)
1131
					strbuf_addbuf(out, opts->separator);
1132
				if (!opts->value_only)
1133
					strbuf_addbuf(out, &tok);
1134
				if (!opts->key_only && !opts->value_only) {
1135
					if (opts->key_value_separator)
1136
						strbuf_addbuf(out, opts->key_value_separator);
1137
					else {
1138
						char c = last_non_space_char(tok.buf);
1139
						if (c && !strchr(separators, c))
1140
							strbuf_addf(out, "%c ", separators[0]);
1141
					}
1142
				}
1143
				if (!opts->key_only)
1144
					strbuf_addbuf(out, &val);
1145
				if (!opts->separator)
1146
					strbuf_addch(out, '\n');
1147
			}
1148
			strbuf_release(&tok);
1149
			strbuf_release(&val);
1150

1151
		} else if (!opts->only_trailers) {
1152
			if (opts->separator && out->len != origlen) {
1153
				strbuf_addbuf(out, opts->separator);
1154
			}
1155
			strbuf_addstr(out, item->value);
1156
			if (opts->separator)
1157
				strbuf_rtrim(out);
1158
			else
1159
				strbuf_addch(out, '\n');
1160
		}
1161
	}
1162
}
1163

1164
void format_trailers_from_commit(const struct process_trailer_options *opts,
1165
				 const char *msg,
1166
				 struct strbuf *out)
1167
{
1168
	LIST_HEAD(trailer_objects);
1169
	struct trailer_info *info = parse_trailers(opts, msg, &trailer_objects);
1170

1171
	/* If we want the whole block untouched, we can take the fast path. */
1172
	if (!opts->only_trailers && !opts->unfold && !opts->filter &&
1173
	    !opts->separator && !opts->key_only && !opts->value_only &&
1174
	    !opts->key_value_separator) {
1175
		strbuf_add(out, msg + info->trailer_block_start,
1176
			   info->trailer_block_end - info->trailer_block_start);
1177
	} else
1178
		format_trailers(opts, &trailer_objects, out);
1179

1180
	free_trailers(&trailer_objects);
1181
	trailer_info_release(info);
1182
}
1183

1184
void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
1185
{
1186
	struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
1187
	strbuf_init(&iter->key, 0);
1188
	strbuf_init(&iter->val, 0);
1189
	opts.no_divider = 1;
1190
	iter->internal.info = trailer_info_get(&opts, msg);
1191
	iter->internal.cur = 0;
1192
}
1193

1194
int trailer_iterator_advance(struct trailer_iterator *iter)
1195
{
1196
	if (iter->internal.cur < iter->internal.info->trailer_nr) {
1197
		char *line = iter->internal.info->trailers[iter->internal.cur++];
1198
		int separator_pos = find_separator(line, separators);
1199

1200
		iter->raw = line;
1201
		strbuf_reset(&iter->key);
1202
		strbuf_reset(&iter->val);
1203
		parse_trailer(&iter->key, &iter->val, NULL,
1204
			      line, separator_pos);
1205
		/* Always unfold values during iteration. */
1206
		unfold_value(&iter->val);
1207
		return 1;
1208
	}
1209
	return 0;
1210
}
1211

1212
void trailer_iterator_release(struct trailer_iterator *iter)
1213
{
1214
	trailer_info_release(iter->internal.info);
1215
	strbuf_release(&iter->val);
1216
	strbuf_release(&iter->key);
1217
}
1218

1219
int amend_file_with_trailers(const char *path, const struct strvec *trailer_args)
1220
{
1221
	struct child_process run_trailer = CHILD_PROCESS_INIT;
1222

1223
	run_trailer.git_cmd = 1;
1224
	strvec_pushl(&run_trailer.args, "interpret-trailers",
1225
		     "--in-place", "--no-divider",
1226
		     path, NULL);
1227
	strvec_pushv(&run_trailer.args, trailer_args->v);
1228
	return run_command(&run_trailer);
1229
}
1230

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.