Commit Diff


commit - d7b899ab022b3f7c7a42ed1338efa4ad6be626c8
commit + f1bcca348e0d038b29d4d9bc6c87cbccf6893265
blob - b02072bccaf3d9b6fd8fbcc856402702d6307ba0
blob + 96e6346e20d7ac287590a46af4a3f785b7fc7f4c
--- got/got.c
+++ got/got.c
@@ -1501,7 +1501,42 @@ done:
 	free(new_id_str);
 	return err;
 }
+
+static const struct got_error *
+update_symref(const char *refname, struct got_reference *target_ref,
+    int verbosity, struct got_repository *repo)
+{
+	const struct got_error *err = NULL, *unlock_err;
+	struct got_reference *symref;
+
+	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;
 
+	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;
+	got_ref_close(symref);
+	return err;
+	return NULL;
+}
+
 __dead static void
 usage_fetch(void)
 {
@@ -1895,8 +1930,58 @@ cmd_fetch(int argc, char *argv[])
 			}
 		}
 	}
-	if (delete_refs)
+	if (delete_refs) {
 		error = delete_missing_refs(&refs, verbosity, repo);
+		if (error)
+			goto done;
+	}
+
+	if (!remote->mirror_references) {
+		/* Update remote HEAD reference if the server provided one. */
+		TAILQ_FOREACH(pe, &symrefs, entry) {
+			struct got_reference *target_ref;
+			const char *refname = pe->path;
+			const char *target = pe->data;
+			char *remote_refname = NULL, *remote_target = NULL;
+
+			if (strcmp(refname, GOT_REF_HEAD) != 0)
+				continue;
+
+			if (strncmp("refs/heads/", target, 11) != 0)
+				continue;
+
+			if (asprintf(&remote_refname, "refs/remotes/%s/%s",
+			    remote->name, refname) == -1) {
+				error = got_error_from_errno("asprintf");
+				goto done;
+			}
+			if (asprintf(&remote_target, "refs/remotes/%s/%s",
+			    remote->name, target + 11) == -1) {
+				error = got_error_from_errno("asprintf");
+				free(remote_refname);
+				goto done;
+			}
+
+			error = got_ref_open(&target_ref, repo, remote_target,
+			    0);
+			if (error) {
+				free(remote_refname);
+				free(remote_target);
+				if (error->code == GOT_ERR_NOT_REF) {
+					error = NULL;
+					continue;
+				}
+				goto done;
+			}
+			error = update_symref(remote_refname, target_ref,
+			    verbosity, repo);
+			free(remote_refname);
+			free(remote_target);
+			got_ref_close(target_ref);
+			if (error)
+				goto done;
+		}
+	}
 done:
 	if (fetchpid > 0) {
 		if (kill(fetchpid, SIGTERM) == -1)
blob - 404c994afe6baff215d08dc8f14a1f9437434dac
blob + e4a422fced01a3e564e433af5872e46fb304c6af
--- regress/cmdline/fetch.sh
+++ regress/cmdline/fetch.sh
@@ -207,7 +207,7 @@ function test_fetch_branch {
 	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
 	echo "refs/heads/foo: $commit_id3" >> $testroot/stdout.expected
 	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
-	echo "refs/remotes/origin/HEAD: refs/remotes/origin/master" \
+	echo "refs/remotes/origin/HEAD: refs/remotes/origin/foo" \
 		>> $testroot/stdout.expected
 	echo "refs/remotes/origin/foo: $commit_id3" >> $testroot/stdout.expected
 	# refs/remotes/origin/master is umodified because it wasn't fetched
@@ -247,7 +247,7 @@ function test_fetch_branch {
 	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
 	echo "refs/heads/foo: $commit_id3" >> $testroot/stdout.expected
 	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
-	echo "refs/remotes/origin/HEAD: refs/remotes/origin/master" \
+	echo "refs/remotes/origin/HEAD: refs/remotes/origin/foo" \
 		>> $testroot/stdout.expected
 	echo "refs/remotes/origin/foo: $commit_id3" >> $testroot/stdout.expected
 	echo "refs/remotes/origin/master: $commit_id2" \
@@ -813,12 +813,81 @@ function test_fetch_replace_symref {
 	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
 	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
 	echo "refs/hoo/boo/zoo: $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"
+
+}
+
+function test_fetch_update_headref {
+	local testroot=`test_init fetch_update_headref`
+	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 -c refs/heads/master refs/heads/foo
+	got ref -r $testroot/repo -s refs/heads/foo HEAD
+	got ref -l -r $testroot/repo > $testroot/stdout
+
+	echo "HEAD: refs/heads/foo" > $testroot/stdout.expected
+	echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected
+	echo "refs/heads/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 fetch -q -r $testroot/repo-clone
+
+	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/heads/master: $commit_id" >> $testroot/stdout.expected
+	echo "refs/remotes/origin/HEAD: refs/remotes/origin/foo" \
+		>> $testroot/stdout.expected
+	echo "refs/remotes/origin/foo: $commit_id" \
+		>> $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"
 
 }
@@ -832,3 +901,4 @@ run_test test_fetch_delete_branch
 run_test test_fetch_update_tag
 run_test test_fetch_reference
 run_test test_fetch_replace_symref
+run_test test_fetch_update_headref