Commit Diff


commit - e40622f412c8f937c81a8f76aa780647ea7392bf
commit + 0587e10c4c4e054f8aeda2bd499b76c0ec60dc40
blob - f14e875454e17d7c6012447918fec9a697f40c10
blob + ab4db7cd394dd2226cb63800aa4dc8c0d517c846
--- got/got.c
+++ got/got.c
@@ -4100,6 +4100,7 @@ cmd_blame(int argc, char *argv[])
 	struct got_repository *repo = NULL;
 	struct got_worktree *worktree = NULL;
 	char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
+	char *link_target = NULL;
 	struct got_object_id *obj_id = NULL;
 	struct got_object_id *commit_id = NULL;
 	struct got_blob_object *blob = NULL;
@@ -4214,7 +4215,13 @@ cmd_blame(int argc, char *argv[])
 			goto done;
 	}
 
-	error = got_object_id_by_path(&obj_id, repo, commit_id, in_repo_path);
+	error = got_object_resolve_symlinks(&link_target, in_repo_path,
+	    commit_id, repo);
+	if (error)
+		goto done;
+
+	error = got_object_id_by_path(&obj_id, repo, commit_id,
+	    link_target ? link_target : in_repo_path);
 	if (error)
 		goto done;
 
@@ -4258,10 +4265,11 @@ cmd_blame(int argc, char *argv[])
 	}
 	bca.repo = repo;
 
-	error = got_blame(in_repo_path, commit_id, repo, blame_cb, &bca,
-	    check_cancelled, NULL);
+	error = got_blame(link_target ? link_target : in_repo_path, commit_id,
+	    repo, blame_cb, &bca, check_cancelled, NULL);
 done:
 	free(in_repo_path);
+	free(link_target);
 	free(repo_path);
 	free(cwd);
 	free(commit_id);
blob - 9e0475af05cac63a02818ca77164c2355cf2428e
blob + d3a3bf3015086c89a0e41790e267184927b15cee
--- regress/cmdline/blame.sh
+++ regress/cmdline/blame.sh
@@ -761,7 +761,127 @@ function test_blame_submodule {
 	fi
 	test_done "$testroot" "$ret"
 }
+
+function test_blame_symlink {
+	local testroot=`test_init blame_symlink`
+	local commit_id0=`git_show_head $testroot/repo`
+	local short_commit0=`trim_obj_id 32 $commit_id0`
+
+	(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 && git add .)
+	git_commit $testroot/repo -m "add symlinks"
+
+	local commit_id1=`git_show_head $testroot/repo`
+	local short_commit1=`trim_obj_id 32 $commit_id1`
+	local author_time=`git_show_author_time $testroot/repo`
+
+	# got blame dereferences symlink to a regular file
+	got blame -r $testroot/repo alpha.link > $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "blame command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	d=`date -r $author_time +"%G-%m-%d"`
+	echo "1) $short_commit0 $d $GOT_AUTHOR_8 alpha" \
+		> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" -a "$xfail" == "" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "1"
+		return 1
+	fi
 
+	# got blame dereferences symlink with relative path
+	got blame -r $testroot/repo epsilon/beta.link > $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "blame command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	d=`date -r $author_time +"%G-%m-%d"`
+	echo "1) $short_commit0 $d $GOT_AUTHOR_8 beta" \
+		> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" -a "$xfail" == "" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	got blame -r $testroot/repo epsilon.link > $testroot/stdout \
+		2> $testroot/stderr
+	ret="$?"
+	if [ "$ret" == "0" ]; then
+		echo "blame command succeeded unexpectedly" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	# blame dereferences symlink to a directory
+	echo "got: wrong type of object" > $testroot/stderr.expected
+	cmp -s $testroot/stderr.expected $testroot/stderr
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	# got blame fails if symlink target does not exist in repo
+	got blame -r $testroot/repo passwd.link > $testroot/stdout \
+		2> $testroot/stderr
+	ret="$?"
+	if [ "$ret" == "0" ]; then
+		echo "blame command succeeded unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "got: /etc/passwd: no such entry found in tree" \
+		> $testroot/stderr.expected
+	cmp -s $testroot/stderr.expected $testroot/stderr
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	got blame -r $testroot/repo nonexistent.link > $testroot/stdout \
+		2> $testroot/stderr
+	ret="$?"
+	if [ "$ret" == "0" ]; then
+		echo "blame command succeeded unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "got: /nonexistent: no such entry found in tree" \
+		> $testroot/stderr.expected
+	cmp -s $testroot/stderr.expected $testroot/stderr
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	test_done "$testroot" "$ret"
+}
+
 run_test test_blame_basic
 run_test test_blame_tag
 run_test test_blame_file_single_line
@@ -773,3 +893,4 @@ run_test test_blame_commit_subsumed
 run_test test_blame_blame_h
 run_test test_blame_added_on_branch
 run_test test_blame_submodule
+run_test test_blame_symlink
blob - 37110e98ad018c151bf660c62fd637570da2df3a
blob + 47ddd48ca251a920a6562265a2746e94e9df8ad5
--- tog/tog.c
+++ tog/tog.c
@@ -4499,6 +4499,7 @@ cmd_blame(int argc, char *argv[])
 	struct got_reflist_head refs;
 	struct got_worktree *worktree = NULL;
 	char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
+	char *link_target = NULL;
 	struct got_object_id *commit_id = NULL;
 	char *commit_id_str = NULL;
 	int ch;
@@ -4594,9 +4595,16 @@ cmd_blame(int argc, char *argv[])
 		error = got_error_from_errno("view_open");
 		goto done;
 	}
-	error = open_blame_view(view, in_repo_path, commit_id, &refs, repo);
+
+	error = got_object_resolve_symlinks(&link_target, in_repo_path,
+	    commit_id, repo);
 	if (error)
 		goto done;
+
+	error = open_blame_view(view, link_target ? link_target : in_repo_path,
+	    commit_id, &refs, repo);
+	if (error)
+		goto done;
 	if (worktree) {
 		/* Release work tree lock. */
 		got_worktree_close(worktree);
@@ -4606,6 +4614,7 @@ cmd_blame(int argc, char *argv[])
 done:
 	free(repo_path);
 	free(in_repo_path);
+	free(link_target);
 	free(cwd);
 	free(commit_id);
 	if (worktree)