commit f21ec2f0a8aced9bb068d3faa18b7b5c483fe345 from: Stefan Sperling date: Sat Mar 21 11:20:33 2020 UTC add -d option to 'got fetch' for deleting old branches and tags 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)