All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tobias Klauser <tklauser@distanz.ch>
To: Junio C Hamano <gitster@pobox.com>,
	Christian Couder <chriscool@tuxfamily.org>
Cc: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>,
	Eric Sunshine <sunshine@sunshineco.com>,
	git@vger.kernel.org
Subject: [PATCH v4 2/2] interpret-trailers: add option for in-place editing
Date: Thu, 14 Jan 2016 17:57:55 +0100	[thread overview]
Message-ID: <1452790676-11937-3-git-send-email-tklauser@distanz.ch> (raw)
In-Reply-To: <1452790676-11937-1-git-send-email-tklauser@distanz.ch>

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>
---
 Documentation/git-interpret-trailers.txt | 24 ++++++++++++++++++-
 builtin/interpret-trailers.c             | 13 ++++++----
 t/t7513-interpret-trailers.sh            | 40 +++++++++++++++++++++++++++++++
 trailer.c                                | 41 +++++++++++++++++++++++++++++++-
 trailer.h                                |  3 ++-
 5 files changed, 114 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 0ecd497c4de7..a77b901f1d7b 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -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:
 +
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 46838d24a90a..b99ae4be8875 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -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);
 
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 322c436a494c..aee785cffa8d 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -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 &&
diff --git a/trailer.c b/trailer.c
index 176fac213450..94b387b49971 100644
--- a/trailer.c
+++ b/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>
@@ -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);
 }
diff --git a/trailer.h b/trailer.h
index 8eb25d565e28..36b40b81761f 100644
--- a/trailer.h
+++ b/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 */
-- 
2.7.0.1.g5e091f5

  parent reply	other threads:[~2016-01-14 16:58 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-14 16:57 [PATCH v4 0/2] Add in-place editing support to git interpret-trailers Tobias Klauser
2016-01-14 16:57 ` [PATCH v4 1/2] trailer: allow to write to files other than stdout Tobias Klauser
2016-01-14 16:57 ` Tobias Klauser [this message]
2016-01-14 20:45   ` [PATCH v4 2/2] interpret-trailers: add option for in-place editing Junio C Hamano
2016-01-15 10:34     ` Tobias Klauser
2016-01-15 17:24       ` Junio C Hamano
2016-01-15 17:45         ` Tobias Klauser
2016-01-18 21:11     ` Eric Sunshine
     [not found]       ` <CAPc5daWpnReWJzeTJjvZap78H0oZKG-YGEP19Neusyahu5A6cQ@mail.gmail.com>
2016-01-18 22:13         ` Eric Sunshine
2016-01-19  8:28           ` Tobias Klauser
2016-01-19 17:52       ` Junio C Hamano
2016-01-19 17:56         ` Eric Sunshine
2016-01-19 18:10           ` Eric Sunshine
2016-01-19 20:58             ` Junio C Hamano
2016-01-19 21:45               ` Eric Sunshine
2016-01-19 22:09                 ` Junio C Hamano
2016-01-20  0:20                   ` Eric Sunshine

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1452790676-11937-3-git-send-email-tklauser@distanz.ch \
    --to=tklauser@distanz.ch \
    --cc=Matthieu.Moy@grenoble-inp.fr \
    --cc=chriscool@tuxfamily.org \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=sunshine@sunshineco.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.