"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
Re: send "unreachable" tags fails
To:
Omar Polo <op@omarpolo.com>
Cc:
gameoftrees@openbsd.org
Date:
Tue, 14 Sep 2021 11:49:47 +0200

Download raw body.

Thread
On Tue, Sep 14, 2021 at 09:45:49AM +0200, Omar Polo wrote:
> Hello,
> 
> `got send' fails to send tags that were tagged on deleted branches.  I
> don't know how widespread it is but for a couple of projects I'm used to
> tag bugfix releases by branching off the last release, cherrypicking the
> changes, tag the minor release and delete the branch.  I understand that
> probably is not a workflow intended for got, and that I'm not in the
> targeted audience, but anyway.
> 
> Here's how to reproduce.  I'm using a "testing" repo over at my site,
> but any repository would do

Thank you, Omar! Below is a regression test for this issue, and a fix.

There was an error in the load_tag() function: It didn't load any commits
associated with the tag. So commits which were only reachable via a tag
were never sent to the server. The server refused the pack file because
the objects the tag was pointing at were missing. Any referenced objects
are required to either already exist server-side or be shipped along in
the pack file.

diff 824d5f776c8195a3faa8c0b167888dbb99542482 /home/stsp/src/got
blob - 4ab44ac470c99d3b1842ebaef994127428153e04
file + lib/pack_create.c
--- lib/pack_create.c
+++ lib/pack_create.c
@@ -527,7 +527,7 @@ load_tag(struct got_pack_metavec *v, struct got_object
 
 	switch (got_object_tag_get_object_type(tag)) {
 	case GOT_OBJ_TYPE_COMMIT:
-		err = load_commit(NULL, idset,
+		err = load_commit(v, idset,
 		    got_object_tag_get_object_id(tag), repo,
 		    loose_obj_only, cancel_cb, cancel_arg);
 		break;
blob - ce7b085beed403260f19800f8e25f6d484e5e1d5
file + regress/cmdline/send.sh
--- regress/cmdline/send.sh
+++ regress/cmdline/send.sh
@@ -723,6 +723,124 @@ EOF
 	test_done "$testroot" "$ret"
 }
 
+test_send_tag_of_deleted_branch() {
+	local testroot=`test_init send_tag_of_deleted_branch`
+	local testurl=ssh://127.0.0.1/$testroot
+	local commit_id=`git_show_head $testroot/repo`
+
+	got clone -q $testurl/repo $testroot/repo-clone
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "got clone command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+	cat > $testroot/repo/.git/got.conf <<EOF
+remote "origin" {
+	protocol ssh
+	server 127.0.0.1
+	repository "$testroot/repo-clone"
+}
+EOF
+	got branch -r $testroot/repo foo
+
+	# modify alpha on branch foo
+	got checkout -b foo $testroot/repo $testroot/wt > /dev/null
+	echo boo >> $testroot/wt/beta
+	(cd $testroot/wt && got commit -m 'changed beta on branch foo' \
+		> /dev/null)
+	local commit_id2=`git_show_branch_head $testroot/repo foo`
+
+	# tag HEAD commit of branch foo
+	got tag -r $testroot/repo -c foo -m '1.0' 1.0 > /dev/null
+	tag_id=`got ref -r $testroot/repo -l | grep "^refs/tags/1.0" \
+		| tr -d ' ' | cut -d: -f2`
+
+	# delete the branch; commit is now only reachable via tags/1.0
+	got branch -r $testroot/repo -d foo > /dev/null
+
+	# unrelated change on master branch, then try sending this branch
+	# and the tag
+	echo "modified alpha" > $testroot/repo/alpha
+	git_commit $testroot/repo -m "modified alpha"
+	local commit_id3=`git_show_head $testroot/repo`
+
+	got send -q -r $testroot/repo -T > $testroot/stdout 2> $testroot/stderr
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "got send command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+	
+	echo -n > $testroot/stdout.expected
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	got ref -l -r $testroot/repo > $testroot/stdout
+	if [ "$ret" != "0" ]; then
+		echo "got ref command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	wt_uuid=`(cd $testroot/wt && got info | grep 'UUID:' | \
+		cut -d ':' -f 2 | tr -d ' ')`
+	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
+	echo "refs/got/worktree/base-$wt_uuid: $commit_id2" \
+		>> $testroot/stdout.expected
+	echo "refs/heads/master: $commit_id3" >> $testroot/stdout.expected
+	echo "refs/remotes/origin/master: $commit_id3" \
+		>> $testroot/stdout.expected
+	echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	got ref -l -r $testroot/repo-clone > $testroot/stdout
+	if [ "$ret" != "0" ]; then
+		echo "got ref command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
+	echo "refs/heads/master: $commit_id3" >> $testroot/stdout.expected
+	echo "refs/remotes/origin/HEAD: refs/remotes/origin/master" \
+		>> $testroot/stdout.expected
+	echo "refs/remotes/origin/master: $commit_id" \
+		>> $testroot/stdout.expected
+	echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	got tag -l -r $testroot/repo-clone | grep ^tag | sort > $testroot/stdout
+	echo "tag 1.0 $tag_id" > $testroot/stdout.expected
+
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 test_send_new_branch() {
 	local testroot=`test_init send_new_branch`
 	local testurl=ssh://127.0.0.1/$testroot
@@ -1241,6 +1359,7 @@ run_test test_send_rebase_required_overwrite
 run_test test_send_delete
 run_test test_send_clone_and_send
 run_test test_send_tags
+run_test test_send_tag_of_deleted_branch
 run_test test_send_new_branch
 run_test test_send_all_branches
 run_test test_send_to_empty_repo