commit 39449a05d564e098f638d1be356d1efbba3ab38c from: Stefan Sperling date: Thu Jul 23 14:21:29 2020 UTC make 'got diff' show changes to symlinks in a work tree commit - 7d61d89137e4704e685dacfcfb71a42a87ba9f78 commit + 39449a05d564e098f638d1be356d1efbba3ab38c blob - d2353a4d430b6ae349341d2bfac864d65c2a9a7a blob + 8d5e78e2dc5a7a5aabb308726b8319e56d4a3ab9 --- got/got.c +++ got/got.c @@ -3631,6 +3631,52 @@ struct print_diff_arg { int diff_staged; int ignore_whitespace; }; + +/* + * Create a file which contains the target path of a symlink so we can feed + * it as content to the diff engine. + */ +static const struct got_error * +get_symlink_target_file(int *fd, int dirfd, const char *de_name, + const char *abspath) +{ + const struct got_error *err = NULL; + char target_path[PATH_MAX]; + ssize_t target_len, outlen; + + *fd = -1; + + if (dirfd != -1) { + target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX); + if (target_len == -1) + return got_error_from_errno2("readlinkat", abspath); + } else { + target_len = readlink(abspath, target_path, PATH_MAX); + if (target_len == -1) + return got_error_from_errno2("readlink", abspath); + } + + *fd = got_opentempfd(); + if (*fd == -1) + return got_error_from_errno("got_opentempfd"); + + outlen = write(*fd, target_path, target_len); + if (outlen == -1) { + err = got_error_from_errno("got_opentempfd"); + goto done; + } + + if (lseek(*fd, 0, SEEK_SET) == -1) { + err = got_error_from_errno2("lseek", abspath); + goto done; + } +done: + if (err) { + close(*fd); + *fd = -1; + } + return err; +} static const struct got_error * print_diff(void *arg, unsigned char status, unsigned char staged_status, @@ -3723,14 +3769,28 @@ print_diff(void *arg, unsigned char status, unsigned c if (dirfd != -1) { fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW); if (fd == -1) { - err = got_error_from_errno2("openat", abspath); - goto done; + if (errno != ELOOP) { + err = got_error_from_errno2("openat", + abspath); + goto done; + } + err = get_symlink_target_file(&fd, dirfd, + de_name, abspath); + if (err) + goto done; } } else { fd = open(abspath, O_RDONLY | O_NOFOLLOW); if (fd == -1) { - err = got_error_from_errno2("open", abspath); - goto done; + if (errno != ELOOP) { + err = got_error_from_errno2("open", + abspath); + goto done; + } + err = get_symlink_target_file(&fd, dirfd, + de_name, abspath); + if (err) + goto done; } } if (fstat(fd, &sb) == -1) { blob - a3f99c8779d3bd933ae0f0f14741563fad6794a7 blob + b29b40b95fb58c581c7b24ef325ee920470bb0cf --- regress/cmdline/diff.sh +++ regress/cmdline/diff.sh @@ -361,10 +361,103 @@ function test_diff_submodule_of_same_repo { fi test_done "$testroot" "$ret" } + +function test_diff_symlinks_in_work_tree { + local testroot=`test_init diff_symlinks_in_work_tree` + + (cd $testroot/repo && ln -s alpha alpha.link) + (cd $testroot/repo && ln -s epsilon epsilon.link) + (cd $testroot/repo && ln -s /etc/passwd passwd.link) + (cd $testroot/repo && ln -s ../beta epsilon/beta.link) + (cd $testroot/repo && ln -s nonexistent nonexistent.link) + (cd $testroot/repo && ln -s .got/foo dotgotfoo.link) + (cd $testroot/repo && git add .) + git_commit $testroot/repo -m "add symlinks" + local commit_id1=`git_show_head $testroot/repo` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/wt && ln -sf beta alpha.link) + (cd $testroot/wt && ln -sf gamma epsilon.link) + (cd $testroot/wt && ln -sf ../gamma/delta epsilon/beta.link) + echo -n '.got/bar' > $testroot/wt/dotgotfoo.link + (cd $testroot/wt && got rm nonexistent.link > /dev/null) + (cd $testroot/wt && ln -sf epsilon/zeta zeta.link) + (cd $testroot/wt && got add zeta.link > /dev/null) + (cd $testroot/wt && got diff > $testroot/stdout) + echo "diff $commit_id1 $testroot/wt" > $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -c $commit_id1 -i | \ + grep 'alpha.link@ -> alpha$' | \ + cut -d' ' -f 1 >> $testroot/stdout.expected + echo 'file + alpha.link' >> $testroot/stdout.expected + echo '--- alpha.link' >> $testroot/stdout.expected + echo '+++ alpha.link' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + echo '+beta' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -c $commit_id1 -i | \ + grep 'dotgotfoo.link@ -> .got/foo$' | \ + cut -d' ' -f 1 >> $testroot/stdout.expected + echo 'file + dotgotfoo.link' >> $testroot/stdout.expected + echo '--- dotgotfoo.link' >> $testroot/stdout.expected + echo '+++ dotgotfoo.link' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-.got/foo' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + echo '+.got/bar' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -c $commit_id1 -i epsilon | \ + grep 'beta.link@ -> ../beta$' | \ + cut -d' ' -f 1 >> $testroot/stdout.expected + echo 'file + epsilon/beta.link' >> $testroot/stdout.expected + echo '--- epsilon/beta.link' >> $testroot/stdout.expected + echo '+++ epsilon/beta.link' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-../beta' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + echo '+../gamma/delta' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -c $commit_id1 -i | \ + grep 'nonexistent.link@ -> nonexistent$' | \ + cut -d' ' -f 1 >> $testroot/stdout.expected + echo 'file + /dev/null' >> $testroot/stdout.expected + echo '--- nonexistent.link' >> $testroot/stdout.expected + echo '+++ nonexistent.link' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-nonexistent' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + zeta.link' >> $testroot/stdout.expected + echo '--- zeta.link' >> $testroot/stdout.expected + echo '+++ zeta.link' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+epsilon/zeta' >> $testroot/stdout.expected + echo '\ No newline at end of file' >> $testroot/stdout.expected + + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + run_test test_diff_basic run_test test_diff_shows_conflict run_test test_diff_tag run_test test_diff_lightweight_tag run_test test_diff_ignore_whitespace run_test test_diff_submodule_of_same_repo +run_test test_diff_symlinks_in_work_tree