commit - da630daa54557a42066707f18f070393197a0243
commit + 161728eb26bf63ad53f11367358ea6190bad8968
blob - 64eb89a0f0d09c013a42191b548de64e1ecc96c7
blob + 2fc788135c449d339f9e22c8ac5d28eaf3bb8047
--- got/got.1
+++ got/got.1
.It Cm cl
Short alias for
.Cm clone .
-.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 t Oc Oo Fl q Oc Oo Fl v Oc Oo Fl R Ar reference 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 t Oc Oo Fl q Oc Oo Fl v Oc Oo Fl R Ar reference Oc Oo Fl X Oc Op Ar remote-repository
Fetch new changes from a remote repository.
If no
.Ar remote-repository
or
.Dq refs/got/
namespace.
+.It Fl X
+Delete all references which correspond to a particular
+.Ar remote-repository
+instead of fetching new changes.
+This can be useful when a remote repository is being removed from
+.Xr got.conf 5 .
+.Pp
+With
+.Fl X ,
+the
+.Ar remote-repository
+argument is mandatory and no other options except
+.Fl r ,
+.Fl v ,
+and
+.Fl q
+are allowed.
+.Pp
+Only references are deleted.
+Any commit, tree, tag, and blob objects fetched from a remote repository
+will generally be stored in pack files and may be removed separately with
+.Xr git-repack 1
+and Git's garbage collector.
.El
.It Cm fe
Short alias for
blob - f4cf4ebbb4c63cb23b1bfd3b1d55c216a3e8951b
blob + eead62c4d2989b648729acc4857033da98231ab3
--- got/got.c
+++ got/got.c
usage_fetch(void)
{
fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-d] [-l] "
- "[-r repository-path] [-t] [-q] [-v] [-R reference] "
+ "[-r repository-path] [-t] [-q] [-v] [-R reference] [-X] "
"[remote-repository-name]\n",
getprogname());
exit(1);
}
static const struct got_error *
+delete_ref(struct got_repository *repo, struct got_reference *ref)
+{
+ const struct got_error *err = NULL;
+ struct got_object_id *id = NULL;
+ char *id_str = NULL;
+ const char *target;
+
+ if (got_ref_is_symbolic(ref)) {
+ target = got_ref_get_symref_target(ref);
+ } else {
+ err = got_ref_resolve(&id, repo, ref);
+ if (err)
+ goto done;
+ err = got_object_id_str(&id_str, id);
+ if (err)
+ goto done;
+ target = id_str;
+ }
+
+ err = got_ref_delete(ref, repo);
+ if (err)
+ goto done;
+
+ printf("Deleted %s: %s\n", got_ref_get_name(ref), target);
+done:
+ free(id);
+ free(id_str);
+ return err;
+}
+
+static const struct got_error *
+delete_refs_for_remote(struct got_repository *repo, const char *remote_name)
+{
+ const struct got_error *err = NULL;
+ struct got_reflist_head refs;
+ struct got_reflist_entry *re;
+ char *prefix;
+
+ TAILQ_INIT(&refs);
+
+ if (asprintf(&prefix, "refs/remotes/%s", remote_name) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ err = got_ref_list(&refs, repo, prefix, got_ref_cmp_by_name, NULL);
+ if (err)
+ goto done;
+
+ TAILQ_FOREACH(re, &refs, entry)
+ delete_ref(repo, re->ref);
+done:
+ got_ref_list_free(&refs);
+ return err;
+}
+
+static const struct got_error *
cmd_fetch(int argc, char *argv[])
{
const struct got_error *error = NULL, *unlock_err;
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, replace_tags = 0;
+ int delete_refs = 0, replace_tags = 0, delete_remote = 0;
TAILQ_INIT(&refs);
TAILQ_INIT(&symrefs);
TAILQ_INIT(&wanted_branches);
TAILQ_INIT(&wanted_refs);
- while ((ch = getopt(argc, argv, "ab:dlr:tvqR:")) != -1) {
+ while ((ch = getopt(argc, argv, "ab:dlr:tvqR:X")) != -1) {
switch (ch) {
case 'a':
fetch_all_branches = 1;
if (error)
return error;
break;
+ case 'X':
+ delete_remote = 1;
+ break;
default:
usage_fetch();
break;
option_conflict('l', 'a');
if (delete_refs)
option_conflict('l', 'd');
+ if (delete_remote)
+ option_conflict('l', 'X');
}
+ if (delete_remote) {
+ if (fetch_all_branches)
+ option_conflict('X', 'a');
+ if (!TAILQ_EMPTY(&wanted_branches))
+ option_conflict('X', 'b');
+ if (delete_refs)
+ option_conflict('X', 'd');
+ if (replace_tags)
+ option_conflict('X', 't');
+ if (!TAILQ_EMPTY(&wanted_refs))
+ option_conflict('X', 'R');
+ }
- if (argc == 0)
+ if (argc == 0) {
+ if (delete_remote)
+ errx(1, "-X option requires a remote name");
remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
- else if (argc == 1)
+ } else if (argc == 1)
remote_name = argv[0];
else
usage_fetch();
if (error)
goto done;
+ if (delete_remote) {
+ error = delete_refs_for_remote(repo, remote_name);
+ goto done; /* nothing else to do */
+ }
+
if (worktree) {
worktree_conf = got_worktree_get_gotconfig(worktree);
if (worktree_conf) {
}
static const struct got_error *
-delete_ref(struct got_repository *repo, const char *refname)
+delete_ref_by_name(struct got_repository *repo, const char *refname)
{
- const struct got_error *err = NULL;
+ const struct got_error *err;
struct got_reference *ref;
- struct got_object_id *id = NULL;
- char *id_str = NULL;
- const char *target;
err = got_ref_open(&ref, repo, refname, 0);
if (err)
return err;
- if (got_ref_is_symbolic(ref)) {
- target = got_ref_get_symref_target(ref);
- } else {
- err = got_ref_resolve(&id, repo, ref);
- if (err)
- goto done;
- err = got_object_id_str(&id_str, id);
- if (err)
- goto done;
- target = id_str;
- }
-
- err = got_ref_delete(ref, repo);
- if (err)
- goto done;
-
- printf("Deleted %s: %s\n", got_ref_get_name(ref), target);
-done:
+ err = delete_ref(repo, ref);
got_ref_close(ref);
- free(id);
- free(id_str);
return err;
}
if (do_list)
error = list_refs(repo, refname);
else if (do_delete)
- error = delete_ref(repo, refname);
+ error = delete_ref_by_name(repo, refname);
else if (symref_target)
error = add_symref(repo, refname, symref_target);
else {
blob - 5292407a8ef566f5ec84e218ccc736574fa22077
blob + c0be768af618f9a9416b82e0725d11644762ee9b
--- regress/cmdline/fetch.sh
+++ regress/cmdline/fetch.sh
test_done "$testroot" "$ret"
}
+test_fetch_delete_remote_refs() {
+ local testroot=`test_init fetch_basic`
+ 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
+ if [ "$ret" != "0" ]; then
+ echo "got ref command failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ 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 fetch -q -r $testroot/repo-clone -X > $testroot/stdout \
+ 2> $testroot/stderr
+ ret="$?"
+ if [ "$ret" == "0" ]; then
+ echo "got fetch command succeeded unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "got: -X option requires a remote name" > $testroot/stderr.expected
+ cmp -s $testroot/stderr $testroot/stderr.expected
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got fetch -q -r $testroot/repo-clone -X origin > $testroot/stdout \
+ 2> $testroot/stderr
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "got fetch command failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo -n "Deleted refs/remotes/origin/HEAD: " > $testroot/stdout.expected
+ echo "refs/remotes/origin/master" >> $testroot/stdout.expected
+ echo "Deleted 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 -l -r $testroot/repo-clone > $testroot/stdout
+ if [ "$ret" != "0" ]; then
+ echo "got ref command failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "HEAD: refs/heads/master" > $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
+ fi
+ test_done "$testroot" "$ret"
+}
+
+
test_parseargs "$@"
run_test test_fetch_basic
run_test test_fetch_list
run_test test_fetch_update_headref
run_test test_fetch_headref_deleted_locally
run_test test_fetch_gotconfig_remote_repo
+run_test test_fetch_delete_remote_refs