Skip to content

Commit accd0e1

Browse files
committed
Merge branch 'lc/rebase-trailer'
"git rebase" learns "--trailer" command to drive the interpret-trailers machinery. * lc/rebase-trailer: rebase: support --trailer commit, tag: parse --trailer with OPT_STRVEC trailer: append trailers without fork/exec trailer: libify a couple of functions interpret-trailers: refactor create_in_place_tempfile() interpret-trailers: factor trailer rewriting
2 parents a7a079c + e4f9d6b commit accd0e1

File tree

11 files changed

+460
-88
lines changed

11 files changed

+460
-88
lines changed

Documentation/git-rebase.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,13 @@ See also INCOMPATIBLE OPTIONS below.
497497
+
498498
See also INCOMPATIBLE OPTIONS below.
499499

500+
--trailer=<trailer>::
501+
Append the given trailer to every rebased commit message, processed
502+
via linkgit:git-interpret-trailers[1]. This option implies
503+
`--force-rebase`.
504+
+
505+
See also INCOMPATIBLE OPTIONS below.
506+
500507
-i::
501508
--interactive::
502509
Make a list of the commits which are about to be rebased. Let the
@@ -653,6 +660,7 @@ are incompatible with the following options:
653660
* --[no-]reapply-cherry-picks when used without --keep-base
654661
* --update-refs
655662
* --root when used without --onto
663+
* --trailer
656664
657665
In addition, the following pairs of options are incompatible:
658666

builtin/commit.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1720,7 +1720,8 @@ int cmd_commit(int argc,
17201720
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
17211721
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
17221722
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
1723-
OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG),
1723+
OPT_STRVEC(0, "trailer", &trailer_args, N_("trailer"),
1724+
N_("add custom trailer(s)")),
17241725
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
17251726
OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
17261727
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1820,6 +1821,9 @@ int cmd_commit(int argc,
18201821
argc = parse_and_validate_options(argc, argv, builtin_commit_options,
18211822
builtin_commit_usage,
18221823
prefix, current_head, &s);
1824+
if (trailer_args.nr)
1825+
trailer_config_init();
1826+
18231827
if (verbose == -1)
18241828
verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
18251829

builtin/interpret-trailers.c

Lines changed: 16 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -93,37 +93,6 @@ static int parse_opt_parse(const struct option *opt, const char *arg,
9393
return 0;
9494
}
9595

96-
static struct tempfile *trailers_tempfile;
97-
98-
static FILE *create_in_place_tempfile(const char *file)
99-
{
100-
struct stat st;
101-
struct strbuf filename_template = STRBUF_INIT;
102-
const char *tail;
103-
FILE *outfile;
104-
105-
if (stat(file, &st))
106-
die_errno(_("could not stat %s"), file);
107-
if (!S_ISREG(st.st_mode))
108-
die(_("file %s is not a regular file"), file);
109-
if (!(st.st_mode & S_IWUSR))
110-
die(_("file %s is not writable by user"), file);
111-
112-
/* Create temporary file in the same directory as the original */
113-
tail = strrchr(file, '/');
114-
if (tail)
115-
strbuf_add(&filename_template, file, tail - file + 1);
116-
strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
117-
118-
trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
119-
strbuf_release(&filename_template);
120-
outfile = fdopen_tempfile(trailers_tempfile, "w");
121-
if (!outfile)
122-
die_errno(_("could not open temporary file"));
123-
124-
return outfile;
125-
}
126-
12796
static void read_input_file(struct strbuf *sb, const char *file)
12897
{
12998
if (file) {
@@ -140,55 +109,31 @@ static void interpret_trailers(const struct process_trailer_options *opts,
140109
struct list_head *new_trailer_head,
141110
const char *file)
142111
{
143-
LIST_HEAD(head);
144-
struct strbuf sb = STRBUF_INIT;
145-
struct strbuf trailer_block_sb = STRBUF_INIT;
146-
struct trailer_block *trailer_block;
147-
FILE *outfile = stdout;
112+
struct strbuf input = STRBUF_INIT;
113+
struct strbuf out = STRBUF_INIT;
114+
struct tempfile *tempfile = NULL;
115+
int fd = 1;
148116

149117
trailer_config_init();
150118

151-
read_input_file(&sb, file);
152-
153-
if (opts->in_place)
154-
outfile = create_in_place_tempfile(file);
155-
156-
trailer_block = parse_trailers(opts, sb.buf, &head);
157-
158-
/* Print the lines before the trailer block */
159-
if (!opts->only_trailers)
160-
fwrite(sb.buf, 1, trailer_block_start(trailer_block), outfile);
161-
162-
if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block))
163-
fprintf(outfile, "\n");
119+
read_input_file(&input, file);
164120

165-
166-
if (!opts->only_input) {
167-
LIST_HEAD(config_head);
168-
LIST_HEAD(arg_head);
169-
parse_trailers_from_config(&config_head);
170-
parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
171-
list_splice(&config_head, &arg_head);
172-
process_trailers_lists(&head, &arg_head);
121+
if (opts->in_place) {
122+
tempfile = trailer_create_in_place_tempfile(file);
123+
if (!tempfile)
124+
die(NULL);
125+
fd = tempfile->fd;
173126
}
127+
process_trailers(opts, new_trailer_head, &input, &out);
174128

175-
/* Print trailer block. */
176-
format_trailers(opts, &head, &trailer_block_sb);
177-
free_trailers(&head);
178-
fwrite(trailer_block_sb.buf, 1, trailer_block_sb.len, outfile);
179-
strbuf_release(&trailer_block_sb);
180-
181-
/* Print the lines after the trailer block as is. */
182-
if (!opts->only_trailers)
183-
fwrite(sb.buf + trailer_block_end(trailer_block), 1,
184-
sb.len - trailer_block_end(trailer_block), outfile);
185-
trailer_block_release(trailer_block);
186-
129+
if (write_in_full(fd, out.buf, out.len) < 0)
130+
die_errno(_("could not write to temporary file '%s'"), file);
187131
if (opts->in_place)
188-
if (rename_tempfile(&trailers_tempfile, file))
132+
if (rename_tempfile(&tempfile, file))
189133
die_errno(_("could not rename temporary file to %s"), file);
190134

191-
strbuf_release(&sb);
135+
strbuf_release(&input);
136+
strbuf_release(&out);
192137
}
193138

194139
int cmd_interpret_trailers(int argc,

builtin/rebase.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "reset.h"
3737
#include "trace2.h"
3838
#include "hook.h"
39+
#include "trailer.h"
3940

4041
static char const * const builtin_rebase_usage[] = {
4142
N_("git rebase [-i] [options] [--exec <cmd>] "
@@ -113,6 +114,7 @@ struct rebase_options {
113114
enum action action;
114115
char *reflog_action;
115116
int signoff;
117+
struct strvec trailer_args;
116118
int allow_rerere_autoupdate;
117119
int keep_empty;
118120
int autosquash;
@@ -143,6 +145,7 @@ struct rebase_options {
143145
.flags = REBASE_NO_QUIET, \
144146
.git_am_opts = STRVEC_INIT, \
145147
.exec = STRING_LIST_INIT_NODUP, \
148+
.trailer_args = STRVEC_INIT, \
146149
.git_format_patch_opt = STRBUF_INIT, \
147150
.fork_point = -1, \
148151
.reapply_cherry_picks = -1, \
@@ -166,6 +169,7 @@ static void rebase_options_release(struct rebase_options *opts)
166169
free(opts->strategy);
167170
string_list_clear(&opts->strategy_opts, 0);
168171
strbuf_release(&opts->git_format_patch_opt);
172+
strvec_clear(&opts->trailer_args);
169173
}
170174

171175
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -177,6 +181,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
177181
sequencer_init_config(&replay);
178182

179183
replay.signoff = opts->signoff;
184+
185+
for (size_t i = 0; i < opts->trailer_args.nr; i++)
186+
strvec_push(&replay.trailer_args, opts->trailer_args.v[i]);
187+
180188
replay.allow_ff = !(opts->flags & REBASE_FORCE);
181189
if (opts->allow_rerere_autoupdate)
182190
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
@@ -1134,6 +1142,8 @@ int cmd_rebase(int argc,
11341142
.flags = PARSE_OPT_NOARG,
11351143
.defval = REBASE_DIFFSTAT,
11361144
},
1145+
OPT_STRVEC(0, "trailer", &options.trailer_args, N_("trailer"),
1146+
N_("add custom trailer(s)")),
11371147
OPT_BOOL(0, "signoff", &options.signoff,
11381148
N_("add a Signed-off-by trailer to each commit")),
11391149
OPT_BOOL(0, "committer-date-is-author-date",
@@ -1287,6 +1297,12 @@ int cmd_rebase(int argc,
12871297
builtin_rebase_options,
12881298
builtin_rebase_usage, 0);
12891299

1300+
if (options.trailer_args.nr) {
1301+
if (validate_trailer_args(&options.trailer_args))
1302+
die(NULL);
1303+
options.flags |= REBASE_FORCE;
1304+
}
1305+
12901306
if (preserve_merges_selected)
12911307
die(_("--preserve-merges was replaced by --rebase-merges\n"
12921308
"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
@@ -1544,6 +1560,9 @@ int cmd_rebase(int argc,
15441560
if (options.root && !options.onto_name)
15451561
imply_merge(&options, "--root without --onto");
15461562

1563+
if (options.trailer_args.nr)
1564+
imply_merge(&options, "--trailer");
1565+
15471566
if (isatty(2) && options.flags & REBASE_NO_QUIET)
15481567
strbuf_addstr(&options.git_format_patch_opt, " --progress");
15491568

builtin/tag.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,8 +499,8 @@ int cmd_tag(int argc,
499499
OPT_CALLBACK_F('m', "message", &msg, N_("message"),
500500
N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
501501
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
502-
OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"),
503-
N_("add custom trailer(s)"), PARSE_OPT_NONEG),
502+
OPT_STRVEC(0, "trailer", &trailer_args, N_("trailer"),
503+
N_("add custom trailer(s)")),
504504
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
505505
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
506506
OPT_CLEANUP(&cleanup_arg),
@@ -568,6 +568,9 @@ int cmd_tag(int argc,
568568
if (cmdmode == 'l')
569569
setup_auto_pager("tag", 1);
570570

571+
if (trailer_args.nr)
572+
trailer_config_init();
573+
571574
if (opt.sign == -1)
572575
opt.sign = cmdmode ? 0 : config_sign_tag > 0;
573576

sequencer.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedul
209209
static GIT_PATH_FUNC(rebase_path_no_reschedule_failed_exec, "rebase-merge/no-reschedule-failed-exec")
210210
static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits")
211211
static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits")
212+
static GIT_PATH_FUNC(rebase_path_trailer, "rebase-merge/trailer")
212213

213214
/*
214215
* A 'struct replay_ctx' represents the private state of the sequencer.
@@ -420,6 +421,7 @@ void replay_opts_release(struct replay_opts *opts)
420421
if (opts->revs)
421422
release_revisions(opts->revs);
422423
free(opts->revs);
424+
strvec_clear(&opts->trailer_args);
423425
replay_ctx_release(ctx);
424426
free(opts->ctx);
425427
}
@@ -2027,12 +2029,15 @@ static int append_squash_message(struct strbuf *buf, const char *body,
20272029
if (is_fixup_flag(command, flag) && !seen_squash(ctx)) {
20282030
/*
20292031
* We're replacing the commit message so we need to
2030-
* append the Signed-off-by: trailer if the user
2031-
* requested '--signoff'.
2032+
* append any trailers if the user requested
2033+
* '--signoff' or '--trailer'.
20322034
*/
20332035
if (opts->signoff)
20342036
append_signoff(buf, 0, 0);
20352037

2038+
if (opts->trailer_args.nr)
2039+
amend_strbuf_with_trailers(buf, &opts->trailer_args);
2040+
20362041
if ((command == TODO_FIXUP) &&
20372042
(flag & TODO_REPLACE_FIXUP_MSG) &&
20382043
(file_exists(rebase_path_fixup_msg()) ||
@@ -2451,6 +2456,9 @@ static int do_pick_commit(struct repository *r,
24512456
if (opts->signoff && !is_fixup(command))
24522457
append_signoff(&ctx->message, 0, 0);
24532458

2459+
if (opts->trailer_args.nr && !is_fixup(command))
2460+
amend_strbuf_with_trailers(&ctx->message, &opts->trailer_args);
2461+
24542462
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
24552463
res = -1;
24562464
else if (!opts->strategy ||
@@ -3180,6 +3188,33 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
31803188
parse_strategy_opts(opts, buf->buf);
31813189
}
31823190

3191+
static int read_trailers(struct replay_opts *opts, struct strbuf *buf)
3192+
{
3193+
ssize_t len;
3194+
3195+
strbuf_reset(buf);
3196+
len = strbuf_read_file(buf, rebase_path_trailer(), 0);
3197+
if (len > 0) {
3198+
char *p = buf->buf, *nl;
3199+
3200+
trailer_config_init();
3201+
3202+
while ((nl = strchr(p, '\n'))) {
3203+
*nl = '\0';
3204+
if (!*p)
3205+
return error(_("trailers file contains empty line"));
3206+
strvec_push(&opts->trailer_args, p);
3207+
p = nl + 1;
3208+
}
3209+
} else if (!len) {
3210+
return error(_("trailers file is empty"));
3211+
} else if (errno != ENOENT) {
3212+
return error(_("cannot read trailers files"));
3213+
}
3214+
3215+
return 0;
3216+
}
3217+
31833218
static int read_populate_opts(struct replay_opts *opts)
31843219
{
31853220
struct replay_ctx *ctx = opts->ctx;
@@ -3241,6 +3276,11 @@ static int read_populate_opts(struct replay_opts *opts)
32413276
opts->keep_redundant_commits = 1;
32423277

32433278
read_strategy_opts(opts, &buf);
3279+
3280+
if (read_trailers(opts, &buf)) {
3281+
ret = -1;
3282+
goto done_rebase_i;
3283+
}
32443284
strbuf_reset(&buf);
32453285

32463286
if (read_oneliner(&ctx->current_fixups,
@@ -3336,6 +3376,14 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
33363376
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
33373377
else
33383378
write_file(rebase_path_no_reschedule_failed_exec(), "%s", "");
3379+
if (opts->trailer_args.nr) {
3380+
struct strbuf buf = STRBUF_INIT;
3381+
3382+
for (size_t i = 0; i < opts->trailer_args.nr; i++)
3383+
strbuf_addf(&buf, "%s\n", opts->trailer_args.v[i]);
3384+
write_file(rebase_path_trailer(), "%s", buf.buf);
3385+
strbuf_release(&buf);
3386+
}
33393387

33403388
return 0;
33413389
}

sequencer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ struct replay_opts {
5757
int ignore_date;
5858
int commit_use_reference;
5959

60+
struct strvec trailer_args;
61+
6062
int mainline;
6163

6264
char *gpg_sign;
@@ -84,6 +86,7 @@ struct replay_opts {
8486
#define REPLAY_OPTS_INIT { \
8587
.edit = -1, \
8688
.action = -1, \
89+
.trailer_args = STRVEC_INIT, \
8790
.xopts = STRVEC_INIT, \
8891
.ctx = replay_ctx_new(), \
8992
}

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ integration_tests = [
393393
't3436-rebase-more-options.sh',
394394
't3437-rebase-fixup-options.sh',
395395
't3438-rebase-broken-files.sh',
396+
't3440-rebase-trailer.sh',
396397
't3450-history.sh',
397398
't3451-history-reword.sh',
398399
't3500-cherry.sh',

0 commit comments

Comments
 (0)