Commit e1f89863 authored by Tobias Klauser's avatar Tobias Klauser Committed by Junio C Hamano

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: default avatarTobias Klauser <tklauser@distanz.ch>
Signed-off-by: default avatarJunio C Hamano <gitster@pobox.com>
parent d0d2344a
......@@ -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 &&
......
......@@ -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>
......@@ -843,7 +844,38 @@ 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;
......@@ -858,6 +890,9 @@ 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(outfile, lines, &in_tok_first, &in_tok_last);
......@@ -872,5 +907,9 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
/* Print the lines after the trailers as is */
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);
}
#ifndef TRAILER_H
#define TRAILER_H
void process_trailers(const char *file, int trim_empty, struct string_list *trailers);
void process_trailers(const char *file, int in_place, int trim_empty,
struct string_list *trailers);
#endif /* TRAILER_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment