Commit Diff


commit - 14ed98fdf951d01d640d17b7ef18d4a9a3b761a7
commit + 8d725ae11ecc53e7d7f7bc3b576f7e949d08b980
blob - d7a5a560a572e11ce47c89a95d7c14bc30483ee8
blob + a1b4bd57e86473bd5bd845a4ee2c6601b8df3430
--- lib/blame.c
+++ lib/blame.c
@@ -40,7 +40,6 @@
 struct got_blame_line {
 	int annotated;
 	struct got_object_id id;
-	off_t offset;
 };
 
 struct got_blame_diff_offsets {
@@ -188,16 +187,22 @@ blame_changes(struct got_blame *blame, struct got_diff
 
 static const struct got_error *
 blame_commit(struct got_blame *blame, struct got_object_id *id,
-    struct got_object_id *pid, const char *path, struct got_repository *repo,
+    const char *path, struct got_repository *repo,
     const struct got_error *(*cb)(void *, int, int, struct got_object_id *),
     void *arg)
 {
 	const struct got_error *err = NULL;
 	struct got_object *obj = NULL, *pobj = NULL;
 	struct got_object_id *obj_id = NULL, *pobj_id = NULL;
+	struct got_commit_object *commit = NULL;
 	struct got_blob_object *blob = NULL, *pblob = NULL;
 	struct got_diff_changes *changes = NULL;
+	struct got_object_qid *pid = NULL;
 
+	err = got_object_open_as_commit(&commit, repo, id);
+	if (err)
+		return err;
+
 	err = got_object_id_by_path(&obj_id, repo, id, path);
 	if (err)
 		goto done;
@@ -211,8 +216,9 @@ blame_commit(struct got_blame *blame, struct got_objec
 		goto done;
 	}
 
+	pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
 	if (pid) {
-		err = got_object_id_by_path(&pobj_id, repo, pid, path);
+		err = got_object_id_by_path(&pobj_id, repo, pid->id, path);
 		if (err) {
 			if (err->code == GOT_ERR_NO_TREE_ENTRY) {
 				/* Blob's history began in previous commit. */
@@ -260,6 +266,8 @@ blame_commit(struct got_blame *blame, struct got_objec
 	} else if (cb)
 		err = cb(arg, blame->nlines, -1, id);
 done:
+	if (commit)
+		got_object_commit_close(commit);
 	free(obj_id);
 	free(pobj_id);
 	if (obj)
@@ -302,7 +310,7 @@ blame_open(struct got_blame **blamep, const char *path
 	struct got_object_id *obj_id = NULL;
 	struct got_blob_object *blob = NULL;
 	struct got_blame *blame = NULL;
-	struct got_object_id *id = NULL, *parent_id = NULL;
+	struct got_object_id *id = NULL, *next_id = NULL;
 	int lineno;
 	struct got_commit_graph *graph = NULL;
 
@@ -345,7 +353,7 @@ blame_open(struct got_blame **blamep, const char *path
 		goto done;
 	}
 
-	err = got_commit_graph_open(&graph, start_commit_id, path, 0, repo);
+	err = got_commit_graph_open(&graph, start_commit_id, path, 1, repo);
 	if (err)
 		return err;
 	err = got_commit_graph_iter_start(graph, start_commit_id, repo);
@@ -354,12 +362,12 @@ blame_open(struct got_blame **blamep, const char *path
 
 	id = NULL;
 	for (;;) {
-		err = got_commit_graph_iter_next(&parent_id, graph);
+		err = got_commit_graph_iter_next(&next_id, graph);
 		if (err) {
 			if (err->code == GOT_ERR_ITER_COMPLETED) {
 				if (id)
 					err = blame_commit(blame, id,
-					    parent_id, path, repo, cb, arg);
+					    path, repo, cb, arg);
 				else
 					err = NULL;
 				break;
@@ -372,7 +380,7 @@ blame_open(struct got_blame **blamep, const char *path
 			continue;
 		}
 		if (id) {
-			err = blame_commit(blame, id, parent_id, path, repo,
+			err = blame_commit(blame, id, path, repo,
 			    cb, arg);
 			if (err) {
 				if (err->code == GOT_ERR_ITER_COMPLETED)
@@ -382,7 +390,7 @@ blame_open(struct got_blame **blamep, const char *path
 			if (blame->nannotated == blame->nlines)
 				break;
 		}
-		id = parent_id;
+		id = next_id;
 	}
 
 	if (id && blame->nannotated < blame->nlines) {
blob - 60ecdd0c932486f6f73a0a6a29d9048339d48eae
blob + 66015530063440d784e2152767420965b157b0de
--- regress/cmdline/blame.sh
+++ regress/cmdline/blame.sh
@@ -161,7 +161,342 @@ function test_blame_file_single_line_no_newline {
 	test_done "$testroot" "$ret"
 }
 
+function test_blame_all_lines_replaced {
+	local testroot=`test_init blame_all_lines_replaced`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	jot 8 > $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 1" > /dev/null)
+	local commit1=`git_show_head $testroot/repo`
+	local short_commit1=`trim_obj_id 32 $commit1`
+	local author_time=`git_show_author_time $testroot/repo`
+
+	(cd $testroot/wt && got blame alpha > $testroot/stdout)
+
+	d=`date -r $author_time +"%g/%m/%d"`
+	echo "1) $short_commit1 $d $GOT_AUTHOR_8 1" > $testroot/stdout.expected
+	echo "2) $short_commit1 $d $GOT_AUTHOR_8 2" >> $testroot/stdout.expected
+	echo "3) $short_commit1 $d $GOT_AUTHOR_8 3" >> $testroot/stdout.expected
+	echo "4) $short_commit1 $d $GOT_AUTHOR_8 4" >> $testroot/stdout.expected
+	echo "5) $short_commit1 $d $GOT_AUTHOR_8 5" >> $testroot/stdout.expected
+	echo "6) $short_commit1 $d $GOT_AUTHOR_8 6" >> $testroot/stdout.expected
+	echo "7) $short_commit1 $d $GOT_AUTHOR_8 7" >> $testroot/stdout.expected
+	echo "8) $short_commit1 $d $GOT_AUTHOR_8 8" >> $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_blame_lines_shifted_up {
+	local testroot=`test_init blame_lines_shifted_up`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	jot 8 > $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 1" > /dev/null)
+	local commit1=`git_show_head $testroot/repo`
+	local short_commit1=`trim_obj_id 32 $commit1`
+	local author_time=`git_show_author_time $testroot/repo`
+
+	sed -i -e '/^[345]$/d' $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 2" > /dev/null)
+	local commit2=`git_show_head $testroot/repo`
+	local short_commit2=`trim_obj_id 32 $commit2`
+
+	jot 2 > $testroot/wt/alpha
+	echo foo >> $testroot/wt/alpha
+	echo bar >> $testroot/wt/alpha
+	echo baz >> $testroot/wt/alpha
+	jot 8 6 8 1 >> $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 3" > /dev/null)
+	local commit3=`git_show_head $testroot/repo`
+	local short_commit3=`trim_obj_id 32 $commit3`
+	local author_time=`git_show_author_time $testroot/repo`
+
+	(cd $testroot/wt && got blame alpha > $testroot/stdout)
+
+	d=`date -r $author_time +"%g/%m/%d"`
+	echo "1) $short_commit1 $d $GOT_AUTHOR_8 1" > $testroot/stdout.expected
+	echo "2) $short_commit1 $d $GOT_AUTHOR_8 2" >> $testroot/stdout.expected
+	echo "3) $short_commit3 $d $GOT_AUTHOR_8 foo" >> $testroot/stdout.expected
+	echo "4) $short_commit3 $d $GOT_AUTHOR_8 bar" >> $testroot/stdout.expected
+	echo "5) $short_commit3 $d $GOT_AUTHOR_8 baz" >> $testroot/stdout.expected
+	echo "6) $short_commit1 $d $GOT_AUTHOR_8 6" >> $testroot/stdout.expected
+	echo "7) $short_commit1 $d $GOT_AUTHOR_8 7" >> $testroot/stdout.expected
+	echo "8) $short_commit1 $d $GOT_AUTHOR_8 8" >> $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_blame_lines_shifted_down {
+	local testroot=`test_init blame_lines_shifted_down`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	jot 8 > $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 1" > /dev/null)
+	local commit1=`git_show_head $testroot/repo`
+	local short_commit1=`trim_obj_id 32 $commit1`
+	local author_time=`git_show_author_time $testroot/repo`
+
+	sed -i -e '/^8$/d' $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 2" > /dev/null)
+	local commit2=`git_show_head $testroot/repo`
+	local short_commit2=`trim_obj_id 32 $commit2`
+
+	jot 2 > $testroot/wt/alpha
+	echo foo >> $testroot/wt/alpha
+	echo bar >> $testroot/wt/alpha
+	echo baz >> $testroot/wt/alpha
+	jot 8 3 8 1 >> $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 3" > /dev/null)
+	local commit3=`git_show_head $testroot/repo`
+	local short_commit3=`trim_obj_id 32 $commit3`
+	local author_time=`git_show_author_time $testroot/repo`
+
+	(cd $testroot/wt && got blame alpha > $testroot/stdout)
+
+	d=`date -r $author_time +"%g/%m/%d"`
+	echo "01) $short_commit1 $d $GOT_AUTHOR_8 1" \
+		> $testroot/stdout.expected
+	echo "02) $short_commit1 $d $GOT_AUTHOR_8 2" \
+		>> $testroot/stdout.expected
+	echo "03) $short_commit3 $d $GOT_AUTHOR_8 foo" \
+		>> $testroot/stdout.expected
+	echo "04) $short_commit3 $d $GOT_AUTHOR_8 bar" \
+		>> $testroot/stdout.expected
+	echo "05) $short_commit3 $d $GOT_AUTHOR_8 baz" \
+		>> $testroot/stdout.expected
+	echo "06) $short_commit1 $d $GOT_AUTHOR_8 3" \
+		>> $testroot/stdout.expected
+	echo "07) $short_commit1 $d $GOT_AUTHOR_8 4" \
+		>> $testroot/stdout.expected
+	echo "08) $short_commit1 $d $GOT_AUTHOR_8 5" \
+		>> $testroot/stdout.expected
+	echo "09) $short_commit1 $d $GOT_AUTHOR_8 6" \
+		>> $testroot/stdout.expected
+	echo "10) $short_commit1 $d $GOT_AUTHOR_8 7" \
+		>> $testroot/stdout.expected
+	echo "11) $short_commit3 $d $GOT_AUTHOR_8 8" \
+		>> $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_blame_commit_subsumed {
+	local testroot=`test_init blame_commit_subsumed`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	cat > $testroot/wt/alpha <<EOF
+SUBDIRS = ext modules codedocs docs
+
+if WITH_PDNS_SERVER
+  SUBDIRS += pdns
+endif
+
+EXTRA_DIST =
+	INSTALL
+	NOTICE
+	README
+	.version
+	build-aux/gen-version
+	codedocs/doxygen.conf
+	contrib/powerdns.solaris.init.d
+	pdns/named.conf.parsertest
+	regression-tests/zones/unit.test
+
+ACLOCAL_AMFLAGS = -I m4
+
+dvi: # do nothing to build dvi
+EOF
+	(cd $testroot/wt && got commit -m "change 1" > /dev/null)
+	local commit1=`git_show_head $testroot/repo`
+	local short_commit1=`trim_obj_id 32 $commit1`
+	local author_time1=`git_show_author_time $testroot/repo`
+	local d1=`date -r $author_time1 +"%g/%m/%d"`
+
+	cat > $testroot/wt/alpha <<EOF
+SUBDIRS = ext modules codedocs docs
+
+SUBDIRS += pdns
+
+EXTRA_DIST =
+	INSTALL
+	NOTICE
+	README
+	.version
+	build-aux/gen-version
+	codedocs/doxygen.conf
+	contrib/powerdns.solaris.init.d
+	pdns/named.conf.parsertest
+	regression-tests/zones/unit.test
+
+ACLOCAL_AMFLAGS = -I m4
+
+dvi: # do nothing to build dvi
+EOF
+	# all changes in this commit will be subsumed by later commits
+	(cd $testroot/wt && got commit -m "change 2" > /dev/null)
+	local commit2=`git_show_head $testroot/repo`
+	local short_commit2=`trim_obj_id 32 $commit2`
+	local author_time2=`git_show_author_time $testroot/repo`
+	local d2=`date -r $author_time2 +"%g/%m/%d"`
+
+	cat > $testroot/wt/alpha <<EOF
+SUBDIRS = ext modules pdns codedocs docs
+
+EXTRA_DIST =
+	INSTALL
+	NOTICE
+	README
+	.version
+	build-aux/gen-version
+	codedocs/doxygen.conf
+	contrib/powerdns.solaris.init.d
+	pdns/named.conf.parsertest
+	regression-tests/zones/unit.test
+
+ACLOCAL_AMFLAGS = -I m4
+
+dvi: # do nothing to build dvi
+EOF
+	(cd $testroot/wt && got commit -m "change 3" > /dev/null)
+	local commit3=`git_show_head $testroot/repo`
+	local short_commit3=`trim_obj_id 32 $commit3`
+	local author_time3=`git_show_author_time $testroot/repo`
+	local d3=`date -r $author_time3 +"%g/%m/%d"`
+
+	cat > $testroot/wt/alpha <<EOF
+SUBDIRS = ext modules pdns codedocs docs
+
+EXTRA_DIST =
+	INSTALL
+	NOTICE
+	README
+	COPYING
+	codedocs/doxygen.conf
+	contrib/powerdns.solaris.init.d
+	pdns/named.conf.parsertest
+	regression-tests/zones/unit.test
+	builder-support/gen-version
+
+ACLOCAL_AMFLAGS = -I m4
+
+dvi: # do nothing to build dvi
+EOF
+	(cd $testroot/wt && got commit -m "change 4" > /dev/null)
+	local commit4=`git_show_head $testroot/repo`
+	local short_commit4=`trim_obj_id 32 $commit4`
+	local author_time4=`git_show_author_time $testroot/repo`
+	local d4=`date -r $author_time4 +"%g/%m/%d"`
+
+	(cd $testroot/wt && got blame alpha > $testroot/stdout)
+
+	echo -n "01) $short_commit3 $d3 $GOT_AUTHOR_8 " \
+		> $testroot/stdout.expected
+	echo "SUBDIRS = ext modules pdns codedocs docs" \
+		>> $testroot/stdout.expected
+	echo "02) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	echo -n "03) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	echo 'EXTRA_DIST =' >> $testroot/stdout.expected
+	echo -n "04) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tINSTALL\n" >> $testroot/stdout.expected
+	echo -n "05) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tNOTICE\n" >> $testroot/stdout.expected
+	echo -n "06) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tREADME\n"  >> $testroot/stdout.expected
+	echo -n "07) $short_commit4 $d4 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tCOPYING\n" >> $testroot/stdout.expected
+	echo -n "08) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tcodedocs/doxygen.conf\n" >> $testroot/stdout.expected
+	echo -n "09) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tcontrib/powerdns.solaris.init.d\n" \
+		>> $testroot/stdout.expected
+	echo -n "10) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tpdns/named.conf.parsertest\n" >> $testroot/stdout.expected
+	echo -n "11) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tregression-tests/zones/unit.test\n" \
+		>> $testroot/stdout.expected
+	echo -n "12) $short_commit4 $d4 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	printf "\tbuilder-support/gen-version\n" >> $testroot/stdout.expected
+	echo "13) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	echo -n "14) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	echo "ACLOCAL_AMFLAGS = -I m4" \
+		>> $testroot/stdout.expected
+	echo "15) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	echo -n "16) $short_commit1 $d1 $GOT_AUTHOR_8 " \
+		>> $testroot/stdout.expected
+	echo "dvi: # do nothing to build dvi" \
+		>> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		#diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "xfail: line 3 has wrong annotation"
+		return 1
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_blame_basic
 run_test test_blame_tag
 run_test test_blame_file_single_line
 run_test test_blame_file_single_line_no_newline
+run_test test_blame_all_lines_replaced
+run_test test_blame_lines_shifted_up
+run_test test_blame_lines_shifted_down
+run_test test_blame_commit_subsumed