diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='diff --relative tests'
|
2022-11-09 02:17:39 +08:00
|
|
|
|
|
|
|
TEST_PASSES_SANITIZE_LEAK=true
|
diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
. ./test-lib.sh
|
|
|
|
|
|
|
|
test_expect_success 'setup' '
|
|
|
|
git commit --allow-empty -m empty &&
|
|
|
|
echo content >file1 &&
|
|
|
|
mkdir subdir &&
|
|
|
|
echo other content >subdir/file2 &&
|
2020-05-22 18:46:18 +08:00
|
|
|
blob_file1=$(git hash-object file1) &&
|
|
|
|
blob_file2=$(git hash-object subdir/file2) &&
|
diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
git add . &&
|
|
|
|
git commit -m one
|
|
|
|
'
|
|
|
|
|
2017-12-10 04:40:13 +08:00
|
|
|
check_diff () {
|
|
|
|
dir=$1
|
|
|
|
shift
|
|
|
|
expect=$1
|
|
|
|
shift
|
2020-05-22 18:46:18 +08:00
|
|
|
short_blob=$(git rev-parse --short $blob_file2)
|
2017-12-10 04:40:13 +08:00
|
|
|
cat >expected <<-EOF
|
|
|
|
diff --git a/$expect b/$expect
|
|
|
|
new file mode 100644
|
2018-05-21 10:01:45 +08:00
|
|
|
index 0000000..$short_blob
|
2017-12-10 04:40:13 +08:00
|
|
|
--- /dev/null
|
|
|
|
+++ b/$expect
|
|
|
|
@@ -0,0 +1 @@
|
|
|
|
+other content
|
|
|
|
EOF
|
|
|
|
test_expect_success "-p $*" "
|
|
|
|
git -C '$dir' diff -p $* HEAD^ >actual &&
|
|
|
|
test_cmp expected actual
|
|
|
|
"
|
diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
}
|
|
|
|
|
2017-12-10 04:40:13 +08:00
|
|
|
check_numstat () {
|
|
|
|
dir=$1
|
|
|
|
shift
|
|
|
|
expect=$1
|
|
|
|
shift
|
|
|
|
cat >expected <<-EOF
|
|
|
|
1 0 $expect
|
|
|
|
EOF
|
|
|
|
test_expect_success "--numstat $*" "
|
|
|
|
echo '1 0 $expect' >expected &&
|
|
|
|
git -C '$dir' diff --numstat $* HEAD^ >actual &&
|
|
|
|
test_cmp expected actual
|
|
|
|
"
|
2012-03-13 13:05:54 +08:00
|
|
|
}
|
|
|
|
|
2017-12-10 04:40:13 +08:00
|
|
|
check_stat () {
|
|
|
|
dir=$1
|
|
|
|
shift
|
|
|
|
expect=$1
|
|
|
|
shift
|
|
|
|
cat >expected <<-EOF
|
|
|
|
$expect | 1 +
|
|
|
|
1 file changed, 1 insertion(+)
|
|
|
|
EOF
|
|
|
|
test_expect_success "--stat $*" "
|
|
|
|
git -C '$dir' diff --stat $* HEAD^ >actual &&
|
2021-02-11 09:53:53 +08:00
|
|
|
test_cmp expected actual
|
2017-12-10 04:40:13 +08:00
|
|
|
"
|
diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
}
|
|
|
|
|
2017-12-10 04:40:13 +08:00
|
|
|
check_raw () {
|
|
|
|
dir=$1
|
|
|
|
shift
|
|
|
|
expect=$1
|
|
|
|
shift
|
|
|
|
cat >expected <<-EOF
|
2020-05-22 18:46:18 +08:00
|
|
|
:000000 100644 $ZERO_OID $blob_file2 A $expect
|
2017-12-10 04:40:13 +08:00
|
|
|
EOF
|
|
|
|
test_expect_success "--raw $*" "
|
|
|
|
git -C '$dir' diff --no-abbrev --raw $* HEAD^ >actual &&
|
|
|
|
test_cmp expected actual
|
|
|
|
"
|
diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
}
|
|
|
|
|
2017-12-10 04:40:12 +08:00
|
|
|
for type in diff numstat stat raw
|
|
|
|
do
|
|
|
|
check_$type . file2 --relative=subdir/
|
|
|
|
check_$type . file2 --relative=subdir
|
|
|
|
check_$type subdir file2 --relative
|
|
|
|
check_$type . dir/file2 --relative=sub
|
diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
done
|
|
|
|
|
2020-05-22 18:46:18 +08:00
|
|
|
check_diff_relative_option () {
|
|
|
|
dir=$1
|
|
|
|
shift
|
|
|
|
expect=$1
|
|
|
|
shift
|
|
|
|
relative_opt=$1
|
|
|
|
shift
|
|
|
|
test_expect_success "config diff.relative $relative_opt -p $*" "
|
|
|
|
short_blob=\$(git rev-parse --short $blob_file2) &&
|
|
|
|
cat >expected <<-EOF &&
|
|
|
|
diff --git a/$expect b/$expect
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000..\$short_blob
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/$expect
|
|
|
|
@@ -0,0 +1 @@
|
|
|
|
+other content
|
|
|
|
EOF
|
|
|
|
test_config -C $dir diff.relative $relative_opt &&
|
|
|
|
git -C '$dir' diff -p $* HEAD^ >actual &&
|
|
|
|
test_cmp expected actual
|
|
|
|
"
|
|
|
|
}
|
|
|
|
|
|
|
|
check_diff_no_relative_option () {
|
|
|
|
dir=$1
|
|
|
|
shift
|
|
|
|
expect=$1
|
|
|
|
shift
|
|
|
|
relative_opt=$1
|
|
|
|
shift
|
|
|
|
test_expect_success "config diff.relative $relative_opt -p $*" "
|
|
|
|
short_blob_file1=\$(git rev-parse --short $blob_file1) &&
|
|
|
|
short_blob_file2=\$(git rev-parse --short $blob_file2) &&
|
|
|
|
cat >expected <<-EOF &&
|
|
|
|
diff --git a/file1 b/file1
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000..\$short_blob_file1
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/file1
|
|
|
|
@@ -0,0 +1 @@
|
|
|
|
+content
|
|
|
|
diff --git a/$expect b/$expect
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000..\$short_blob_file2
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/$expect
|
|
|
|
@@ -0,0 +1 @@
|
|
|
|
+other content
|
|
|
|
EOF
|
|
|
|
test_config -C $dir diff.relative $relative_opt &&
|
|
|
|
git -C '$dir' diff -p $* HEAD^ >actual &&
|
|
|
|
test_cmp expected actual
|
|
|
|
"
|
|
|
|
}
|
|
|
|
|
|
|
|
check_diff_no_relative_option . subdir/file2 false
|
|
|
|
check_diff_no_relative_option . subdir/file2 true --no-relative
|
|
|
|
check_diff_no_relative_option . subdir/file2 false --no-relative
|
|
|
|
check_diff_no_relative_option subdir subdir/file2 false
|
|
|
|
check_diff_no_relative_option subdir subdir/file2 true --no-relative
|
|
|
|
check_diff_no_relative_option subdir subdir/file2 false --no-relative
|
|
|
|
|
|
|
|
check_diff_relative_option . file2 false --relative=subdir/
|
|
|
|
check_diff_relative_option . file2 false --relative=subdir
|
|
|
|
check_diff_relative_option . file2 true --relative=subdir/
|
|
|
|
check_diff_relative_option . file2 true --relative=subdir
|
|
|
|
check_diff_relative_option subdir file2 false --relative
|
|
|
|
check_diff_relative_option subdir file2 true --relative
|
|
|
|
check_diff_relative_option subdir file2 true
|
|
|
|
check_diff_relative_option subdir file2 false --no-relative --relative
|
|
|
|
check_diff_relative_option subdir file2 true --no-relative --relative
|
|
|
|
check_diff_relative_option . file2 false --no-relative --relative=subdir
|
|
|
|
check_diff_relative_option . file2 true --no-relative --relative=subdir
|
|
|
|
|
diff: use filespec path to set up tempfiles for ext-diff
When we're going to run an external diff, we have to make the contents
of the pre- and post-images available either by dumping them to a
tempfile, or by pointing at a valid file in the worktree. The logic of
this is all handled by prepare_temp_file(), and we just pass in the
filename and the diff_filespec.
But there's a gotcha here. The "filename" we have is a logical filename
and not necessarily a path on disk or in the repository. This matters in
at least one case: when using "--relative", we may have a name like
"foo", even though the file content is found at "subdir/foo". As a
result, we look for the wrong path, fail to find "foo", and claim that
the file has been deleted (passing "/dev/null" to the external diff,
rather than the correct worktree path).
We can fix this by passing the pathname from the diff_filespec, which
should always be a full repository path (and that's what we want even if
reusing a worktree file, since we're always operating from the top-level
of the working tree).
The breakage seems to go all the way back to cd676a5136 (diff
--relative: output paths as relative to the current subdirectory,
2008-02-12). As far as I can tell, before then "name" would always have
been the same as the filespec's "path".
There are two related cases I looked at that aren't buggy:
1. the only other caller of prepare_temp_file() is run_textconv(). But
it always passes the filespec's path field, so it's OK.
2. I wondered if file renames/copies might cause similar confusion.
But they don't, because run_external_diff() receives two names in
that case: "name" and "other", which correspond to the two sides of
the diff. And we did correctly pass "other" when handling the
post-image side. Barring the use of "--relative", that would always
match "two->path", the path of the second filespec (and the rename
destination).
So the only bug is just the interaction with external diff drivers and
--relative.
Reported-by: Carl Baldwin <carl@ecbaldwin.net>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-01-06 19:03:57 +08:00
|
|
|
test_expect_success 'external diff with --relative' '
|
|
|
|
test_when_finished "git reset --hard" &&
|
|
|
|
echo changed >file1 &&
|
|
|
|
echo changed >subdir/file2 &&
|
|
|
|
|
|
|
|
write_script mydiff <<-\EOF &&
|
|
|
|
# hacky pretend diff; the goal here is just to make sure we got
|
|
|
|
# passed sensible input that we _could_ diff, without relying on
|
|
|
|
# the specific output of a system diff tool.
|
|
|
|
echo "diff a/$1 b/$1" &&
|
|
|
|
echo "--- a/$1" &&
|
|
|
|
echo "+++ b/$1" &&
|
|
|
|
echo "@@ -1 +0,0 @@" &&
|
|
|
|
sed "s/^/-/" "$2" &&
|
|
|
|
sed "s/^/+/" "$5"
|
|
|
|
EOF
|
|
|
|
|
|
|
|
cat >expect <<-\EOF &&
|
|
|
|
diff a/file2 b/file2
|
|
|
|
--- a/file2
|
|
|
|
+++ b/file2
|
|
|
|
@@ -1 +0,0 @@
|
|
|
|
-other content
|
|
|
|
+changed
|
|
|
|
EOF
|
|
|
|
GIT_EXTERNAL_DIFF=./mydiff git diff --relative=subdir >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2021-08-22 16:49:08 +08:00
|
|
|
test_expect_success 'setup diff --relative unmerged' '
|
|
|
|
test_commit zero file0 &&
|
|
|
|
test_commit base subdir/file0 &&
|
|
|
|
git switch -c br1 &&
|
|
|
|
test_commit one file0 &&
|
|
|
|
test_commit sub1 subdir/file0 &&
|
|
|
|
git switch -c br2 base &&
|
|
|
|
test_commit two file0 &&
|
|
|
|
git switch -c br3 &&
|
|
|
|
test_commit sub3 subdir/file0
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'diff --relative without change in subdir' '
|
|
|
|
git switch br2 &&
|
|
|
|
test_when_finished "git merge --abort" &&
|
|
|
|
test_must_fail git merge one &&
|
|
|
|
git -C subdir diff --relative >out &&
|
|
|
|
test_must_be_empty out &&
|
|
|
|
git -C subdir diff --relative --name-only >out &&
|
|
|
|
test_must_be_empty out
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'diff --relative --name-only with change in subdir' '
|
|
|
|
git switch br3 &&
|
|
|
|
test_when_finished "git merge --abort" &&
|
|
|
|
test_must_fail git merge sub1 &&
|
|
|
|
test_write_lines file0 file0 >expected &&
|
|
|
|
git -C subdir diff --relative --name-only >out &&
|
|
|
|
test_cmp expected out
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_failure 'diff --relative with change in subdir' '
|
|
|
|
git switch br3 &&
|
|
|
|
br1_blob=$(git rev-parse --short --verify br1:subdir/file0) &&
|
|
|
|
br3_blob=$(git rev-parse --short --verify br3:subdir/file0) &&
|
|
|
|
test_when_finished "git merge --abort" &&
|
|
|
|
test_must_fail git merge br1 &&
|
|
|
|
cat >expected <<-EOF &&
|
|
|
|
diff --cc file0
|
|
|
|
index $br3_blob,$br1_blob..0000000
|
|
|
|
--- a/file0
|
|
|
|
+++ b/file0
|
|
|
|
@@@ -1,1 -1,1 +1,5 @@@
|
|
|
|
++<<<<<<< HEAD
|
|
|
|
+sub3
|
|
|
|
++=======
|
|
|
|
+ sub1
|
|
|
|
++>>>>>>> br1
|
|
|
|
EOF
|
|
|
|
git -C subdir diff --relative >out &&
|
|
|
|
test_cmp expected out
|
|
|
|
'
|
|
|
|
|
diff: strip extra "/" when stripping prefix
There are two ways a user might want to use "diff --relative":
1. For a file in a directory, like "subdir/file", the user
can use "--relative=subdir/" to strip the directory.
2. To strip part of a filename, like "foo-10", they can
use "--relative=foo-".
We currently handle both of those situations. However, if the user passes
"--relative=subdir" (without the trailing slash), we produce inconsistent
results. For the unified diff format, we collapse the double-slash of
"a//file" correctly into "a/file". But for other formats (raw, stat,
name-status), we end up with "/file".
We can do what the user means here and strip the extra "/" (and only a
slash). We are not hurting any existing users of (2) above with this
behavior change because the existing output for this case was nonsensical.
Patch by Jakub, tests and commit message by Jeff King.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-09 22:50:53 +08:00
|
|
|
test_done
|