Commit Diff


commit - 4135d7d058f245363e836faa15dbedbd91bd1334
commit + 40dde666c0e7cae797b8652b1f4368e52c2c7b13
blob - 152352cad8daa30cc23016c559df4f22a087bd74
blob + 7e5ee06994a5158bc937ce8307bd19f51d7e0ef5
--- lib/diff.c
+++ lib/diff.c
@@ -114,16 +114,25 @@ diff_blobs(struct got_blob_object *blob1, struct got_b
 
 	if (outfile) {
 		char *modestr1 = NULL, *modestr2 = NULL;
+		int modebits;
 		if (mode1 && mode1 != mode2) {
+			if (S_ISLNK(mode1))
+				modebits = S_IFLNK;
+			else
+				modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
 			if (asprintf(&modestr1, " (mode %o)",
-			    mode1 & (S_IRWXU | S_IRWXG | S_IRWXO)) == -1) {
+			    mode1 & modebits) == -1) {
 				err = got_error_from_errno("asprintf");
 				goto done;
 			}
 		}
 		if (mode2 && mode1 != mode2) {
+			if (S_ISLNK(mode2))
+				modebits = S_IFLNK;
+			else
+				modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
 			if (asprintf(&modestr2, " (mode %o)",
-			    mode2 & (S_IRWXU | S_IRWXG | S_IRWXO)) == -1) {
+			    mode2 & modebits) == -1) {
 				err = got_error_from_errno("asprintf");
 				goto done;
 			}
@@ -550,9 +559,11 @@ diff_entry_old_new(struct got_tree_entry *te1,
 		if (!id_match)
 			return diff_modified_tree(&te1->id, &te2->id,
 			    label1, label2, repo, cb, cb_arg, diff_content);
-	} else if (S_ISREG(te1->mode) && S_ISREG(te2->mode)) {
+	} else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) &&
+	    (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) {
 		if (!id_match ||
-		    (te1->mode & S_IXUSR) != (te2->mode & S_IXUSR)) {
+		    ((te1->mode & (S_IFLNK | S_IXUSR))) !=
+		    (te2->mode & (S_IFLNK | S_IXUSR))) {
 			if (diff_content)
 				return diff_modified_blob(&te1->id, &te2->id,
 				    label1, label2, te1->mode, te2->mode,
blob - cfc0d4b524be60277183c28685d392dbde650020
blob + eaff0579c403d60f90fdc01f50fde183091286a2
--- lib/object.c
+++ lib/object.c
@@ -1700,6 +1700,14 @@ normalize_mode_for_comparison(mode_t mode)
 	 */
 	if (S_ISDIR(mode))
 		return mode & S_IFDIR;
+
+	/*
+	 * For symlinks, the only relevant bit is the IFLNK bit.
+	 * This allows us to detect paths changing from a symlinks
+	 * to a file or directory and vice versa.
+	 */
+	if (S_ISLNK(mode))
+		return mode & S_IFLNK;
 
 	/* For files, the only change we care about is the executable bit. */
 	return mode & S_IXUSR;
blob - 649ab258d4c149e3942f5f2da369992a8dd44f69
blob + dbc39580319a7e28e9adc23ded3e53d643d3d6e0
--- regress/cmdline/diff.sh
+++ regress/cmdline/diff.sh
@@ -453,6 +453,123 @@ function test_diff_symlinks_in_work_tree {
 	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"
+}
+
+function test_diff_symlinks_in_repo {
+	local testroot=`test_init diff_symlinks_in_repo`
+
+	(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`
+
+	(cd $testroot/repo && ln -sf beta alpha.link)
+	(cd $testroot/repo && ln -sfh gamma epsilon.link)
+	(cd $testroot/repo && ln -sf ../gamma/delta epsilon/beta.link)
+	(cd $testroot/repo && ln -sf .got/bar $testroot/repo/dotgotfoo.link)
+	(cd $testroot/repo && git rm -q nonexistent.link)
+	(cd $testroot/repo && ln -sf epsilon/zeta zeta.link)
+	(cd $testroot/repo && git add .)
+	git_commit $testroot/repo -m "change symlinks"
+	local commit_id2=`git_show_head $testroot/repo`
+
+	got diff -r $testroot/repo $commit_id1 $commit_id2 > $testroot/stdout
+
+	echo "diff $commit_id1 $commit_id2" > $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 -n 'blob + ' >> $testroot/stdout.expected
+	got tree -r $testroot/repo -c $commit_id2 -i | \
+		grep 'alpha.link@ -> beta$' | \
+		cut -d' ' -f 1 >> $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 -n 'blob + ' >> $testroot/stdout.expected
+	got tree -r $testroot/repo -c $commit_id2 -i | \
+		grep 'dotgotfoo.link@ -> .got/bar$' | \
+		cut -d' ' -f 1 >> $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 -n 'blob + ' >> $testroot/stdout.expected
+	got tree -r $testroot/repo -c $commit_id2 -i epsilon | \
+		grep 'beta.link@ -> ../gamma/delta$' | \
+		cut -d' ' -f 1 >> $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 'epsilon.link@ -> epsilon$' | \
+		cut -d' ' -f 1 >> $testroot/stdout.expected
+	echo -n 'blob + ' >> $testroot/stdout.expected
+	got tree -r $testroot/repo -c $commit_id2 -i | \
+		grep 'epsilon.link@ -> gamma$' | \
+		cut -d' ' -f 1 >> $testroot/stdout.expected
+	echo '--- epsilon.link' >> $testroot/stdout.expected
+	echo '+++ epsilon.link' >> $testroot/stdout.expected
+	echo '@@ -1 +1 @@' >> $testroot/stdout.expected
+	echo '-epsilon' >> $testroot/stdout.expected
+	echo '\ No newline at end of file' >> $testroot/stdout.expected
+	echo '+gamma' >> $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 | sed -e 's/$/ (mode 120000)/' \
+		>> $testroot/stdout.expected
+	echo 'blob + /dev/null' >> $testroot/stdout.expected
+	echo '--- nonexistent.link' >> $testroot/stdout.expected
+	echo '+++ /dev/null' >> $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 -n 'blob + ' >> $testroot/stdout.expected
+	got tree -r $testroot/repo -c $commit_id2 -i | \
+		grep 'zeta.link@ -> epsilon/zeta$' | \
+		cut -d' ' -f 1 | sed -e 's/$/ (mode 120000)/' \
+		>> $testroot/stdout.expected
+	echo '--- /dev/null' >> $testroot/stdout.expected
 	echo '+++ zeta.link' >> $testroot/stdout.expected
 	echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected
 	echo '+epsilon/zeta' >> $testroot/stdout.expected
@@ -473,3 +590,4 @@ 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
+run_test test_diff_symlinks_in_repo