interpret-trailers: add option for in-place editing
Add a command line option --in-place to support in-place editing akin to sed -i. This allows to write commands like the following: git interpret-trailers --trailer "X: Y" a.txt > b.txt && mv b.txt a.txt in a more concise way: git interpret-trailers --trailer "X: Y" --in-place a.txt Signed-off-by: Tobias Klauser <tklauser@distanz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Tobias Klauser committed
Jan 14, 2016 at 17:57 UTC
e1f898639e906158fec26bdf3111d6f623288fa1
5 files changed
+114
-7
Documentation/git-interpret-trailers.txt
+23
-1
@@ -8,7 +8,7 @@ git-interpret-trailers - help add structured information into commit messages
8
SYNOPSIS
9
--------
10
[verse]
11
-'git interpret-trailers' [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
11
+'git interpret-trailers' [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
12
13
DESCRIPTION
14
-----------
@@ -64,6 +64,9 @@ folding rules, the encoding rules and probably many other rules.
64
65
OPTIONS
66
-------
67
+--in-place::
68
+ Edit the files in place.
69
+
70
--trim-empty::
71
If the <value> part of any trailer contains only whitespace,
72
the whole trailer will be removed from the resulting message.
@@ -216,6 +219,25 @@ Signed-off-by: Alice <alice@example.com>
219
Signed-off-by: Bob <bob@example.com>
220
------------
221
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 <bob@example.com>
231
+$ git interpret-trailers --trailer 'Acked-by: Alice <alice@example.com>' --in-place msg.txt
232
+$ cat msg.txt
233
+subject
234
+
235
+message
236
+
237
+Signed-off-by: Bob <bob@example.com>
238
+Acked-by: Alice <alice@example.com>
239
+------------
240
+
241
* Extract the last commit as a patch, and add a 'Cc' and a
242
'Reviewed-by' trailer to it:
243
+
builtin/interpret-trailers.c
+9
-4
@@ -12,16 +12,18 @@
12
#include "trailer.h"
13
14
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>...]"),
16
NULL
17
};
18
19
int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
20
{
21
+ int in_place = 0;
22
int trim_empty = 0;
23
struct string_list trailers = STRING_LIST_INIT_DUP;
24
25
struct option options[] = {
26
+ OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
27
OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
28
OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
29
N_("trailer(s) to add")),
@@ -34,9 +36,12 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
36
if (argc) {
37
int i;
38
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
+ }
45
46
string_list_clear(&trailers, 0);
47
t/t7513-interpret-trailers.sh
+40
@@ -326,6 +326,46 @@ test_expect_success 'with complex patch, args and --trim-empty' '
326
test_cmp expected actual
327
'
328
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
+
369
test_expect_success 'using "where = before"' '
370
git config trailer.bug.where "before" &&
371
cat complex_message_body >expected &&
trailer.c
+40
-1
@@ -2,6 +2,7 @@
2
#include "string-list.h"
3
#include "run-command.h"
4
#include "commit.h"
5
+#include "tempfile.h"
6
#include "trailer.h"
7
/*
8
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
@@ -843,7 +844,38 @@ static void free_all(struct trailer_item **first)
844
}
845
}
846
846
-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)
879
{
880
struct trailer_item *in_tok_first = NULL;
881
struct trailer_item *in_tok_last = NULL;
@@ -858,6 +890,9 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
890
891
lines = read_input_file(file);
892
893
+ if (in_place)
894
+ outfile = create_in_place_tempfile(file);
895
+
896
/* Print the lines before the trailers */
897
trailer_end = process_input_file(outfile, lines, &in_tok_first, &in_tok_last);
898
@@ -872,5 +907,9 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
907
/* Print the lines after the trailers as is */
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);
913
+
914
strbuf_list_free(lines);
915
}
trailer.h
+2
-1
@@ -1,6 +1,7 @@
1
#ifndef TRAILER_H
2
#define TRAILER_H
3
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);
6
7
#endif /* TRAILER_H */