From: Thomas Guyot-Sionnest <tguyot@gmail.com>
To: git@vger.kernel.org
Cc: dermoth@aei.ca, Thomas Guyot-Sionnest <tguyot@gmail.com>
Subject: [PATCH 2/2] Allow passing pipes for input pipes to diff --no-index
Date: Fri, 18 Sep 2020 07:32:56 -0400 [thread overview]
Message-ID: <20200918113256.8699-3-tguyot@gmail.com> (raw)
In-Reply-To: <20200918113256.8699-1-tguyot@gmail.com>
A very handy way to pass data to applications is to use the <() process
substitution syntax in bash variants. It allow comparing files streamed
from a remote server or doing on-the-fly stream processing to alter the
diff. These are usually implemented as a symlink that points to a bogus
name (ex "pipe:[209326419]") but opens as a pipe.
Git normally tracks symlinks targets. This patch makes it detect such
pipes in --no-index mode and read the file normally like it would do for
stdin ("-"), so they can be compared directly.
Signed-off-by: Thomas Guyot-Sionnest <tguyot@gmail.com>
---
diff-no-index.c | 56 ++++++++++--
t/t4053-diff-no-index.sh | 189 +++++++++++++++++++++++++++++++++++++++
2 files changed, 238 insertions(+), 7 deletions(-)
diff --git a/diff-no-index.c b/diff-no-index.c
index 7814eabfe0..779c686d23 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -41,6 +41,33 @@ static int read_directory_contents(const char *path, struct string_list *list)
*/
static const char file_from_standard_input[] = "-";
+/* Check that file is - (STDIN) or unnamed pipe - explicitly
+ * avoid on-disk named pipes which could block
+ */
+static int ispipe(const char *name)
+{
+ struct stat st;
+
+ if (name == file_from_standard_input)
+ return 1; /* STDIN */
+
+ if (!lstat(name, &st)) {
+ if (S_ISLNK(st.st_mode)) {
+ /* symlink - read it and check it doesn't exists
+ * as a file yet link to a pipe */
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_realpath(&sb, name, 0);
+ /* We're abusing strbuf_realpath here, it may append
+ * pipe:[NNNNNNNNN] to an abs path */
+ if (!stat(sb.buf, &st))
+ return 0; /* link target exists , not pipe */
+ if (!stat(name, &st))
+ return S_ISFIFO(st.st_mode);
+ }
+ }
+ return 0;
+}
+
static int get_mode(const char *path, int *mode)
{
struct stat st;
@@ -51,7 +78,7 @@ static int get_mode(const char *path, int *mode)
else if (!strcasecmp(path, "nul"))
*mode = 0;
#endif
- else if (path == file_from_standard_input)
+ else if (ispipe(path))
*mode = create_ce_mode(0666);
else if (lstat(path, &st))
return error("Could not access '%s'", path);
@@ -60,13 +87,13 @@ static int get_mode(const char *path, int *mode)
return 0;
}
-static int populate_from_stdin(struct diff_filespec *s)
+static int populate_from_fd(struct diff_filespec *s, int fd)
{
struct strbuf buf = STRBUF_INIT;
size_t size = 0;
- if (strbuf_read(&buf, 0, 0) < 0)
- return error_errno("error while reading from stdin");
+ if (strbuf_read(&buf, fd, 0) < 0)
+ return error_errno(_("error while reading from fd %i"), fd);
s->should_munmap = 0;
s->data = strbuf_detach(&buf, &size);
@@ -76,6 +103,20 @@ static int populate_from_stdin(struct diff_filespec *s)
return 0;
}
+static int populate_from_pipe(struct diff_filespec *s, const char *name)
+{
+ int ret;
+ FILE *f;
+
+ f = fopen(name, "r");
+ if (!f)
+ return error_errno(_("cannot open %s"), name);
+
+ ret = populate_from_fd(s, fileno(f));
+ fclose(f);
+ return ret;
+}
+
static struct diff_filespec *noindex_filespec(const char *name, int mode)
{
struct diff_filespec *s;
@@ -85,7 +126,9 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode)
s = alloc_filespec(name);
fill_filespec(s, &null_oid, 0, mode);
if (name == file_from_standard_input)
- populate_from_stdin(s);
+ populate_from_fd(s, 0);
+ else if (ispipe(name))
+ populate_from_pipe(s, name);
return s;
}
@@ -218,8 +261,7 @@ static void fixup_paths(const char **path, struct strbuf *replacement)
{
unsigned int isdir0, isdir1;
- if (path[0] == file_from_standard_input ||
- path[1] == file_from_standard_input)
+ if (ispipe(path[0]) || ispipe(path[1]))
return;
isdir0 = is_directory(path[0]);
isdir1 = is_directory(path[1]);
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 0168946b63..e49f773515 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -144,4 +144,193 @@ test_expect_success 'diff --no-index allows external diff' '
test_cmp expect actual
'
+test_expect_success 'diff --no-index can diff piped subshells' '
+ echo 1 >non/git/c &&
+ test_expect_code 0 git diff --no-index non/git/b <(cat non/git/c) &&
+ test_expect_code 0 git diff --no-index <(cat non/git/b) non/git/c &&
+ test_expect_code 0 git diff --no-index <(cat non/git/b) <(cat non/git/c) &&
+ test_expect_code 0 cat non/git/b | git diff --no-index - non/git/c &&
+ test_expect_code 0 cat non/git/c | git diff --no-index non/git/b - &&
+ test_expect_code 0 cat non/git/b | git diff --no-index - <(cat non/git/c) &&
+ test_expect_code 0 cat non/git/c | git diff --no-index <(cat non/git/b) -
+'
+
+test_expect_success 'diff --no-index finds diff in piped subshells' '
+ (
+ set -- <(cat /dev/null) <(cat /dev/null)
+ cat <<-EOF >expect
+ diff --git a$1 b$2
+ --- a$1
+ +++ b$2
+ @@ -1 +1 @@
+ -1
+ +2
+ EOF
+ ) &&
+ test_expect_code 1 \
+ git diff --no-index <(cat non/git/b) <(sed s/1/2/ non/git/c) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff --no-index with stat and numstat' '
+ (
+ set -- <(cat /dev/null) <(cat /dev/null)
+ min=$((${#1} < ${#2} ? ${#1} : ${#2}))
+ for ((i=0; i<min; i++)); do [ "${1:i:1}" = "${2:i:1}" ] || break; done
+ base=${1:0:i-1}
+ cat <<-EOF >expect1
+ $base{${1#$base} => ${2#$base}} | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ EOF
+ cat <<-EOF >expect2
+ 1 1 $base{${1#$base} => ${2#$base}}
+ EOF
+ ) &&
+ test_expect_code 1 \
+ git diff --no-index --stat <(cat non/git/a) <(sed s/1/2/ non/git/b) >actual &&
+ test_cmp expect1 actual &&
+ test_expect_code 1 \
+ git diff --no-index --numstat <(cat non/git/a) <(sed s/1/2/ non/git/b) >actual &&
+ test_cmp expect2 actual
+'
+
+test_expect_success PIPE 'diff --no-index on filesystem pipes' '
+ (
+ cd non/git &&
+ mkdir f g &&
+ mkfifo f/1 g/1 &&
+ test_expect_code 128 git diff --no-index f g &&
+ test_expect_code 128 git diff --no-index ../../a f &&
+ test_expect_code 128 git diff --no-index g ../../a &&
+ test_expect_code 128 git diff --no-index f/1 g/1 &&
+ test_expect_code 128 git diff --no-index f/1 ../../a/1 &&
+ test_expect_code 128 git diff --no-index ../../a/1 g/1
+ )
+'
+
+test_expect_success PIPE 'diff --no-index reads symlinks to named pipes as symlinks' '
+ (
+ cd non/git &&
+ mkdir h i &&
+ ln -s ../f/1 h/1 &&
+ ln -s ../g/1 i/1 &&
+ test_expect_code 1 git diff --no-index h i >actual &&
+ cat <<-EOF >expect &&
+ diff --git a/h/1 b/i/1
+ index d0b5850..d8b9c34 120000
+ --- a/h/1
+ +++ b/i/1
+ @@ -1 +1 @@
+ -../f/1
+ \ No newline at end of file
+ +../g/1
+ \ No newline at end of file
+ EOF
+ test_cmp expect actual &&
+ test_expect_code 1 git diff --no-index ../../a h >actual &&
+ cat <<-EOF >expect &&
+ diff --git a/../../a/1 b/../../a/1
+ deleted file mode 100644
+ index d00491f..0000000
+ --- a/../../a/1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -1
+ diff --git a/h/1 b/h/1
+ new file mode 120000
+ index 0000000..d0b5850
+ --- /dev/null
+ +++ b/h/1
+ @@ -0,0 +1 @@
+ +../f/1
+ \ No newline at end of file
+ diff --git a/../../a/2 b/../../a/2
+ deleted file mode 100644
+ index 0cfbf08..0000000
+ --- a/../../a/2
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -2
+ EOF
+ test_cmp expect actual &&
+ test_expect_code 1 git diff --no-index i ../../a >actual &&
+ cat <<-EOF >expect &&
+ diff --git a/i/1 b/i/1
+ deleted file mode 120000
+ index d8b9c34..0000000
+ --- a/i/1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -../g/1
+ \ No newline at end of file
+ diff --git a/../../a/1 b/../../a/1
+ new file mode 100644
+ index 0000000..d00491f
+ --- /dev/null
+ +++ b/../../a/1
+ @@ -0,0 +1 @@
+ +1
+ diff --git a/../../a/2 b/../../a/2
+ new file mode 100644
+ index 0000000..0cfbf08
+ --- /dev/null
+ +++ b/../../a/2
+ @@ -0,0 +1 @@
+ +2
+ EOF
+ test_cmp expect actual &&
+ test_expect_code 1 git diff --no-index h/1 i/1 >actual &&
+ cat <<-EOF >expect &&
+ diff --git a/h/1 b/i/1
+ index d0b5850..d8b9c34 120000
+ --- a/h/1
+ +++ b/i/1
+ @@ -1 +1 @@
+ -../f/1
+ \ No newline at end of file
+ +../g/1
+ \ No newline at end of file
+ EOF
+ test_cmp expect actual &&
+ test_expect_code 1 git diff --no-index h/1 ../../a/1 >actual &&
+ cat <<-EOF >expect &&
+ diff --git a/h/1 b/h/1
+ deleted file mode 120000
+ index d0b5850..0000000
+ --- a/h/1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -../f/1
+ \ No newline at end of file
+ diff --git a/../../a/1 b/../../a/1
+ new file mode 100644
+ index 0000000..d00491f
+ --- /dev/null
+ +++ b/../../a/1
+ @@ -0,0 +1 @@
+ +1
+ EOF
+ test_cmp expect actual &&
+ test_expect_code 1 git diff --no-index ../../a/1 i/1 >actual &&
+ cat <<-EOF >expect &&
+ diff --git a/../../a/1 b/../../a/1
+ deleted file mode 100644
+ index d00491f..0000000
+ --- a/../../a/1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -1
+ diff --git a/i/1 b/i/1
+ new file mode 120000
+ index 0000000..d8b9c34
+ --- /dev/null
+ +++ b/i/1
+ @@ -0,0 +1 @@
+ +../g/1
+ \ No newline at end of file
+ EOF
+ test_cmp expect actual
+ )
+'
+
test_done
--
2.20.1
next prev parent reply other threads:[~2020-09-18 11:39 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-09-18 11:32 Allow passing pipes to diff --no-index + bugfix Thomas Guyot-Sionnest
2020-09-18 11:32 ` [PATCH 1/2] diff: Fix modified lines stats with --stat and --numstat Thomas Guyot-Sionnest
2020-09-18 14:46 ` Taylor Blau
2020-09-18 15:10 ` Thomas Guyot-Sionnest
2020-09-18 17:37 ` Jeff King
2020-09-18 18:00 ` Thomas Guyot-Sionnest
2020-09-20 4:53 ` Thomas Guyot
2020-09-18 17:27 ` Jeff King
2020-09-18 17:52 ` Thomas Guyot-Sionnest
2020-09-18 18:06 ` Junio C Hamano
2020-09-23 19:16 ` Johannes Schindelin
2020-09-23 19:23 ` Junio C Hamano
2020-09-23 20:44 ` Johannes Schindelin
2020-09-24 4:49 ` Thomas Guyot
2020-09-24 5:24 ` [PATCH v3] " Thomas Guyot-Sionnest
2020-09-24 7:41 ` [PATCH v4] " Thomas Guyot-Sionnest
2020-09-24 6:40 ` [PATCH 1/2] " Junio C Hamano
2020-09-24 7:13 ` Thomas Guyot
2020-09-24 17:19 ` Junio C Hamano
2020-09-24 17:38 ` Junio C Hamano
2020-09-23 15:05 ` Johannes Schindelin
2020-09-20 13:09 ` [PATCH v2] " Thomas Guyot-Sionnest
2020-09-20 15:39 ` Taylor Blau
2020-09-20 16:38 ` Thomas Guyot
2020-09-20 19:11 ` Junio C Hamano
2020-09-20 20:08 ` Junio C Hamano
2020-09-20 20:36 ` Junio C Hamano
2020-09-20 22:15 ` Junio C Hamano
2020-09-21 19:26 ` Jeff King
2020-09-21 21:51 ` Junio C Hamano
2020-09-21 22:20 ` Jeff King
2020-09-21 22:37 ` Junio C Hamano
2020-09-18 11:32 ` Thomas Guyot-Sionnest [this message]
2020-09-18 14:36 ` [PATCH 2/2] Allow passing pipes for input pipes to diff --no-index Taylor Blau
2020-09-18 16:34 ` Thomas Guyot-Sionnest
2020-09-18 17:19 ` Jeff King
2020-09-18 17:21 ` Jeff King
2020-09-18 17:39 ` Thomas Guyot-Sionnest
2020-09-18 17:48 ` Junio C Hamano
2020-09-18 18:02 ` Jeff King
2020-09-20 12:54 ` Thomas Guyot
2020-09-21 19:31 ` Jeff King
2020-09-21 20:14 ` Junio C Hamano
2020-09-18 17:58 ` Taylor Blau
2020-09-18 18:05 ` Jeff King
2020-09-18 17:20 ` Jeff King
2020-09-18 18:00 ` Taylor Blau
2020-09-18 21:56 ` brian m. carlson
2020-09-18 17:51 ` Allow passing pipes to diff --no-index + bugfix Junio C Hamano
2020-09-18 18:24 ` Thomas Guyot-Sionnest
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
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200918113256.8699-3-tguyot@gmail.com \
--to=tguyot@gmail.com \
--cc=dermoth@aei.ca \
--cc=git@vger.kernel.org \
/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.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).