"GOT", but the "O" is a cute, smiling sun Index | Thread

From:
Stefan Sperling <stsp@stsp.name>
Subject:
Re: fix cloning of repositories with dangling head reference
To:
Omar Polo <op@omarpolo.com>
Cc:
gameoftrees@openbsd.org
Date:
Tue, 8 Nov 2022 17:25:39 +0100

Download raw body.

On Tue, Nov 08, 2022 at 05:01:35PM +0100, Omar Polo wrote:
> > @@ -1881,6 +1882,20 @@ cmd_clone(int argc, char *argv[])
> >  				goto done;
> >  			break;
> >  		}
> > +
> > +		if (!fpa.configs_created && target_ref) {
> 
> Reached this point target_ref is either NULL or a free'd pointer.
> It's fine; the integral number doesn't change after passing it to
> got_ref_close, but I just felt to pointing it out.

Oh, indeed.

What we want is to ensure that a known branch exist that will end up
being written into the config file. It might be better to abuse 'pe'
as a flag, see below. Less risk of future changes starting to pass the
variable around after it's been freed.

> > +	got ref -l -r $testroot/repo-clone > $testroot/stdout
> > +
> > +	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
> > +	echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected
> > +	echo "refs/remotes/origin/foo: $commit_id" >> $testroot/stdout.expected
> 
> forgot to cmp stdout and stdout.expected

Good catch, also fixed below.

diff b43d5a6cd3a253c93c9cc25574997ac4aac7a4f9 cf5e25eb694afb46dddc7e27ef35da2117a0a45f
commit - b43d5a6cd3a253c93c9cc25574997ac4aac7a4f9
commit + cf5e25eb694afb46dddc7e27ef35da2117a0a45f
blob - c0c38ea47dfbd657ee0c14d0e8829767ad691ae6
blob + b444c533995567c4ece649cbcf950cd7f365361e
--- got/got.c
+++ got/got.c
@@ -1881,6 +1881,20 @@ cmd_clone(int argc, char *argv[])
 				goto done;
 			break;
 		}
+
+		if (!fpa.configs_created && pe != NULL) {
+			error = create_config_files(fpa.config_info.proto,
+			    fpa.config_info.host, fpa.config_info.port,
+			    fpa.config_info.remote_repo_path,
+			    fpa.config_info.git_url,
+			    fpa.config_info.fetch_all_branches,
+			    fpa.config_info.mirror_references,
+			    fpa.config_info.symrefs,
+			    fpa.config_info.wanted_branches,
+			    fpa.config_info.wanted_refs, fpa.repo);
+			if (error)
+				goto done;
+		}
 	}
 
 	if (verbosity >= 0)
blob - 6cac7e61e2407f2c053f4cf8256c67ae466489a6
blob + 4fa55782d6cfa72a92ffedfb310e3ac0e508e55c
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
@@ -373,7 +373,8 @@ fetch_pack(int fd, int packfd, uint8_t *pack_sha1,
 					break;
 				}
 			}
-			continue;
+			if (default_branch)
+				continue;
 		}
 		if (strstr(refname, "^{}")) {
 			if (chattygot) {
blob - 35d7c0ba41e646078b5beea834a05d4d28891363
blob + 6881cc1cec543e7bfd4986adaba1e99620dfeabd
--- regress/cmdline/clone.sh
+++ regress/cmdline/clone.sh
@@ -754,6 +754,87 @@ test_parseargs "$@"
 	test_done "$testroot" "$ret"
 }
 
+test_clone_dangling_headref() {
+	local testroot=`test_init clone_dangling_headref`
+	local testurl=ssh://127.0.0.1/$testroot
+	local commit_id=`git_show_head $testroot/repo`
+
+	got branch -r $testroot/repo -d master > /dev/null
+	got branch -r $testroot/repo -c $commit_id foo
+
+	got ref -l -r $testroot/repo > $testroot/stdout
+
+	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
+	echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected
+	# refs/heads/master is missing because it was deleted above
+
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	got clone -q -b foo $testurl/repo $testroot/repo-clone
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "got clone command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	got ref -l -r $testroot/repo-clone > $testroot/stdout
+
+	echo "HEAD: refs/heads/foo" > $testroot/stdout.expected
+	echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected
+	echo "refs/remotes/origin/foo: $commit_id" >> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	cat > $testroot/got.conf.expected <<EOF
+remote "origin" {
+	server 127.0.0.1
+	protocol ssh
+	repository "$testroot/repo"
+	branch { "foo" }
+}
+EOF
+	cmp -s $testroot/repo-clone/got.conf $testroot/got.conf.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/got.conf.expected \
+			$testroot/repo-clone/got.conf
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	cat > $testroot/config.expected <<EOF
+[core]
+	repositoryformatversion = 0
+	filemode = true
+	bare = true
+
+[remote "origin"]
+	url = ssh://127.0.0.1$testroot/repo
+	fetch = refs/heads/foo:refs/remotes/origin/foo
+	fetch = refs/tags/*:refs/tags/*
+EOF
+	cmp -s $testroot/repo-clone/config $testroot/config.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/config.expected \
+			$testroot/repo-clone/config
+	fi
+	test_done "$testroot" "$ret"
+}
+
 test_parseargs "$@"
 run_test test_clone_basic
 run_test test_clone_list
@@ -765,3 +846,4 @@ run_test test_clone_multiple_branches
 run_test test_clone_branch_and_reference
 run_test test_clone_reference_mirror
 run_test test_clone_multiple_branches
+run_test test_clone_dangling_headref