Commit Diff


commit - 3789fd730ae3acb3dd47ddf9f5da50d1453afd71
commit + bcf34b0ec978d055514a2759ed37d7b8a5f4ea08
blob - 8c3efa9c087f5a9d3b959c3a6a8a27f3b3d73264
blob + 69c9927afc8fdb351891da171a6ce68595a56e05
--- got/got.c
+++ got/got.c
@@ -1508,33 +1508,54 @@ update_symref(const char *refname, struct got_referenc
 {
 	const struct got_error *err = NULL, *unlock_err;
 	struct got_reference *symref;
+	int symref_is_locked = 0;
 
 	err = got_ref_open(&symref, repo, refname, 1);
-	if (err)
-		return err;
-
-	if (strcmp(got_ref_get_symref_target(symref),
-	    got_ref_get_name(target_ref)) == 0)
-		goto done;
+	if (err) {
+		if (err->code != GOT_ERR_NOT_REF)
+			return err;
+		err = got_ref_alloc_symref(&symref, refname, target_ref);
+		if (err)
+			goto done;
 
-	err = got_ref_change_symref(symref, got_ref_get_name(target_ref));
-	if (err)
-		goto done;
+		err = got_ref_write(symref, repo);
+		if (err)
+			goto done;
 
-	err = got_ref_write(symref, repo);
-	if (err)
-		goto done;
+		if (verbosity >= 0)
+			printf("Created reference %s: %s\n",
+			    got_ref_get_name(symref),
+			    got_ref_get_symref_target(symref));
+	} else {
+		symref_is_locked = 1;
 
-	if (verbosity >= 0)
-		printf("Updated reference %s: %s\n", got_ref_get_name(symref),
-		    got_ref_get_symref_target(symref));
+		if (strcmp(got_ref_get_symref_target(symref),
+		    got_ref_get_name(target_ref)) == 0)
+			goto done;
+
+		err = got_ref_change_symref(symref,
+		    got_ref_get_name(target_ref));
+		if (err)
+			goto done;
+
+		err = got_ref_write(symref, repo);
+		if (err)
+			goto done;
+
+		if (verbosity >= 0)
+			printf("Updated reference %s: %s\n",
+			    got_ref_get_name(symref),
+			    got_ref_get_symref_target(symref));
+
+	}
 done:
-	unlock_err = got_ref_unlock(symref);
-	if (unlock_err && err == NULL)
-		err = unlock_err;
+	if (symref_is_locked) {
+		unlock_err = got_ref_unlock(symref);
+		if (unlock_err && err == NULL)
+			err = unlock_err;
+	}
 	got_ref_close(symref);
 	return err;
-	return NULL;
 }
 
 __dead static void
@@ -1912,13 +1933,7 @@ cmd_fetch(int argc, char *argv[])
 	if (pack_hash == NULL) {
 		if (verbosity >= 0)
 			printf("Already up-to-date\n");
-		if (delete_refs)
-			error = delete_missing_refs(&refs, &symrefs,
-			    remote, verbosity, repo);
-		goto done;
-	}
-
-	if (verbosity >= 0) {
+	} else if (verbosity >= 0) {
 		error = got_object_id_str(&id_str, pack_hash);
 		if (error)
 			goto done;
blob - 4efa1f083052561dedf24eac5bef0786bc66a4bb
blob + d46a15108d913f2686fdf20a1cf6fbe75e121e1e
--- regress/cmdline/fetch.sh
+++ regress/cmdline/fetch.sh
@@ -889,7 +889,63 @@ function test_fetch_update_headref {
 		diff -u $testroot/stdout.expected $testroot/stdout
 	fi
 	test_done "$testroot" "$ret"
+}
+
+function test_fetch_headref_deleted_locally {
+	local testroot=`test_init fetch_headref_deleted_locally`
+	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
+
+	got ref -l -r $testroot/repo-clone > $testroot/stdout
 
+	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
+	echo "refs/heads/master: $commit_id" >> $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
+
+	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 -r $testroot/repo-clone -d refs/remotes/origin/HEAD
+
+	got fetch -q -r $testroot/repo-clone
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "got fetch command failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+	got ref -l -r $testroot/repo-clone > $testroot/stdout
+
+	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
+	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
+	# refs/remotes/origin/HEAD has been restored:
+	echo "refs/remotes/origin/HEAD: refs/remotes/origin/master" \
+		>> $testroot/stdout.expected
+	echo "refs/remotes/origin/master: $commit_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"
 }
 
 run_test test_fetch_basic
@@ -902,3 +958,4 @@ run_test test_fetch_update_tag
 run_test test_fetch_reference
 run_test test_fetch_replace_symref
 run_test test_fetch_update_headref
+run_test test_fetch_headref_deleted_locally