]> git.ipfire.org Git - thirdparty/git.git/commitdiff
diff-lib: ignore paths that are outside $cwd if --relative asked
authorĐoàn Trần Công Danh <congdanhqx@gmail.com>
Sun, 22 Aug 2021 08:49:08 +0000 (15:49 +0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 25 Aug 2021 18:49:36 +0000 (11:49 -0700)
For diff family commands, we can tell them to exclude changes outside
of some directories if --relative is requested.

In diff_unmerge(), NULL will be returned if the requested path is
outside of the interesting directories, thus we'll run into NULL
pointer dereference in run_diff_files when trying to dereference
its return value.

Checking for return value of diff_unmerge before dereferencing
is not sufficient, though. Since, diff engine will try to work on such
pathspec later.

Let's not run diff on those unintesting entries, instead.
As a side effect, by skipping like that, we can save some CPU cycles.

Reported-by: Thomas De Zeeuw <thomas@slight.dev>
Tested-by: Carlo Arenas <carenas@gmail.com>
Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff-lib.c
t/t4045-diff-relative.sh

index b73cc1859a49eb8b7f3cf7321680dfda4d96c705..b0c383647d0c8429b5c191463ebd12834141b887 100644 (file)
@@ -116,6 +116,10 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                if (!ce_path_match(istate, ce, &revs->prune_data, NULL))
                        continue;
 
+               if (revs->diffopt.prefix &&
+                   strncmp(ce->name, revs->diffopt.prefix, revs->diffopt.prefix_length))
+                       continue;
+
                if (ce_stage(ce)) {
                        struct combine_diff_path *dpath;
                        struct diff_filepair *pair;
index 7be1de736d86c90ac9c36ff68c0e8f4535e9bafd..6afa3dffaa6de77e504eccba5032e0dc5296a312 100755 (executable)
@@ -162,4 +162,57 @@ 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
 
+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
+'
+
 test_done