Skip to content

Commit 4f3aa9d

Browse files
committed
Merge branch 'tk/interpret-trailers-in-place'
"interpret-trailers" has been taught to optionally update a file in place, instead of always writing the result to the standard output. * tk/interpret-trailers-in-place: interpret-trailers: add option for in-place editing trailer: allow to write to files other than stdout
2 parents 4b16573 + e1f8986 commit 4f3aa9d

File tree

5 files changed

+129
-20
lines changed

5 files changed

+129
-20
lines changed

Documentation/git-interpret-trailers.txt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ git-interpret-trailers - help add structured information into commit messages
88
SYNOPSIS
99
--------
1010
[verse]
11-
'git interpret-trailers' [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
11+
'git interpret-trailers' [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
1212

1313
DESCRIPTION
1414
-----------
@@ -64,6 +64,9 @@ folding rules, the encoding rules and probably many other rules.
6464

6565
OPTIONS
6666
-------
67+
--in-place::
68+
Edit the files in place.
69+
6770
--trim-empty::
6871
If the <value> part of any trailer contains only whitespace,
6972
the whole trailer will be removed from the resulting message.
@@ -216,6 +219,25 @@ Signed-off-by: Alice <[email protected]>
216219
Signed-off-by: Bob <[email protected]>
217220
------------
218221

222+
* Use the '--in-place' option to edit a message file in place:
223+
+
224+
------------
225+
$ cat msg.txt
226+
subject
227+
228+
message
229+
230+
Signed-off-by: Bob <[email protected]>
231+
$ git interpret-trailers --trailer 'Acked-by: Alice <[email protected]>' --in-place msg.txt
232+
$ cat msg.txt
233+
subject
234+
235+
message
236+
237+
Signed-off-by: Bob <[email protected]>
238+
Acked-by: Alice <[email protected]>
239+
------------
240+
219241
* Extract the last commit as a patch, and add a 'Cc' and a
220242
'Reviewed-by' trailer to it:
221243
+

builtin/interpret-trailers.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@
1212
#include "trailer.h"
1313

1414
static const char * const git_interpret_trailers_usage[] = {
15-
N_("git interpret-trailers [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
15+
N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
1616
NULL
1717
};
1818

1919
int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
2020
{
21+
int in_place = 0;
2122
int trim_empty = 0;
2223
struct string_list trailers = STRING_LIST_INIT_DUP;
2324

2425
struct option options[] = {
26+
OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
2527
OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
2628
OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
2729
N_("trailer(s) to add")),
@@ -34,9 +36,12 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
3436
if (argc) {
3537
int i;
3638
for (i = 0; i < argc; i++)
37-
process_trailers(argv[i], trim_empty, &trailers);
38-
} else
39-
process_trailers(NULL, trim_empty, &trailers);
39+
process_trailers(argv[i], in_place, trim_empty, &trailers);
40+
} else {
41+
if (in_place)
42+
die(_("no input file given for in-place editing"));
43+
process_trailers(NULL, in_place, trim_empty, &trailers);
44+
}
4045

4146
string_list_clear(&trailers, 0);
4247

t/t7513-interpret-trailers.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,46 @@ test_expect_success 'with complex patch, args and --trim-empty' '
326326
test_cmp expected actual
327327
'
328328

329+
test_expect_success 'in-place editing with basic patch' '
330+
cat basic_message >message &&
331+
cat basic_patch >>message &&
332+
cat basic_message >expected &&
333+
echo >>expected &&
334+
cat basic_patch >>expected &&
335+
git interpret-trailers --in-place message &&
336+
test_cmp expected message
337+
'
338+
339+
test_expect_success 'in-place editing with additional trailer' '
340+
cat basic_message >message &&
341+
cat basic_patch >>message &&
342+
cat basic_message >expected &&
343+
echo >>expected &&
344+
cat >>expected <<-\EOF &&
345+
Reviewed-by: Alice
346+
EOF
347+
cat basic_patch >>expected &&
348+
git interpret-trailers --trailer "Reviewed-by: Alice" --in-place message &&
349+
test_cmp expected message
350+
'
351+
352+
test_expect_success 'in-place editing on stdin disallowed' '
353+
test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place < basic_message
354+
'
355+
356+
test_expect_success 'in-place editing on non-existing file' '
357+
test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place nonexisting &&
358+
test_path_is_missing nonexisting
359+
'
360+
361+
test_expect_success POSIXPERM,SANITY "in-place editing doesn't clobber original file on error" '
362+
cat basic_message >message &&
363+
chmod -r message &&
364+
test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place message &&
365+
chmod +r message &&
366+
test_cmp message basic_message
367+
'
368+
329369
test_expect_success 'using "where = before"' '
330370
git config trailer.bug.where "before" &&
331371
cat complex_message_body >expected &&

trailer.c

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "string-list.h"
33
#include "run-command.h"
44
#include "commit.h"
5+
#include "tempfile.h"
56
#include "trailer.h"
67
/*
78
* Copyright (c) 2013, 2014 Christian Couder <[email protected]>
@@ -108,23 +109,23 @@ static char last_non_space_char(const char *s)
108109
return '\0';
109110
}
110111

111-
static void print_tok_val(const char *tok, const char *val)
112+
static void print_tok_val(FILE *outfile, const char *tok, const char *val)
112113
{
113114
char c = last_non_space_char(tok);
114115
if (!c)
115116
return;
116117
if (strchr(separators, c))
117-
printf("%s%s\n", tok, val);
118+
fprintf(outfile, "%s%s\n", tok, val);
118119
else
119-
printf("%s%c %s\n", tok, separators[0], val);
120+
fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
120121
}
121122

122-
static void print_all(struct trailer_item *first, int trim_empty)
123+
static void print_all(FILE *outfile, struct trailer_item *first, int trim_empty)
123124
{
124125
struct trailer_item *item;
125126
for (item = first; item; item = item->next) {
126127
if (!trim_empty || strlen(item->value) > 0)
127-
print_tok_val(item->token, item->value);
128+
print_tok_val(outfile, item->token, item->value);
128129
}
129130
}
130131

@@ -795,14 +796,15 @@ static int has_blank_line_before(struct strbuf **lines, int start)
795796
return 0;
796797
}
797798

798-
static void print_lines(struct strbuf **lines, int start, int end)
799+
static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
799800
{
800801
int i;
801802
for (i = start; lines[i] && i < end; i++)
802-
printf("%s", lines[i]->buf);
803+
fprintf(outfile, "%s", lines[i]->buf);
803804
}
804805

805-
static int process_input_file(struct strbuf **lines,
806+
static int process_input_file(FILE *outfile,
807+
struct strbuf **lines,
806808
struct trailer_item **in_tok_first,
807809
struct trailer_item **in_tok_last)
808810
{
@@ -818,10 +820,10 @@ static int process_input_file(struct strbuf **lines,
818820
trailer_start = find_trailer_start(lines, trailer_end);
819821

820822
/* Print lines before the trailers as is */
821-
print_lines(lines, 0, trailer_start);
823+
print_lines(outfile, lines, 0, trailer_start);
822824

823825
if (!has_blank_line_before(lines, trailer_start - 1))
824-
printf("\n");
826+
fprintf(outfile, "\n");
825827

826828
/* Parse trailer lines */
827829
for (i = trailer_start; i < trailer_end; i++) {
@@ -842,33 +844,72 @@ static void free_all(struct trailer_item **first)
842844
}
843845
}
844846

845-
void process_trailers(const char *file, int trim_empty, struct string_list *trailers)
847+
static struct tempfile trailers_tempfile;
848+
849+
static FILE *create_in_place_tempfile(const char *file)
850+
{
851+
struct stat st;
852+
struct strbuf template = STRBUF_INIT;
853+
const char *tail;
854+
FILE *outfile;
855+
856+
if (stat(file, &st))
857+
die_errno(_("could not stat %s"), file);
858+
if (!S_ISREG(st.st_mode))
859+
die(_("file %s is not a regular file"), file);
860+
if (!(st.st_mode & S_IWUSR))
861+
die(_("file %s is not writable by user"), file);
862+
863+
/* Create temporary file in the same directory as the original */
864+
tail = strrchr(file, '/');
865+
if (tail != NULL)
866+
strbuf_add(&template, file, tail - file + 1);
867+
strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
868+
869+
xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
870+
strbuf_release(&template);
871+
outfile = fdopen_tempfile(&trailers_tempfile, "w");
872+
if (!outfile)
873+
die_errno(_("could not open temporary file"));
874+
875+
return outfile;
876+
}
877+
878+
void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
846879
{
847880
struct trailer_item *in_tok_first = NULL;
848881
struct trailer_item *in_tok_last = NULL;
849882
struct trailer_item *arg_tok_first;
850883
struct strbuf **lines;
851884
int trailer_end;
885+
FILE *outfile = stdout;
852886

853887
/* Default config must be setup first */
854888
git_config(git_trailer_default_config, NULL);
855889
git_config(git_trailer_config, NULL);
856890

857891
lines = read_input_file(file);
858892

893+
if (in_place)
894+
outfile = create_in_place_tempfile(file);
895+
859896
/* Print the lines before the trailers */
860-
trailer_end = process_input_file(lines, &in_tok_first, &in_tok_last);
897+
trailer_end = process_input_file(outfile, lines, &in_tok_first, &in_tok_last);
861898

862899
arg_tok_first = process_command_line_args(trailers);
863900

864901
process_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);
865902

866-
print_all(in_tok_first, trim_empty);
903+
print_all(outfile, in_tok_first, trim_empty);
867904

868905
free_all(&in_tok_first);
869906

870907
/* Print the lines after the trailers as is */
871-
print_lines(lines, trailer_end, INT_MAX);
908+
print_lines(outfile, lines, trailer_end, INT_MAX);
909+
910+
if (in_place)
911+
if (rename_tempfile(&trailers_tempfile, file))
912+
die_errno(_("could not rename temporary file to %s"), file);
872913

873914
strbuf_list_free(lines);
874915
}

trailer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef TRAILER_H
22
#define TRAILER_H
33

4-
void process_trailers(const char *file, int trim_empty, struct string_list *trailers);
4+
void process_trailers(const char *file, int in_place, int trim_empty,
5+
struct string_list *trailers);
56

67
#endif /* TRAILER_H */

0 commit comments

Comments
 (0)