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
This commit is contained in:
commit
4f3aa9da70
@ -8,7 +8,7 @@ git-interpret-trailers - help add structured information into commit messages
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git interpret-trailers' [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
|
||||
'git interpret-trailers' [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -64,6 +64,9 @@ folding rules, the encoding rules and probably many other rules.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--in-place::
|
||||
Edit the files in place.
|
||||
|
||||
--trim-empty::
|
||||
If the <value> part of any trailer contains only whitespace,
|
||||
the whole trailer will be removed from the resulting message.
|
||||
@ -216,6 +219,25 @@ Signed-off-by: Alice <alice@example.com>
|
||||
Signed-off-by: Bob <bob@example.com>
|
||||
------------
|
||||
|
||||
* Use the '--in-place' option to edit a message file in place:
|
||||
+
|
||||
------------
|
||||
$ cat msg.txt
|
||||
subject
|
||||
|
||||
message
|
||||
|
||||
Signed-off-by: Bob <bob@example.com>
|
||||
$ git interpret-trailers --trailer 'Acked-by: Alice <alice@example.com>' --in-place msg.txt
|
||||
$ cat msg.txt
|
||||
subject
|
||||
|
||||
message
|
||||
|
||||
Signed-off-by: Bob <bob@example.com>
|
||||
Acked-by: Alice <alice@example.com>
|
||||
------------
|
||||
|
||||
* Extract the last commit as a patch, and add a 'Cc' and a
|
||||
'Reviewed-by' trailer to it:
|
||||
+
|
||||
|
@ -12,16 +12,18 @@
|
||||
#include "trailer.h"
|
||||
|
||||
static const char * const git_interpret_trailers_usage[] = {
|
||||
N_("git interpret-trailers [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
|
||||
N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int in_place = 0;
|
||||
int trim_empty = 0;
|
||||
struct string_list trailers = STRING_LIST_INIT_DUP;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
|
||||
OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
|
||||
OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
|
||||
N_("trailer(s) to add")),
|
||||
@ -34,9 +36,12 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
|
||||
if (argc) {
|
||||
int i;
|
||||
for (i = 0; i < argc; i++)
|
||||
process_trailers(argv[i], trim_empty, &trailers);
|
||||
} else
|
||||
process_trailers(NULL, trim_empty, &trailers);
|
||||
process_trailers(argv[i], in_place, trim_empty, &trailers);
|
||||
} else {
|
||||
if (in_place)
|
||||
die(_("no input file given for in-place editing"));
|
||||
process_trailers(NULL, in_place, trim_empty, &trailers);
|
||||
}
|
||||
|
||||
string_list_clear(&trailers, 0);
|
||||
|
||||
|
@ -326,6 +326,46 @@ test_expect_success 'with complex patch, args and --trim-empty' '
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'in-place editing with basic patch' '
|
||||
cat basic_message >message &&
|
||||
cat basic_patch >>message &&
|
||||
cat basic_message >expected &&
|
||||
echo >>expected &&
|
||||
cat basic_patch >>expected &&
|
||||
git interpret-trailers --in-place message &&
|
||||
test_cmp expected message
|
||||
'
|
||||
|
||||
test_expect_success 'in-place editing with additional trailer' '
|
||||
cat basic_message >message &&
|
||||
cat basic_patch >>message &&
|
||||
cat basic_message >expected &&
|
||||
echo >>expected &&
|
||||
cat >>expected <<-\EOF &&
|
||||
Reviewed-by: Alice
|
||||
EOF
|
||||
cat basic_patch >>expected &&
|
||||
git interpret-trailers --trailer "Reviewed-by: Alice" --in-place message &&
|
||||
test_cmp expected message
|
||||
'
|
||||
|
||||
test_expect_success 'in-place editing on stdin disallowed' '
|
||||
test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place < basic_message
|
||||
'
|
||||
|
||||
test_expect_success 'in-place editing on non-existing file' '
|
||||
test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place nonexisting &&
|
||||
test_path_is_missing nonexisting
|
||||
'
|
||||
|
||||
test_expect_success POSIXPERM,SANITY "in-place editing doesn't clobber original file on error" '
|
||||
cat basic_message >message &&
|
||||
chmod -r message &&
|
||||
test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place message &&
|
||||
chmod +r message &&
|
||||
test_cmp message basic_message
|
||||
'
|
||||
|
||||
test_expect_success 'using "where = before"' '
|
||||
git config trailer.bug.where "before" &&
|
||||
cat complex_message_body >expected &&
|
||||
|
69
trailer.c
69
trailer.c
@ -2,6 +2,7 @@
|
||||
#include "string-list.h"
|
||||
#include "run-command.h"
|
||||
#include "commit.h"
|
||||
#include "tempfile.h"
|
||||
#include "trailer.h"
|
||||
/*
|
||||
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
|
||||
@ -108,23 +109,23 @@ static char last_non_space_char(const char *s)
|
||||
return '\0';
|
||||
}
|
||||
|
||||
static void print_tok_val(const char *tok, const char *val)
|
||||
static void print_tok_val(FILE *outfile, const char *tok, const char *val)
|
||||
{
|
||||
char c = last_non_space_char(tok);
|
||||
if (!c)
|
||||
return;
|
||||
if (strchr(separators, c))
|
||||
printf("%s%s\n", tok, val);
|
||||
fprintf(outfile, "%s%s\n", tok, val);
|
||||
else
|
||||
printf("%s%c %s\n", tok, separators[0], val);
|
||||
fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
|
||||
}
|
||||
|
||||
static void print_all(struct trailer_item *first, int trim_empty)
|
||||
static void print_all(FILE *outfile, struct trailer_item *first, int trim_empty)
|
||||
{
|
||||
struct trailer_item *item;
|
||||
for (item = first; item; item = item->next) {
|
||||
if (!trim_empty || strlen(item->value) > 0)
|
||||
print_tok_val(item->token, item->value);
|
||||
print_tok_val(outfile, item->token, item->value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -795,14 +796,15 @@ static int has_blank_line_before(struct strbuf **lines, int start)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_lines(struct strbuf **lines, int start, int end)
|
||||
static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
|
||||
{
|
||||
int i;
|
||||
for (i = start; lines[i] && i < end; i++)
|
||||
printf("%s", lines[i]->buf);
|
||||
fprintf(outfile, "%s", lines[i]->buf);
|
||||
}
|
||||
|
||||
static int process_input_file(struct strbuf **lines,
|
||||
static int process_input_file(FILE *outfile,
|
||||
struct strbuf **lines,
|
||||
struct trailer_item **in_tok_first,
|
||||
struct trailer_item **in_tok_last)
|
||||
{
|
||||
@ -818,10 +820,10 @@ static int process_input_file(struct strbuf **lines,
|
||||
trailer_start = find_trailer_start(lines, trailer_end);
|
||||
|
||||
/* Print lines before the trailers as is */
|
||||
print_lines(lines, 0, trailer_start);
|
||||
print_lines(outfile, lines, 0, trailer_start);
|
||||
|
||||
if (!has_blank_line_before(lines, trailer_start - 1))
|
||||
printf("\n");
|
||||
fprintf(outfile, "\n");
|
||||
|
||||
/* Parse trailer lines */
|
||||
for (i = trailer_start; i < trailer_end; i++) {
|
||||
@ -842,13 +844,45 @@ static void free_all(struct trailer_item **first)
|
||||
}
|
||||
}
|
||||
|
||||
void process_trailers(const char *file, int trim_empty, struct string_list *trailers)
|
||||
static struct tempfile trailers_tempfile;
|
||||
|
||||
static FILE *create_in_place_tempfile(const char *file)
|
||||
{
|
||||
struct stat st;
|
||||
struct strbuf template = STRBUF_INIT;
|
||||
const char *tail;
|
||||
FILE *outfile;
|
||||
|
||||
if (stat(file, &st))
|
||||
die_errno(_("could not stat %s"), file);
|
||||
if (!S_ISREG(st.st_mode))
|
||||
die(_("file %s is not a regular file"), file);
|
||||
if (!(st.st_mode & S_IWUSR))
|
||||
die(_("file %s is not writable by user"), file);
|
||||
|
||||
/* Create temporary file in the same directory as the original */
|
||||
tail = strrchr(file, '/');
|
||||
if (tail != NULL)
|
||||
strbuf_add(&template, file, tail - file + 1);
|
||||
strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
|
||||
|
||||
xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
|
||||
strbuf_release(&template);
|
||||
outfile = fdopen_tempfile(&trailers_tempfile, "w");
|
||||
if (!outfile)
|
||||
die_errno(_("could not open temporary file"));
|
||||
|
||||
return outfile;
|
||||
}
|
||||
|
||||
void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
|
||||
{
|
||||
struct trailer_item *in_tok_first = NULL;
|
||||
struct trailer_item *in_tok_last = NULL;
|
||||
struct trailer_item *arg_tok_first;
|
||||
struct strbuf **lines;
|
||||
int trailer_end;
|
||||
FILE *outfile = stdout;
|
||||
|
||||
/* Default config must be setup first */
|
||||
git_config(git_trailer_default_config, NULL);
|
||||
@ -856,19 +890,26 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
|
||||
|
||||
lines = read_input_file(file);
|
||||
|
||||
if (in_place)
|
||||
outfile = create_in_place_tempfile(file);
|
||||
|
||||
/* Print the lines before the trailers */
|
||||
trailer_end = process_input_file(lines, &in_tok_first, &in_tok_last);
|
||||
trailer_end = process_input_file(outfile, lines, &in_tok_first, &in_tok_last);
|
||||
|
||||
arg_tok_first = process_command_line_args(trailers);
|
||||
|
||||
process_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);
|
||||
|
||||
print_all(in_tok_first, trim_empty);
|
||||
print_all(outfile, in_tok_first, trim_empty);
|
||||
|
||||
free_all(&in_tok_first);
|
||||
|
||||
/* Print the lines after the trailers as is */
|
||||
print_lines(lines, trailer_end, INT_MAX);
|
||||
print_lines(outfile, lines, trailer_end, INT_MAX);
|
||||
|
||||
if (in_place)
|
||||
if (rename_tempfile(&trailers_tempfile, file))
|
||||
die_errno(_("could not rename temporary file to %s"), file);
|
||||
|
||||
strbuf_list_free(lines);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user