Commit Diff


commit - 13f12b0969ed54d1f3c05e9083b6cf49b411c5a0
commit + f21ec2f0a8aced9bb068d3faa18b7b5c483fe345
blob - 9b734106ce0c02a0c08f7dceeb7d5ac834988511
blob + 36ad40d7a329ebda0af78195f82299a49ddbbe20
--- got/got.1
+++ got/got.1
@@ -272,7 +272,7 @@ The maximum is 3.
 .It Cm cl
 Short alias for
 .Cm clone .
-.It Cm fetch Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl l Oc Oo Fl r Ar repository-path Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository
+.It Cm fetch Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl d Oc Oo Fl l Oc Oo Fl r Ar repository-path Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository
 Fetch new changes from a remote repository.
 If no
 .Ar remote-repository
@@ -333,6 +333,13 @@ repository's HEAD reference will be fetched.
 Cannot be used together with the
 .Fl a
 option.
+.It Fl d
+Delete branches and tags from the local repository which are no longer
+present in the remote repository.
+Only references are deleted.
+Any commit, tree, and blob objects belonging to deleted branches or
+tags remain in the repository and may be removed separately with
+Git's garbage collector.
 .It Fl l
 List branches and tags available for fetching from the remote repository
 and exit immediately.
blob - 2a73dd37b739be4dd27bc440f7949e5232e35f59
blob + 631195ed466b3b6cc52495d65e7293b37818537c
--- got/got.c
+++ got/got.c
@@ -1373,13 +1373,62 @@ done:
 __dead static void
 usage_fetch(void)
 {
-	fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-l] "
+	fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-d] [-l] "
 	    "[-r repository-path] [-q] [-v] [remote-repository-name]\n",
 	    getprogname());
 	exit(1);
 }
 
 static const struct got_error *
+delete_missing_refs(struct got_pathlist_head *their_refs,
+    struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	struct got_reflist_head my_refs;
+	struct got_reflist_entry *re;
+	struct got_pathlist_entry *pe;
+	struct got_object_id *id;
+	char *id_str;
+
+	SIMPLEQ_INIT(&my_refs);
+
+	err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
+	if (err)
+		return err;
+
+	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;
+
+		TAILQ_FOREACH(pe, their_refs, entry) {
+			if (strcmp(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;
+
+		printf("Deleting %s: %s\n", got_ref_get_name(re->ref), id_str);
+		free(id_str);
+		err = got_ref_delete(re->ref, repo);
+		if (err)
+			break;
+	}
+
+	return err;
+}
+
+static const struct got_error *
 cmd_fetch(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
@@ -1399,12 +1448,13 @@ cmd_fetch(int argc, char *argv[])
 	pid_t fetchpid = -1;
 	struct got_fetch_progress_arg fpa;
 	int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
+	int delete_refs = 0;
 
 	TAILQ_INIT(&refs);
 	TAILQ_INIT(&symrefs);
 	TAILQ_INIT(&wanted_branches);
 
-	while ((ch = getopt(argc, argv, "ab:lr:vq")) != -1) {
+	while ((ch = getopt(argc, argv, "ab:dlr:vq")) != -1) {
 		switch (ch) {
 		case 'a':
 			fetch_all_branches = 1;
@@ -1415,6 +1465,9 @@ cmd_fetch(int argc, char *argv[])
 			if (error)
 				return error;
 			break;
+		case 'd':
+			delete_refs = 1;
+			break;
 		case 'l':
 			list_refs_only = 1;
 			break;
@@ -1449,6 +1502,8 @@ cmd_fetch(int argc, char *argv[])
 			errx(1, "-l and -b options are mutually exclusive");
 		if (fetch_all_branches)
 			errx(1, "-l and -a options are mutually exclusive");
+		if (delete_refs)
+			errx(1, "-l and -d options are mutually exclusive");
 		if (verbosity == -1)
 			errx(1, "-l and -q options are mutually exclusive");
 	}
@@ -1568,6 +1623,8 @@ 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, repo);
 		goto done;
 	}
 
@@ -1642,6 +1699,8 @@ cmd_fetch(int argc, char *argv[])
 		free(id_str);
 		id_str = NULL;
 	}
+	if (delete_refs)
+		error = delete_missing_refs(&refs, repo);
 done:
 	if (fetchpid > 0) {
 		if (kill(fetchpid, SIGTERM) == -1)