From patchwork Fri Sep 18 11:32:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Guyot X-Patchwork-Id: 11784741 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3AA39139A for ; Fri, 18 Sep 2020 11:39:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 21E0D2311D for ; Fri, 18 Sep 2020 11:39:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726544AbgIRLjq (ORCPT ); Fri, 18 Sep 2020 07:39:46 -0400 Received: from mail001-2.aei.ca ([206.123.6.133]:36793 "EHLO mail001.aei.ca" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726533AbgIRLjq (ORCPT ); Fri, 18 Sep 2020 07:39:46 -0400 Received: (qmail 32117 invoked by uid 89); 18 Sep 2020 11:33:04 -0000 Received: by simscan 1.2.0 ppid: 32103, pid: 32115, t: 0.0181s scanners: regex: 1.2.0 attach: 1.2.0 Received: from mail002.aei.ca (HELO mta.aei.ca) (206.123.6.132) by mail001.aei.ca with (DHE-RSA-AES256-SHA encrypted) SMTP; 18 Sep 2020 11:33:04 -0000 Received: (qmail 1901 invoked by uid 89); 18 Sep 2020 11:33:04 -0000 Received: by simscan 1.2.0 ppid: 1889, pid: 1893, t: 0.5454s scanners: regex: 1.2.0 attach: 1.2.0 clamav: 0.97.8/m: spam: 3.3.1 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) X-Spam-Level: X-Spam-Status: No, hits=-3.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, FREEMAIL_FROM autolearn=ham autolearn_force=no version=3.4.1 Received: from dsl-216-221-52-235.mtl.contact.net (HELO dermoth.lan) (216.221.52.235) by mail.aei.ca with (AES256-SHA encrypted) SMTP; 18 Sep 2020 11:33:04 -0000 Received: from dermoth by dermoth.lan with local (Exim 4.92) (envelope-from ) id 1kJEdS-0004YC-TW; Fri, 18 Sep 2020 07:33:02 -0400 From: Thomas Guyot-Sionnest To: git@vger.kernel.org Cc: dermoth@aei.ca, Thomas Guyot-Sionnest Subject: [PATCH 1/2] diff: Fix modified lines stats with --stat and --numstat Date: Fri, 18 Sep 2020 07:32:55 -0400 Message-Id: <20200918113256.8699-2-tguyot@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200918113256.8699-1-tguyot@gmail.com> References: <20200918113256.8699-1-tguyot@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org In builtin_diffstat(), when both files are coming from "stdin" (which could be better described as the file's content being written directly into the file object), oideq() compares two null hashes and ignores the actual differences for the statistics. This patch checks if is_stdin flag is set on both sides and compare contents directly. Signed-off-by: Thomas Guyot-Sionnest --- diff.c | 5 ++++- t/t3206-range-diff.sh | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/diff.c b/diff.c index a5114fa864..2995527896 100644 --- a/diff.c +++ b/diff.c @@ -3681,7 +3681,10 @@ static void builtin_diffstat(const char *name_a, const char *name_b, return; } - same_contents = oideq(&one->oid, &two->oid); + if (one->is_stdin && two->is_stdin) + same_contents = !strcmp(one->data, two->data); + else + same_contents = oideq(&one->oid, &two->oid); if (diff_filespec_is_binary(o->repo, one) || diff_filespec_is_binary(o->repo, two)) { diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index e024cff65c..4715e75b68 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh @@ -258,11 +258,11 @@ test_expect_success 'changed commit with --stat diff option' ' a => b | 0 1 file changed, 0 insertions(+), 0 deletions(-) 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/ - a => b | 0 - 1 file changed, 0 insertions(+), 0 deletions(-) + a => b | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/ - a => b | 0 - 1 file changed, 0 insertions(+), 0 deletions(-) + a => b | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) EOF test_cmp expect actual ' From patchwork Fri Sep 18 11:32:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Guyot X-Patchwork-Id: 11784739 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A6AE5746 for ; Fri, 18 Sep 2020 11:39:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8B5B42311C for ; Fri, 18 Sep 2020 11:39:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726548AbgIRLjq (ORCPT ); Fri, 18 Sep 2020 07:39:46 -0400 Received: from mail001-2.aei.ca ([206.123.6.133]:36794 "EHLO mail001.aei.ca" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726121AbgIRLjq (ORCPT ); Fri, 18 Sep 2020 07:39:46 -0400 Received: (qmail 32126 invoked by uid 89); 18 Sep 2020 11:33:05 -0000 Received: by simscan 1.2.0 ppid: 32121, pid: 32123, t: 0.0064s scanners: regex: 1.2.0 attach: 1.2.0 Received: from mail002.aei.ca (HELO mta.aei.ca) (206.123.6.132) by mail001.aei.ca with (DHE-RSA-AES256-SHA encrypted) SMTP; 18 Sep 2020 11:33:05 -0000 Received: (qmail 1916 invoked by uid 89); 18 Sep 2020 11:33:06 -0000 Received: by simscan 1.2.0 ppid: 1898, pid: 1906, t: 1.3308s scanners: regex: 1.2.0 attach: 1.2.0 clamav: 0.97.8/m: spam: 3.3.1 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) X-Spam-Level: X-Spam-Status: No, hits=-3.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, FREEMAIL_FROM autolearn=ham autolearn_force=no version=3.4.1 Received: from dsl-216-221-52-235.mtl.contact.net (HELO dermoth.lan) (216.221.52.235) by mail.aei.ca with (AES256-SHA encrypted) SMTP; 18 Sep 2020 11:33:04 -0000 Received: from dermoth by dermoth.lan with local (Exim 4.92) (envelope-from ) id 1kJEdT-0004cI-GP; Fri, 18 Sep 2020 07:33:03 -0400 From: Thomas Guyot-Sionnest To: git@vger.kernel.org Cc: dermoth@aei.ca, Thomas Guyot-Sionnest Subject: [PATCH 2/2] Allow passing pipes for input pipes to diff --no-index Date: Fri, 18 Sep 2020 07:32:56 -0400 Message-Id: <20200918113256.8699-3-tguyot@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200918113256.8699-1-tguyot@gmail.com> References: <20200918113256.8699-1-tguyot@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org 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 --- 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; iexpect1 + $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