Skip to content

Commit 6c1d1af

Browse files
committed
Merge branch 'lc/rebase-trailer' into seen
Refactor code paths to run "interpret-trailers" from "git commit/tag" and use it in "git rebase". Comments? * lc/rebase-trailer: rebase: support --trailer trailer: append trailers in-process and drop the fork to `interpret-trailers` trailer: move process_trailers to trailer.h interpret-trailers: factor out buffer-based processing to process_trailers()
2 parents f1b0a4e + 036e2d4 commit 6c1d1af

File tree

13 files changed

+392
-90
lines changed

13 files changed

+392
-90
lines changed

Documentation/git-rebase.adoc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,16 @@ See also INCOMPATIBLE OPTIONS below.
487487
Add a `Signed-off-by` trailer to all the rebased commits. Note
488488
that if `--interactive` is given then only commits marked to be
489489
picked, edited or reworded will have the trailer added.
490-
+
490+
491491
See also INCOMPATIBLE OPTIONS below.
492492

493+
--trailer=<trailer>::
494+
Append the given trailer line(s) to every rebased commit
495+
message, processed via linkgit:git-interpret-trailers[1].
496+
When this option is present *rebase automatically implies*
497+
`--force-rebase` so that fast‑forwarded commits are also
498+
rewritten.
499+
493500
-i::
494501
--interactive::
495502
Make a list of the commits which are about to be rebased. Let the

builtin/commit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1719,7 +1719,7 @@ int cmd_commit(int argc,
17191719
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
17201720
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
17211721
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
1722-
OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG),
1722+
OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, parse_opt_strvec),
17231723
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
17241724
OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
17251725
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),

builtin/interpret-trailers.c

Lines changed: 8 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "gettext.h"
1111
#include "parse-options.h"
1212
#include "string-list.h"
13-
#include "tempfile.h"
1413
#include "trailer.h"
1514
#include "config.h"
1615

@@ -93,37 +92,6 @@ static int parse_opt_parse(const struct option *opt, const char *arg,
9392
return 0;
9493
}
9594

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-
12795
static void read_input_file(struct strbuf *sb, const char *file)
12896
{
12997
if (file) {
@@ -140,55 +108,20 @@ static void interpret_trailers(const struct process_trailer_options *opts,
140108
struct list_head *new_trailer_head,
141109
const char *file)
142110
{
143-
LIST_HEAD(head);
144111
struct strbuf sb = STRBUF_INIT;
145-
struct strbuf trailer_block_sb = STRBUF_INIT;
146-
struct trailer_block *trailer_block;
147-
FILE *outfile = stdout;
148-
149-
trailer_config_init();
112+
struct strbuf out = STRBUF_INIT;
150113

151114
read_input_file(&sb, file);
152115

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");
164-
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);
173-
}
174-
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);
116+
process_trailers(opts, new_trailer_head, &sb, &out);
186117

187118
if (opts->in_place)
188-
if (rename_tempfile(&trailers_tempfile, file))
189-
die_errno(_("could not rename temporary file to %s"), file);
119+
write_file_buf(file, out.buf, out.len);
120+
else
121+
strbuf_write(&out, stdout);
190122

191123
strbuf_release(&sb);
124+
strbuf_release(&out);
192125
}
193126

194127
int cmd_interpret_trailers(int argc,
@@ -232,6 +165,8 @@ int cmd_interpret_trailers(int argc,
232165
git_interpret_trailers_usage,
233166
options);
234167

168+
trailer_config_init();
169+
235170
if (argc) {
236171
int i;
237172
for (i = 0; i < argc; i++)

builtin/rebase.c

Lines changed: 50 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;
@@ -500,6 +508,23 @@ static int read_basic_state(struct rebase_options *opts)
500508
opts->gpg_sign_opt = xstrdup(buf.buf);
501509
}
502510

511+
strbuf_reset(&buf);
512+
513+
if (strbuf_read_file(&buf, state_dir_path("trailer", opts), 0) >= 0) {
514+
const char *p = buf.buf, *end = buf.buf + buf.len;
515+
516+
while (p < end) {
517+
char *nl = memchr(p, '\n', end - p);
518+
if (!nl)
519+
die("nl shouldn't be NULL");
520+
*nl = '\0';
521+
522+
if (*p)
523+
strvec_push(&opts->trailer_args, p);
524+
525+
p = nl + 1;
526+
}
527+
}
503528
strbuf_release(&buf);
504529

505530
return 0;
@@ -528,6 +553,21 @@ static int rebase_write_basic_state(struct rebase_options *opts)
528553
if (opts->signoff)
529554
write_file(state_dir_path("signoff", opts), "--signoff");
530555

556+
/*
557+
* save opts->trailer_args into state_dir/trailer
558+
*/
559+
if (opts->trailer_args.nr) {
560+
struct strbuf buf = STRBUF_INIT;
561+
562+
for (size_t i = 0; i < opts->trailer_args.nr; i++) {
563+
strbuf_addstr(&buf, opts->trailer_args.v[i]);
564+
strbuf_addch(&buf, '\n');
565+
}
566+
write_file(state_dir_path("trailer", opts),
567+
"%s", buf.buf);
568+
strbuf_release(&buf);
569+
}
570+
531571
return 0;
532572
}
533573

@@ -1132,6 +1172,8 @@ int cmd_rebase(int argc,
11321172
.flags = PARSE_OPT_NOARG,
11331173
.defval = REBASE_DIFFSTAT,
11341174
},
1175+
OPT_STRVEC(0, "trailer", &options.trailer_args, N_("trailer"),
1176+
N_("add custom trailer(s)")),
11351177
OPT_BOOL(0, "signoff", &options.signoff,
11361178
N_("add a Signed-off-by trailer to each commit")),
11371179
OPT_BOOL(0, "committer-date-is-author-date",
@@ -1285,6 +1327,11 @@ int cmd_rebase(int argc,
12851327
builtin_rebase_options,
12861328
builtin_rebase_usage, 0);
12871329

1330+
if (options.trailer_args.nr) {
1331+
validate_trailer_args_after_config(&options.trailer_args);
1332+
options.flags |= REBASE_FORCE;
1333+
}
1334+
12881335
if (preserve_merges_selected)
12891336
die(_("--preserve-merges was replaced by --rebase-merges\n"
12901337
"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
@@ -1542,6 +1589,9 @@ int cmd_rebase(int argc,
15421589
if (options.root && !options.onto_name)
15431590
imply_merge(&options, "--root without --onto");
15441591

1592+
if (options.trailer_args.nr)
1593+
imply_merge(&options, "--trailer");
1594+
15451595
if (isatty(2) && options.flags & REBASE_NO_QUIET)
15461596
strbuf_addstr(&options.git_format_patch_opt, " --progress");
15471597

builtin/tag.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,8 +499,7 @@ 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_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, parse_opt_strvec),
504503
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
505504
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
506505
OPT_CLEANUP(&cleanup_arg),

sequencer.c

Lines changed: 34 additions & 0 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
}
@@ -2033,6 +2035,10 @@ static int append_squash_message(struct strbuf *buf, const char *body,
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+
return error(_("unable to add trailers to commit message"));
2041+
20362042
if ((command == TODO_FIXUP) &&
20372043
(flag & TODO_REPLACE_FIXUP_MSG) &&
20382044
(file_exists(rebase_path_fixup_msg()) ||
@@ -2451,6 +2457,14 @@ static int do_pick_commit(struct repository *r,
24512457
if (opts->signoff && !is_fixup(command))
24522458
append_signoff(&ctx->message, 0, 0);
24532459

2460+
if (opts->trailer_args.nr && !is_fixup(command)) {
2461+
if (amend_strbuf_with_trailers(&ctx->message,
2462+
&opts->trailer_args)) {
2463+
res = error(_("unable to add trailers to commit message"));
2464+
goto leave;
2465+
}
2466+
}
2467+
24542468
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
24552469
res = -1;
24562470
else if (!opts->strategy ||
@@ -2525,6 +2539,7 @@ static int do_pick_commit(struct repository *r,
25252539
_("dropping %s %s -- patch contents already upstream\n"),
25262540
oid_to_hex(&commit->object.oid), msg.subject);
25272541
} /* else allow == 0 and there's nothing special to do */
2542+
25282543
if (!opts->no_commit && !drop_commit) {
25292544
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
25302545
res = do_commit(r, msg_file, author, reflog_action,
@@ -3242,6 +3257,17 @@ static int read_populate_opts(struct replay_opts *opts)
32423257

32433258
read_strategy_opts(opts, &buf);
32443259
strbuf_reset(&buf);
3260+
if (strbuf_read_file(&buf, rebase_path_trailer(), 0) >= 0) {
3261+
char *p = buf.buf, *nl;
3262+
3263+
while ((nl = strchr(p, '\n'))) {
3264+
*nl = '\0';
3265+
if (*p)
3266+
strvec_push(&opts->trailer_args, p);
3267+
p = nl + 1;
3268+
}
3269+
strbuf_reset(&buf);
3270+
}
32453271

32463272
if (read_oneliner(&ctx->current_fixups,
32473273
rebase_path_current_fixups(),
@@ -3336,6 +3362,14 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
33363362
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
33373363
else
33383364
write_file(rebase_path_no_reschedule_failed_exec(), "%s", "");
3365+
if (opts->trailer_args.nr) {
3366+
struct strbuf buf = STRBUF_INIT;
3367+
3368+
for (size_t i = 0; i < opts->trailer_args.nr; i++)
3369+
strbuf_addf(&buf, "%s\n", opts->trailer_args.v[i]);
3370+
write_file(rebase_path_trailer(), "%s", buf.buf);
3371+
strbuf_release(&buf);
3372+
}
33393373

33403374
return 0;
33413375
}

sequencer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct replay_opts {
4444
int record_origin;
4545
int no_commit;
4646
int signoff;
47+
struct strvec trailer_args;
4748
int allow_ff;
4849
int allow_rerere_auto;
4950
int allow_empty;
@@ -82,8 +83,9 @@ struct replay_opts {
8283
struct replay_ctx *ctx;
8384
};
8485
#define REPLAY_OPTS_INIT { \
85-
.edit = -1, \
8686
.action = -1, \
87+
.edit = -1, \
88+
.trailer_args = STRVEC_INIT, \
8789
.xopts = STRVEC_INIT, \
8890
.ctx = replay_ctx_new(), \
8991
}

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ integration_tests = [
385385
't3436-rebase-more-options.sh',
386386
't3437-rebase-fixup-options.sh',
387387
't3438-rebase-broken-files.sh',
388+
't3440-rebase-trailer.sh',
388389
't3450-history.sh',
389390
't3451-history-reword.sh',
390391
't3452-history-split.sh',

0 commit comments

Comments
 (0)