From: Mark Jamsek Subject: regress for got diff -d + minor fix To: Game of Trees Date: Tue, 10 Jan 2023 20:15:04 +1100 The below diff adds a couple tests for 'got diff -d'. And they already picked up an edge case: if we specify paths to diff in a worktree for a commit in which one of the paths was deleted, diff_paths() passes "/dev/null" to got_diff_blob_output_unidiff() for the deleted path (i.e., label2). This is for the: --- file/path +++ /dev/null header in the diff. This means we can't unconditionally rely on label2 for the path when collecting its diffstat. The fix, included in the diff, is to only use label2 for the path if it's not a GOT_STATUS_DELETE state or there is no label1; the latter of which shouldn't happen if the file has been deleted for there must have been a file path to be deleted in the first place, but we check just in case. You can backout the change in the below diff to lib/diff.c and run the new tests to see what the diff.c change improves, but it basically turns the top output into the bottom: -----8<----------- diffstat 7f99867f74e9704bb31b4883ce21e64713a1d012 b178d0ebdaaa45141e130377c83cda1f46008361 D /dev/null | 0+ 1- A new | 1+ 0- diffstat 7f99867f74e9704bb31b4883ce21e64713a1d012 b178d0ebdaaa45141e130377c83cda1f46008361 D beta | 0+ 1- A new | 1+ 0- ------------>8---- diffstat /home/mark/src/got M lib/diff.c | 34+ 26- M regress/cmdline/diff.sh | 416+ 0- 2 files changed, 450 insertions(+), 26 deletions(-) diff /home/mark/src/got commit - a76e88e58fb716d5dded83442b153b60687283cb path + /home/mark/src/got blob - b5900dcf76baf3e7af894d4817aa447491a287fc file + lib/diff.c --- lib/diff.c +++ lib/diff.c @@ -246,6 +246,18 @@ diff_blobs(struct got_diff_line **lines, size_t *nline char *path = NULL; int status = GOT_STATUS_NO_CHANGE; + /* + * Ignore 'm'ode status change: if there's no accompanying + * content change, there'll be no diffstat, and if there + * are actual changes, 'M'odified takes precedence. + */ + if (blob1 == NULL) + status = GOT_STATUS_ADD; + else if (blob2 == NULL) + status = GOT_STATUS_DELETE; + else + status = GOT_STATUS_MODIFY; + if (label1 == NULL && label2 == NULL) { /* diffstat of blobs, show hash instead of path */ if (asprintf(&path, "%.10s -> %.10s", @@ -254,25 +266,17 @@ diff_blobs(struct got_diff_line **lines, size_t *nline goto done; } } else { - path = strdup(label2 ? label2 : label1); + if (label2 != NULL && + (status != GOT_STATUS_DELETE || label1 == NULL)) + path = strdup(label2); + else + path = strdup(label1); if (path == NULL) { err = got_error_from_errno("malloc"); goto done; } } - /* - * Ignore 'm'ode status change: if there's no accompanying - * content change, there'll be no diffstat, and if there - * are actual changes, 'M'odified takes precedence. - */ - if (blob1 == NULL) - status = GOT_STATUS_ADD; - else if (blob2 == NULL) - status = GOT_STATUS_DELETE; - else - status = GOT_STATUS_MODIFY; - err = get_diffstat(ds, path, result->result, force_text_diff, status); if (err) { @@ -390,24 +394,28 @@ diff_blob_file(struct got_diffreg_result **resultp, char *path = NULL; int status = GOT_STATUS_NO_CHANGE; - path = strdup(label2 ? label2 : label1); + /* + * Ignore 'm'ode status change: if there's no accompanying + * content change, there'll be no diffstat, and if there + * are actual changes, 'M'odified takes precedence. + */ + if (blob1 == NULL) + status = GOT_STATUS_ADD; + else if (!f2_exists) + status = GOT_STATUS_DELETE; + else + status = GOT_STATUS_MODIFY; + + if (label2 != NULL && + (status != GOT_STATUS_DELETE || label1 == NULL)) + path = strdup(label2); + else + path = strdup(label1); if (path == NULL) { err = got_error_from_errno("malloc"); goto done; } - /* - * Ignore 'm'ode status change: if there's no accompanying - * content change, there'll be no diffstat, and if there - * are actual changes, 'M'odified takes precedence. - */ - if (blob1 == NULL) - status = GOT_STATUS_ADD; - else if (!f2_exists) - status = GOT_STATUS_DELETE; - else - status = GOT_STATUS_MODIFY; - err = get_diffstat(ds, path, result->result, force_text_diff, status); if (err) { blob - 51fe8c9d8176eeaa086aa653071670afd2db3eeb file + regress/cmdline/diff.sh --- regress/cmdline/diff.sh +++ regress/cmdline/diff.sh @@ -1375,6 +1375,420 @@ test_parseargs "$@" test_done "$testroot" $ret } +test_diff_commit_diffstat() { + local testroot=`test_init diff_commit_diffstat` + local commit_id0=`git_show_head $testroot/repo` + alpha_id0=`get_blob_id $testroot/repo "" alpha` + beta_id0=`get_blob_id $testroot/repo "" beta` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + test_done "$testroot" "$ret" + return 1 + fi + + echo "modified alpha" > $testroot/wt/alpha + (cd $testroot/wt && got rm beta >/dev/null) + echo "new file" > $testroot/wt/new + (cd $testroot/wt && got add new >/dev/null) + (cd $testroot/wt && got commit -m 'committing changes' >/dev/null) + local commit_id1=`git_show_head $testroot/repo` + + alpha_id1=`get_blob_id $testroot/repo "" alpha` + new_id1=`get_blob_id $testroot/repo "" new` + + cat <$testroot/stdout.expected +diffstat $commit_id0 refs/heads/master + M alpha | 1+ 1- + D beta | 0+ 1- + A new | 1+ 0- + +3 files changed, 2 insertions(+), 2 deletions(-) + +EOF + + echo "diff $commit_id0 refs/heads/master" >> $testroot/stdout.expected + echo "commit - $commit_id0" >> $testroot/stdout.expected + echo "commit + $commit_id1" >> $testroot/stdout.expected + echo "blob - $alpha_id0" >> $testroot/stdout.expected + echo "blob + $alpha_id1" >> $testroot/stdout.expected + echo '--- alpha' >> $testroot/stdout.expected + echo '+++ alpha' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '+modified alpha' >> $testroot/stdout.expected + echo "blob - $beta_id0 (mode 644)" >> $testroot/stdout.expected + echo 'blob + /dev/null' >> $testroot/stdout.expected + echo '--- beta' >> $testroot/stdout.expected + echo '+++ /dev/null' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-beta' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo "blob + $new_id1 (mode 644)" >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + + (cd $testroot/wt && got diff -d -c master > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # same diffstat with explicit parent commit ID + (cd $testroot/wt && got diff -d -c $commit_id0 -c master \ + > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + cat <$testroot/stdout.expected +diffstat $commit_id0 $commit_id1 + M alpha | 1+ 1- + D beta | 0+ 1- + A new | 1+ 0- + +3 files changed, 2 insertions(+), 2 deletions(-) + +EOF + + # same diffstat with commit object IDs + echo "diff $commit_id0 $commit_id1" >> $testroot/stdout.expected + echo "commit - $commit_id0" >> $testroot/stdout.expected + echo "commit + $commit_id1" >> $testroot/stdout.expected + echo "blob - $alpha_id0" >> $testroot/stdout.expected + echo "blob + $alpha_id1" >> $testroot/stdout.expected + echo '--- alpha' >> $testroot/stdout.expected + echo '+++ alpha' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '+modified alpha' >> $testroot/stdout.expected + echo "blob - $beta_id0 (mode 644)" >> $testroot/stdout.expected + echo 'blob + /dev/null' >> $testroot/stdout.expected + echo '--- beta' >> $testroot/stdout.expected + echo '+++ /dev/null' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-beta' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo "blob + $new_id1 (mode 644)" >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + (cd $testroot/wt && got diff -d -c $commit_id0 -c $commit_id1 \ + > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + cat <$testroot/stdout.expected +diffstat $commit_id0 $commit_id1 + M alpha | 1+ 1- + +1 file changed, 1 insertions(+), 1 deletions(-) + +EOF + + # same diffstat filtered by path "alpha" + echo "diff $commit_id0 $commit_id1" >> $testroot/stdout.expected + echo "commit - $commit_id0" >> $testroot/stdout.expected + echo "commit + $commit_id1" >> $testroot/stdout.expected + echo "blob - $alpha_id0" >> $testroot/stdout.expected + echo "blob + $alpha_id1" >> $testroot/stdout.expected + echo '--- alpha' >> $testroot/stdout.expected + echo '+++ alpha' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '+modified alpha' >> $testroot/stdout.expected + (cd $testroot/repo && got diff -d -c $commit_id0 -c $commit_id1 alpha \ + > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + # same diffstat in work tree + (cd $testroot/wt && got diff -d -c $commit_id0 -c $commit_id1 alpha \ + > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + cat <$testroot/stdout.expected +diffstat $commit_id0 $commit_id1 + D beta | 0+ 1- + A new | 1+ 0- + +2 files changed, 1 insertions(+), 1 deletions(-) + +EOF + + # same diffstat filtered by paths "beta" and "new" + echo "diff $commit_id0 $commit_id1" >> $testroot/stdout.expected + echo "commit - $commit_id0" >> $testroot/stdout.expected + echo "commit + $commit_id1" >> $testroot/stdout.expected + echo "blob - $beta_id0 (mode 644)" >> $testroot/stdout.expected + echo 'blob + /dev/null' >> $testroot/stdout.expected + echo '--- beta' >> $testroot/stdout.expected + echo '+++ /dev/null' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-beta' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo "blob + $new_id1 (mode 644)" >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + (cd $testroot/repo && got diff -d -c $commit_id0 -c $commit_id1 \ + beta new > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + +test_diff_worktree_diffstat() { + local testroot=`test_init diff_worktree_diffstat` + local head_rev=`git_show_head $testroot/repo` + local alpha_blobid=`get_blob_id $testroot/repo "" alpha` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + test_done "$testroot" "$ret" + return 1 + fi + + echo "modified alpha" > $testroot/wt/alpha + (cd $testroot/wt && got rm beta >/dev/null) + echo "new file" > $testroot/wt/new + (cd $testroot/wt && got add new >/dev/null) + + cat <$testroot/stdout.expected +diffstat $testroot/wt + M alpha | 1+ 1- + D beta | 0+ 1- + A new | 1+ 0- + +3 files changed, 2 insertions(+), 2 deletions(-) + +EOF + + echo "diff $testroot/wt" >> $testroot/stdout.expected + echo "commit - $head_rev" >> $testroot/stdout.expected + echo "path + $testroot/wt" >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'alpha$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + alpha' >> $testroot/stdout.expected + echo '--- alpha' >> $testroot/stdout.expected + echo '+++ alpha' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '+modified alpha' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'beta$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + /dev/null' >> $testroot/stdout.expected + echo '--- beta' >> $testroot/stdout.expected + echo '+++ /dev/null' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-beta' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new (mode 644)' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + + (cd $testroot/wt && got diff -d > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + echo "modified zeta" > $testroot/wt/epsilon/zeta + + cat <$testroot/stdout.expected +diffstat $testroot/wt + M alpha | 1+ 1- + D beta | 0+ 1- + M epsilon/zeta | 1+ 1- + A new | 1+ 0- + +4 files changed, 3 insertions(+), 3 deletions(-) + +EOF + + # specify paths to diffstat + echo "diff $testroot/wt" >> $testroot/stdout.expected + echo "commit - $head_rev" >> $testroot/stdout.expected + echo "path + $testroot/wt" >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'alpha$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + alpha' >> $testroot/stdout.expected + echo '--- alpha' >> $testroot/stdout.expected + echo '+++ alpha' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '+modified alpha' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'beta$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + /dev/null' >> $testroot/stdout.expected + echo '--- beta' >> $testroot/stdout.expected + echo '+++ /dev/null' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-beta' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i epsilon | grep 'zeta$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + epsilon/zeta' >> $testroot/stdout.expected + echo '--- epsilon/zeta' >> $testroot/stdout.expected + echo '+++ epsilon/zeta' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-zeta' >> $testroot/stdout.expected + echo '+modified zeta' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new (mode 644)' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + + (cd $testroot/wt && got diff -d new alpha epsilon beta > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # same diff irrespective of argument order + (cd $testroot/wt && got diff -d alpha new epsilon beta \ + > $testroot/stdout 2> $testroot/stderr) + ret=$? + if [ $ret -ne 0 ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # force paths with -P + echo master > $testroot/wt/master + (cd $testroot/wt && got add master > /dev/null) + (cd $testroot/wt && got diff -d -P new master > $testroot/stdout) + ret=$? + if [ $ret -ne 0 ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + + cat <$testroot/stdout.expected +diffstat $testroot/wt + A master | 1+ 0- + A new | 1+ 0- + +2 files changed, 2 insertions(+), 0 deletions(-) + +EOF + + echo "diff $testroot/wt" >> $testroot/stdout.expected + echo "commit - $head_rev" >> $testroot/stdout.expected + echo "path + $testroot/wt" >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + master (mode 644)' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ master' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+master' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new (mode 644)' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # diff two blob ids + (cd $testroot/wt && got commit -m 'edit' alpha >/dev/null) + local alpha_new_blobid=`get_blob_id $testroot/repo "" alpha` + (cd $testroot/wt && got diff -d $alpha_blobid $alpha_new_blobid) \ + > $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + typeset -L10 short_alpha_id + typeset -L10 short_alpha_new_id + short_alpha_id=$alpha_blobid + short_alpha_new_id=$alpha_new_blobid + cat <$testroot/stdout.expected +diffstat $alpha_blobid $alpha_new_blobid + M $short_alpha_id -> $short_alpha_new_id | 1+ 1- + +1 file changed, 1 insertions(+), 1 deletions(-) + +blob - $alpha_blobid +blob + $alpha_new_blobid +--- $alpha_blobid ++++ $alpha_new_blobid +@@ -1 +1 @@ +-alpha ++modified alpha +EOF + + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + test_parseargs "$@" run_test test_diff_basic run_test test_diff_shows_conflict @@ -1389,3 +1803,5 @@ run_test test_diff_worktree_newfile_xbit run_test test_diff_ignored_file run_test test_diff_crlf run_test test_diff_worktree_newfile_xbit +run_test test_diff_commit_diffstat +run_test test_diff_worktree_diffstat -- Mark Jamsek GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68