Commit Diff


commit - 02b79295e87a10a22d5f85028c86b7bdc97c227d
commit + 3789fd730ae3acb3dd47ddf9f5da50d1453afd71
blob - 96e6346e20d7ac287590a46af4a3f785b7fc7f4c
blob + 8c3efa9c087f5a9d3b959c3a6a8a27f3b3d73264
--- got/got.c
+++ got/got.c
@@ -1548,54 +1548,129 @@ usage_fetch(void)
 }
 
 static const struct got_error *
-delete_missing_refs(struct got_pathlist_head *their_refs,
+delete_missing_ref(struct got_reference *ref,
     int verbosity, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
+	struct got_object_id *id = NULL;
+	char *id_str = NULL;
+
+	if (got_ref_is_symbolic(ref)) {
+		err = got_ref_delete(ref, repo);
+		if (err)
+			return err;
+		if (verbosity >= 0) {
+			printf("Deleted reference %s: %s\n",
+			    got_ref_get_name(ref),
+			    got_ref_get_symref_target(ref));
+		}
+	} else {
+		err = got_ref_resolve(&id, repo, ref);
+		if (err)
+			return err;
+		err = got_object_id_str(&id_str, id);
+		if (err)
+			goto done;
+			
+		err = got_ref_delete(ref, repo);
+		if (err)
+			goto done;
+		if (verbosity >= 0) {
+			printf("Deleted reference %s: %s\n",
+			    got_ref_get_name(ref), id_str);
+		}
+	}
+done:
+	free(id);
+	free(id_str);
+	return NULL;
+}
+
+static const struct got_error *
+delete_missing_refs(struct got_pathlist_head *their_refs,
+    struct got_pathlist_head *their_symrefs, struct got_remote_repo *remote,
+    int verbosity, struct got_repository *repo)
+{
+	const struct got_error *err = NULL, *unlock_err;
 	struct got_reflist_head my_refs;
 	struct got_reflist_entry *re;
 	struct got_pathlist_entry *pe;
-	struct got_object_id *id;
-	char *id_str;
+	char *remote_namespace = NULL;
+	char *local_refname = NULL;
 
 	SIMPLEQ_INIT(&my_refs);
 
+	if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
+	    == -1)
+		return got_error_from_errno("asprintf");
+
 	err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
 	if (err)
-		return err;
+		goto done;
 
 	SIMPLEQ_FOREACH(re, &my_refs, entry) {
 		const char *refname = got_ref_get_name(re->ref);
 
-		if (strncmp(refname, "refs/heads/", 11) != 0 &&
-		    strncmp(refname, "refs/tags/", 10) != 0)
-			continue;
+		if (!remote->mirror_references) {
+			if (strncmp(refname, remote_namespace,
+			    strlen(remote_namespace)) == 0) {
+				if (strcmp(refname + strlen(remote_namespace),
+				    GOT_REF_HEAD) == 0)
+					continue;
+				if (asprintf(&local_refname, "refs/heads/%s",
+				    refname + strlen(remote_namespace)) == -1) {
+					err = got_error_from_errno("asprintf");
+					goto done;
+				}
+			} else if (strncmp(refname, "refs/tags/", 10) != 0)
+				continue;
+		}
 
 		TAILQ_FOREACH(pe, their_refs, entry) {
-			if (strcmp(refname, pe->path) == 0)
+			if (strcmp(local_refname, pe->path) == 0)
 				break;
 		}
 		if (pe != NULL)
 			continue;
 
-		err = got_ref_resolve(&id, repo, re->ref);
-		if (err)
-			break;
-		err = got_object_id_str(&id_str, id);
-		free(id);
-		if (err)
-			break;
+		TAILQ_FOREACH(pe, their_symrefs, entry) {
+			if (strcmp(local_refname, pe->path) == 0)
+				break;
+		}
+		if (pe != NULL)
+			continue;
 
-		free(id_str);
-		err = got_ref_delete(re->ref, repo);
+		err = delete_missing_ref(re->ref, verbosity, repo);
 		if (err)
 			break;
-		if (verbosity >= 0) {
-			printf("Deleted reference %s: %s\n",
-			    got_ref_get_name(re->ref), id_str);
+
+		if (local_refname) {
+			struct got_reference *ref;
+			err = got_ref_open(&ref, repo, local_refname, 1);
+			if (err) {
+				if (err->code != GOT_ERR_NOT_REF)
+					break;
+				free(local_refname);
+				local_refname = NULL;
+				continue;
+			}
+			err = delete_missing_ref(ref, verbosity, repo);
+			if (err)
+				break;
+			unlock_err = got_ref_unlock(ref);
+			got_ref_close(ref);
+			if (unlock_err && err == NULL) {
+				err = unlock_err;
+				break;
+			}
+
+			free(local_refname);
+			local_refname = NULL;
 		}
 	}
-
+done:
+	free(remote_namespace);
+	free(local_refname);
 	return err;
 }
 
@@ -1838,7 +1913,8 @@ cmd_fetch(int argc, char *argv[])
 		if (verbosity >= 0)
 			printf("Already up-to-date\n");
 		if (delete_refs)
-			error = delete_missing_refs(&refs, verbosity, repo);
+			error = delete_missing_refs(&refs, &symrefs,
+			    remote, verbosity, repo);
 		goto done;
 	}
 
@@ -1931,7 +2007,8 @@ cmd_fetch(int argc, char *argv[])
 		}
 	}
 	if (delete_refs) {
-		error = delete_missing_refs(&refs, verbosity, repo);
+		error = delete_missing_refs(&refs, &symrefs, remote,
+		    verbosity, repo);
 		if (error)
 			goto done;
 	}
blob - e4a422fced01a3e564e433af5872e46fb304c6af
blob + 4efa1f083052561dedf24eac5bef0786bc66a4bb
--- regress/cmdline/fetch.sh
+++ regress/cmdline/fetch.sh
@@ -495,7 +495,7 @@ function test_fetch_delete_branch {
 	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/foo: $commit_id" >> $testroot/stdout.expected
+	# refs/remotes/origin/foo is now deleted
 	echo "refs/remotes/origin/master: $commit_id" \
 		>> $testroot/stdout.expected
 	# refs/hoo/boo/zoo is missing because it is outside of refs/heads