Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Tobias Klauser authored and Junio C Hamano committed Jan 14, 2016
1 parent d0d2344 commit e1f8986
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 7 deletions.
24 changes: 23 additions & 1 deletion Documentation/git-interpret-trailers.txt
Expand Up @@ -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
-----------
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
+
Expand Down
13 changes: 9 additions & 4 deletions builtin/interpret-trailers.c
Expand Up @@ -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")),
Expand All @@ -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);

Expand Down
40 changes: 40 additions & 0 deletions t/t7513-interpret-trailers.sh
Expand Up @@ -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 &&
Expand Down
41 changes: 40 additions & 1 deletion trailer.c
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -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);
}
3 changes: 2 additions & 1 deletion trailer.h
@@ -1,6 +1,7 @@
#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 */

0 comments on commit e1f8986

Please sign in to comment.