commit 0e4002cadf11b9274c4355850d55bd59bbc20d31 from: Stefan Sperling date: Sat Mar 21 21:49:13 2020 UTC add support for fetching arbitrary references to 'got clone' and 'got fetch' commit - 239821eb715be88de935d7d1b96845b3cb8d324a commit + 0e4002cadf11b9274c4355850d55bd59bbc20d31 blob - 25d74be5281e94a84e756a0c422e72de0aa32d7e blob + c42f0af663c423cad213d1b0e5045d338932372c --- got/got.1 +++ got/got.1 @@ -136,7 +136,7 @@ follows the globbing rules documented in .It Cm im Short alias for .Cm import . -.It Cm clone Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl l Oc Oo Fl m Oc Oo Fl q Oc Oo Fl v Oc Ar repository-URL Op Ar directory +.It Cm clone Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl l Oc Oo Fl m Oc Oo Fl q Oc Oo Fl v Oc Oo Fl R Ar reference Oc Ar repository-URL Op Ar directory Clone a Git repository at the specified .Ar repository-URL into the specified @@ -273,6 +273,29 @@ This option will be passed to if applicable. Multiple -v options increase the verbosity. The maximum is 3. +.It Fl R Ar reference +In addition to the branches and tags that will be fetched, fetch an arbitrary +.Ar reference +from the remote repository's +.Dq refs/ +namespace. +This option may be specified multiple times to build a list of additional +references to fetch. +.Pp +The references will be mapped into the cloned repository's +.Dq refs/remotes/ +namespace, unless the +.Fl m +option is used to mirror references directly into the cloned repository's +.Dq refs/ +namespace. +.Pp +.Cm got clone +will refuse to fetch references from the remote repository's +.Dq refs/remotes/ +or +.Dq refs/got/ +namespace. .El .It Cm cl Short alias for @@ -385,6 +408,30 @@ The same option will be passed to if applicable. Multiple -v options increase the verbosity. The maximum is 3. +.It Fl R Ar reference +In addition to the branches and tags that will be fetched, fetch an arbitrary +.Ar reference +from the remote repository's +.Dq refs/ +namespace. +This option may be specified multiple times to build a list of additional +references to fetch. +.Pp +Each reference will be mapped into the local repository's +.Dq refs/remotes/ +namespace, unless the local repository was created as a mirror with +.Cm got clone -m . +Once a reference has been fetched, a local branch based on it can be +created with +.Cm got branch +if needed. +.Pp +.Cm got fetch +will refuse to fetch references from the remote repository's +.Dq refs/remotes/ +or +.Dq refs/got/ +namespace. .El .It Cm fe Short alias for blob - 989af2eb06a657a2eef3af8bf6402edafbf95c9c blob + 4f87cf680b2db1a9e42d32ba9fff5ce4869f50f8 --- got/got.c +++ got/got.c @@ -812,7 +812,7 @@ __dead static void usage_clone(void) { fprintf(stderr, "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] " - "repository-url [directory]\n", getprogname()); + "[-R reference] repository-url [directory]\n", getprogname()); exit(1); } @@ -957,9 +957,68 @@ create_ref(const char *refname, struct got_object_id * done: free(id_str); return err; +} + +static int +match_wanted_ref(const char *refname, const char *wanted_ref) +{ + if (strncmp(refname, "refs/", 5) != 0) + return 0; + refname += 5; + + /* + * Prevent fetching of references that won't make any + * sense outside of the remote repository's context. + */ + if (strncmp(refname, "got/", 4) == 0) + return 0; + if (strncmp(refname, "remotes/", 8) == 0) + return 0; + + if (strncmp(wanted_ref, "refs/", 5) == 0) + wanted_ref += 5; + + /* Allow prefix match. */ + if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref))) + return 1; + + /* Allow exact match. */ + return (strcmp(refname, wanted_ref) == 0); } +static int +is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname) +{ + struct got_pathlist_entry *pe; + + TAILQ_FOREACH(pe, wanted_refs, entry) { + if (match_wanted_ref(refname, pe->path)) + return 1; + } + + return 0; +} + static const struct got_error * +create_wanted_ref(const char *refname, struct got_object_id *id, + const char *remote_repo_name, int verbosity, struct got_repository *repo) +{ + const struct got_error *err; + char *remote_refname; + + if (strncmp("refs/", refname, 5) == 0) + refname += 5; + + if (asprintf(&remote_refname, "refs/remotes/%s/%s", + remote_repo_name, refname) == -1) + return got_error_from_errno("asprintf"); + + err = create_ref(remote_refname, id, verbosity, repo); + free(remote_refname); + return err; +} + +static const struct got_error * cmd_clone(int argc, char *argv[]) { const struct got_error *error = NULL; @@ -968,7 +1027,7 @@ cmd_clone(int argc, char *argv[]) char *default_destdir = NULL, *id_str = NULL; const char *repo_path; struct got_repository *repo = NULL; - struct got_pathlist_head refs, symrefs, wanted_branches; + struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs; struct got_pathlist_entry *pe; struct got_object_id *pack_hash = NULL; int ch, fetchfd = -1, fetchstatus; @@ -986,8 +1045,9 @@ cmd_clone(int argc, char *argv[]) TAILQ_INIT(&refs); TAILQ_INIT(&symrefs); TAILQ_INIT(&wanted_branches); + TAILQ_INIT(&wanted_refs); - while ((ch = getopt(argc, argv, "ab:lmvq")) != -1) { + while ((ch = getopt(argc, argv, "ab:lmvqR:")) != -1) { switch (ch) { case 'a': fetch_all_branches = 1; @@ -1013,6 +1073,12 @@ cmd_clone(int argc, char *argv[]) case 'q': verbosity = -1; break; + case 'R': + error = got_pathlist_append(&wanted_refs, + optarg, NULL); + if (error) + return error; + break; default: usage_clone(); break; @@ -1032,6 +1098,8 @@ cmd_clone(int argc, char *argv[]) errx(1, "-l and -m options are mutually exclusive"); if (verbosity == -1) errx(1, "-l and -q options are mutually exclusive"); + if (!TAILQ_EMPTY(&wanted_refs)) + errx(1, "-l and -R options are mutually exclusive"); } uri = argv[0]; @@ -1124,8 +1192,9 @@ cmd_clone(int argc, char *argv[]) fpa.verbosity = verbosity; error = got_fetch_pack(&pack_hash, &refs, &symrefs, GOT_FETCH_DEFAULT_REMOTE_NAME, mirror_references, - fetch_all_branches, &wanted_branches, list_refs_only, - verbosity, fetchfd, repo, fetch_progress, &fpa); + fetch_all_branches, &wanted_branches, &wanted_refs, + list_refs_only, verbosity, fetchfd, repo, + fetch_progress, &fpa); if (error) goto done; @@ -1146,6 +1215,16 @@ cmd_clone(int argc, char *argv[]) const char *refname = pe->path; struct got_object_id *id = pe->data; char *remote_refname; + + if (is_wanted_ref(&wanted_refs, refname) && + !mirror_references) { + error = create_wanted_ref(refname, id, + GOT_FETCH_DEFAULT_REMOTE_NAME, + verbosity - 1, repo); + if (error) + goto done; + continue; + } error = create_ref(refname, id, verbosity - 1, repo); if (error) @@ -1308,6 +1387,7 @@ done: } got_pathlist_free(&symrefs); got_pathlist_free(&wanted_branches); + got_pathlist_free(&wanted_refs); free(pack_hash); free(proto); free(host); @@ -1385,7 +1465,8 @@ __dead static void usage_fetch(void) { fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-d] [-l] " - "[-r repository-path] [-t] [-q] [-v] [remote-repository-name]\n", + "[-r repository-path] [-t] [-q] [-v] [-R reference] " + "[remote-repository-name]\n", getprogname()); exit(1); } @@ -1438,7 +1519,36 @@ delete_missing_refs(struct got_pathlist_head *their_re got_ref_get_name(re->ref), id_str); } } + + return err; +} + +static const struct got_error * +update_wanted_ref(const char *refname, struct got_object_id *id, + const char *remote_repo_name, int verbosity, struct got_repository *repo) +{ + const struct got_error *err; + char *remote_refname; + struct got_reference *ref; + + if (strncmp("refs/", refname, 5) == 0) + refname += 5; + + if (asprintf(&remote_refname, "refs/remotes/%s/%s", + remote_repo_name, refname) == -1) + return got_error_from_errno("asprintf"); + err = got_ref_open(&ref, repo, remote_refname, 0); + if (err) { + if (err->code != GOT_ERR_NOT_REF) + goto done; + err = create_ref(remote_refname, id, verbosity, repo); + } else { + err = update_ref(ref, id, 0, verbosity, repo); + got_ref_close(ref); + } +done: + free(remote_refname); return err; } @@ -1455,7 +1565,7 @@ cmd_fetch(int argc, char *argv[]) char *id_str = NULL; struct got_repository *repo = NULL; struct got_worktree *worktree = NULL; - struct got_pathlist_head refs, symrefs, wanted_branches; + struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs; struct got_pathlist_entry *pe; struct got_object_id *pack_hash = NULL; int i, ch, fetchfd = -1, fetchstatus; @@ -1467,8 +1577,9 @@ cmd_fetch(int argc, char *argv[]) TAILQ_INIT(&refs); TAILQ_INIT(&symrefs); TAILQ_INIT(&wanted_branches); + TAILQ_INIT(&wanted_refs); - while ((ch = getopt(argc, argv, "ab:dlr:tvq")) != -1) { + while ((ch = getopt(argc, argv, "ab:dlr:tvqR:")) != -1) { switch (ch) { case 'a': fetch_all_branches = 1; @@ -1504,6 +1615,12 @@ cmd_fetch(int argc, char *argv[]) case 'q': verbosity = -1; break; + case 'R': + error = got_pathlist_append(&wanted_refs, + optarg, NULL); + if (error) + return error; + break; default: usage_fetch(); break; @@ -1628,7 +1745,8 @@ cmd_fetch(int argc, char *argv[]) fpa.verbosity = verbosity; error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name, remote->mirror_references, fetch_all_branches, &wanted_branches, - list_refs_only, verbosity, fetchfd, repo, fetch_progress, &fpa); + &wanted_refs, list_refs_only, verbosity, fetchfd, repo, + fetch_progress, &fpa); if (error) goto done; @@ -1661,6 +1779,15 @@ cmd_fetch(int argc, char *argv[]) struct got_reference *ref; char *remote_refname; + if (is_wanted_ref(&wanted_refs, refname) && + !remote->mirror_references) { + error = update_wanted_ref(refname, id, + remote->name, verbosity, repo); + if (error) + goto done; + continue; + } + if (remote->mirror_references || strncmp("refs/tags/", refname, 10) == 0) { error = got_ref_open(&ref, repo, refname, 0); @@ -1740,6 +1867,7 @@ done: } got_pathlist_free(&symrefs); got_pathlist_free(&wanted_branches); + got_pathlist_free(&wanted_refs); free(id_str); free(cwd); free(repo_path); blob - 7d8cd87f9386f24c86d96e95cac445f281b1b452 blob + dd38ef5eac3a25d0396293542c1569e376556118 --- include/got_error.h +++ include/got_error.h @@ -139,6 +139,7 @@ #define GOT_ERR_BAD_PACKET 122 #define GOT_ERR_NO_REMOTE 123 #define GOT_ERR_FETCH_NO_BRANCH 124 +#define GOT_ERR_FETCH_BAD_REF 125 static const struct got_error { int code; @@ -284,6 +285,7 @@ static const struct got_error { { GOT_ERR_BAD_PACKET, "bad packet received" }, { GOT_ERR_NO_REMOTE, "remote repository not found" }, { GOT_ERR_FETCH_NO_BRANCH, "could not find any branches to fetch" }, + { GOT_ERR_FETCH_BAD_REF, "reference cannot be fetched" }, }; /* blob - 42a3f6350fdf629aaf696b69bd8d27386e5ef3fa blob + ec4d2dc496a45f2a0804737eeb32ad4991f4c8ba --- include/got_fetch.h +++ include/got_fetch.h @@ -66,5 +66,5 @@ typedef const struct got_error *(*got_fetch_progress_c */ const struct got_error *got_fetch_pack(struct got_object_id **, struct got_pathlist_head *, struct got_pathlist_head *, const char *, - int, int, struct got_pathlist_head *, int, int, int, - struct got_repository *, got_fetch_progress_cb, void *); + int, int, struct got_pathlist_head *, struct got_pathlist_head *, + int, int, int, struct got_repository *, got_fetch_progress_cb, void *); blob - 4442a20cef8658f67a2edc55456e82264944e572 blob + 9cdf6781c6716b721ebbe42fd37c027823ee6ba8 --- lib/fetch.c +++ lib/fetch.c @@ -391,8 +391,9 @@ const struct got_error* got_fetch_pack(struct got_object_id **pack_hash, struct got_pathlist_head *refs, struct got_pathlist_head *symrefs, const char *remote_name, int mirror_references, int fetch_all_branches, - struct got_pathlist_head *wanted_branches, int list_refs_only, - int verbosity, int fetchfd, struct got_repository *repo, + struct got_pathlist_head *wanted_branches, + struct got_pathlist_head *wanted_refs, int list_refs_only, int verbosity, + int fetchfd, struct got_repository *repo, got_fetch_progress_cb progress_cb, void *progress_arg) { int imsg_fetchfds[2], imsg_idxfds[2]; @@ -417,6 +418,19 @@ got_fetch_pack(struct got_object_id **pack_hash, struc char *path; char *progress = NULL; + /* + * Prevent fetching of references that won't make any + * sense outside of the remote repository's context. + */ + TAILQ_FOREACH(pe, wanted_refs, entry) { + const char *refname = pe->path; + if (strncmp(refname, "refs/got/", 9) == 0 || + strncmp(refname, "got/", 4) == 0 || + strncmp(refname, "refs/remotes/", 13) == 0 || + strncmp(refname, "remotes/", 8) == 0) + return got_error_path(refname, GOT_ERR_FETCH_BAD_REF); + } + if (!list_refs_only) repo_path = got_repo_get_path_git_dir(repo); @@ -577,7 +591,8 @@ got_fetch_pack(struct got_object_id **pack_hash, struc goto done; } err = got_privsep_send_fetch_req(&fetchibuf, nfetchfd, &have_refs, - fetch_all_branches, wanted_branches, list_refs_only, verbosity); + fetch_all_branches, wanted_branches, wanted_refs, + list_refs_only, verbosity); if (err != NULL) goto done; nfetchfd = -1; blob - 8c31f36d79e4b53047ca02d2b0f280b70f84eaa9 blob + dcfb30c7f3aef64701b8fae8369a9a04ded29115 --- lib/got_lib_privsep.h +++ lib/got_lib_privsep.h @@ -112,6 +112,7 @@ enum got_imsg_type { GOT_IMSG_FETCH_REQUEST, GOT_IMSG_FETCH_HAVE_REF, GOT_IMSG_FETCH_WANTED_BRANCH, + GOT_IMSG_FETCH_WANTED_REF, GOT_IMSG_FETCH_OUTFD, GOT_IMSG_FETCH_SYMREFS, GOT_IMSG_FETCH_REF, @@ -250,6 +251,12 @@ struct got_imsg_fetch_have_ref { /* Structure for GOT_IMSG_FETCH_WANTED_BRANCH data. */ struct got_imsg_fetch_wanted_branch { + size_t name_len; + /* Followed by name_len data bytes. */ +} __attribute__((__packed__)); + +/* Structure for GOT_IMSG_FETCH_WANTED_REF data. */ +struct got_imsg_fetch_wanted_ref { size_t name_len; /* Followed by name_len data bytes. */ } __attribute__((__packed__)); @@ -261,8 +268,10 @@ struct got_imsg_fetch_request { int verbosity; size_t n_have_refs; size_t n_wanted_branches; + size_t n_wanted_refs; /* Followed by n_have_refs GOT_IMSG_FETCH_HAVE_REF messages. */ /* Followed by n_wanted_branches times GOT_IMSG_FETCH_WANTED_BRANCH. */ + /* Followed by n_wanted_refs times GOT_IMSG_FETCH_WANTED_REF. */ } __attribute__((__packed__)); /* Structures for GOT_IMSG_FETCH_SYMREFS data. */ @@ -403,7 +412,8 @@ const struct got_error *got_privsep_send_index_pack_do const struct got_error *got_privsep_recv_index_progress(int *, int *, int *, int *, int *, struct imsgbuf *ibuf); const struct got_error *got_privsep_send_fetch_req(struct imsgbuf *, int, - struct got_pathlist_head *, int, struct got_pathlist_head *, int, int); + struct got_pathlist_head *, int, struct got_pathlist_head *, + struct got_pathlist_head *, int, int); const struct got_error *got_privsep_send_fetch_outfd(struct imsgbuf *, int); const struct got_error *got_privsep_send_fetch_symrefs(struct imsgbuf *, struct got_pathlist_head *); blob - 8c47b66e6079d8494ada12b05443c7d960089ae5 blob + 3e7631e45cb6f10d8c034f2706fc258bd7aeea54 --- lib/privsep.c +++ lib/privsep.c @@ -413,8 +413,8 @@ got_privsep_send_obj(struct imsgbuf *ibuf, struct got_ const struct got_error * got_privsep_send_fetch_req(struct imsgbuf *ibuf, int fd, struct got_pathlist_head *have_refs, int fetch_all_branches, - struct got_pathlist_head *wanted_branches, int list_refs_only, - int verbosity) + struct got_pathlist_head *wanted_branches, + struct got_pathlist_head *wanted_refs, int list_refs_only, int verbosity) { const struct got_error *err = NULL; struct ibuf *wbuf; @@ -430,6 +430,8 @@ got_privsep_send_fetch_req(struct imsgbuf *ibuf, int f fetchreq.n_have_refs++; TAILQ_FOREACH(pe, wanted_branches, entry) fetchreq.n_wanted_branches++; + TAILQ_FOREACH(pe, wanted_refs, entry) + fetchreq.n_wanted_refs++; len = sizeof(struct got_imsg_fetch_request); if (len >= MAX_IMSGSIZE - IMSG_HEADER_SIZE) { close(fd); @@ -514,6 +516,39 @@ got_privsep_send_fetch_req(struct imsgbuf *ibuf, int f return err; } + TAILQ_FOREACH(pe, wanted_refs, entry) { + const char *name = pe->path; + size_t name_len = pe->path_len; + + len = sizeof(struct got_imsg_fetch_wanted_ref) + name_len; + wbuf = imsg_create(ibuf, GOT_IMSG_FETCH_WANTED_REF, 0, 0, + len); + if (wbuf == NULL) + return got_error_from_errno( + "imsg_create FETCH_WANTED_REF"); + + /* Keep in sync with struct got_imsg_fetch_wanted_ref! */ + if (imsg_add(wbuf, &name_len, sizeof(name_len)) == -1) { + err = got_error_from_errno( + "imsg_add FETCH_WANTED_REF"); + ibuf_free(wbuf); + return err; + } + if (imsg_add(wbuf, name, name_len) == -1) { + err = got_error_from_errno( + "imsg_add FETCH_WANTED_REF"); + ibuf_free(wbuf); + return err; + } + + wbuf->fd = -1; + imsg_close(ibuf, wbuf); + err = flush_imsg(ibuf); + if (err) + return err; + } + + return NULL; } blob - 70f8e799b2fd3c6d65ef1ffdb6df9a97c48d9579 blob + 8bedceae325de82abd4bbef6715118687872c598 --- libexec/got-fetch-pack/got-fetch-pack.c +++ libexec/got-fetch-pack/got-fetch-pack.c @@ -249,6 +249,33 @@ match_branch(const char *branch, const char *wanted_br wanted_branch += 11; return (strcmp(branch + 11, wanted_branch) == 0); +} + +static int +match_wanted_ref(const char *refname, const char *wanted_ref) +{ + if (strncmp(refname, "refs/", 5) != 0) + return 0; + refname += 5; + + /* + * Prevent fetching of references that won't make any + * sense outside of the remote repository's context. + */ + if (strncmp(refname, "got/", 4) == 0) + return 0; + if (strncmp(refname, "remotes/", 8) == 0) + return 0; + + if (strncmp(wanted_ref, "refs/", 5) == 0) + wanted_ref += 5; + + /* Allow prefix match. */ + if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref))) + return 1; + + /* Allow exact match. */ + return (strcmp(refname, wanted_ref) == 0); } static const struct got_error * @@ -488,7 +515,8 @@ fetch_error(const char *buf, size_t len) static const struct got_error * fetch_pack(int fd, int packfd, struct got_object_id *packid, struct got_pathlist_head *have_refs, int fetch_all_branches, - struct got_pathlist_head *wanted_branches, int list_refs_only, + struct got_pathlist_head *wanted_branches, + struct got_pathlist_head *wanted_refs, int list_refs_only, struct imsgbuf *ibuf) { const struct got_error *err = NULL; @@ -594,7 +622,21 @@ fetch_pack(int fd, int packfd, struct got_object_id *p found_branch = 1; } } else if (strncmp(refname, "refs/tags/", 10) != 0) { - if (!list_refs_only) { + if (!TAILQ_EMPTY(wanted_refs)) { + TAILQ_FOREACH(pe, wanted_refs, entry) { + if (match_wanted_ref(refname, pe->path)) + break; + } + if (pe == NULL) { + if (chattygot) { + fprintf(stderr, + "%s: ignoring %s\n", + getprogname(), refname); + } + continue; + } + found_branch = 1; + } else if (!list_refs_only) { if (chattygot) { fprintf(stderr, "%s: ignoring %s\n", getprogname(), refname); @@ -871,10 +913,12 @@ main(int argc, char **argv) struct imsg imsg; struct got_pathlist_head have_refs; struct got_pathlist_head wanted_branches; + struct got_pathlist_head wanted_refs; struct got_pathlist_entry *pe; struct got_imsg_fetch_request fetch_req; struct got_imsg_fetch_have_ref href; struct got_imsg_fetch_wanted_branch wbranch; + struct got_imsg_fetch_wanted_ref wref; size_t datalen; #if 0 static int attached; @@ -884,6 +928,7 @@ main(int argc, char **argv) TAILQ_INIT(&have_refs); TAILQ_INIT(&wanted_branches); + TAILQ_INIT(&wanted_refs); imsg_init(&ibuf, GOT_IMSG_FD_CHILD); #ifndef PROFILE @@ -1008,6 +1053,47 @@ main(int argc, char **argv) imsg_free(&imsg); } + for (i = 0; i < fetch_req.n_wanted_refs; i++) { + char *refname; + + if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { + if (err->code == GOT_ERR_PRIVSEP_PIPE) + err = NULL; + goto done; + } + if (imsg.hdr.type == GOT_IMSG_STOP) + goto done; + if (imsg.hdr.type != GOT_IMSG_FETCH_WANTED_REF) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + goto done; + } + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + if (datalen < sizeof(wref)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + goto done; + } + memcpy(&wref, imsg.data, sizeof(wref)); + if (datalen - sizeof(wref) < wref.name_len) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + goto done; + } + refname = malloc(wref.name_len + 1); + if (refname == NULL) { + err = got_error_from_errno("malloc"); + goto done; + } + memcpy(refname, imsg.data + sizeof(wref), wref.name_len); + refname[wref.name_len] = '\0'; + + err = got_pathlist_append(&wanted_refs, refname, NULL); + if (err) { + free(refname); + goto done; + } + + imsg_free(&imsg); + } + if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { if (err->code == GOT_ERR_PRIVSEP_PIPE) err = NULL; @@ -1027,7 +1113,7 @@ main(int argc, char **argv) err = fetch_pack(fetchfd, packfd, &packid, &have_refs, fetch_req.fetch_all_branches, &wanted_branches, - fetch_req.list_refs_only, &ibuf); + &wanted_refs, fetch_req.list_refs_only, &ibuf); done: TAILQ_FOREACH(pe, &have_refs, entry) { free((char *)pe->path); blob - b408166d9ed58cf1a15d4b908ad1a3897a3f44e9 blob + f3a8329ac8336d70b30d8bb5d62f5fa383572eba --- regress/cmdline/clone.sh +++ regress/cmdline/clone.sh @@ -258,10 +258,121 @@ function test_clone_mirror_all { fi test_done "$testroot" "$ret" } + +function test_clone_reference { + local testroot=`test_init clone_reference` + local testurl=ssh://127.0.0.1/$testroot + local commit_id=`git_show_head $testroot/repo` + got branch -r $testroot/repo -c $commit_id foo + got ref -r $testroot/repo refs/hoo/boo/zoo $commit_id + got tag -r $testroot/repo -c $commit_id -m tag "1.0" >/dev/null + local tag_id=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + + got clone -q -R hoo $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/hoo/boo/zoo: $commit_id" \ + >> $testroot/stdout.expected + echo "refs/remotes/origin/master: $commit_id" \ + >> $testroot/stdout.expected + echo "refs/tags/1.0: $tag_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_clone_branch_and_reference { + local testroot=`test_init clone_reference` + local testurl=ssh://127.0.0.1/$testroot + local commit_id=`git_show_head $testroot/repo` + + got branch -r $testroot/repo -c $commit_id foo + got ref -r $testroot/repo refs/hoo/boo/zoo $commit_id + got tag -r $testroot/repo -c $commit_id -m tag "1.0" >/dev/null + local tag_id=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + + got clone -q -R hoo/boo/zoo -b foo $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/foo" > $testroot/stdout.expected + echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/foo: $commit_id" \ + >> $testroot/stdout.expected + echo "refs/remotes/origin/hoo/boo/zoo: $commit_id" \ + >> $testroot/stdout.expected + echo "refs/tags/1.0: $tag_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_clone_reference_mirror { + local testroot=`test_init clone_reference_mirror` + local testurl=ssh://127.0.0.1/$testroot + local commit_id=`git_show_head $testroot/repo` + + got branch -r $testroot/repo -c $commit_id foo + got ref -r $testroot/repo refs/hoo/boo/zoo $commit_id + got tag -r $testroot/repo -c $commit_id -m tag "1.0" >/dev/null + local tag_id=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + + got clone -q -R hoo -m $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/hoo/boo/zoo: $commit_id" >> $testroot/stdout.expected + echo "refs/tags/1.0: $tag_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" +} + run_test test_clone_basic run_test test_clone_list run_test test_clone_branch run_test test_clone_all run_test test_clone_mirror run_test test_clone_mirror_all +run_test test_clone_reference +run_test test_clone_branch_and_reference +run_test test_clone_reference_mirror blob - 374586a754ffabebdadd00bdc20a010dba7fbdd6 blob + ce08ae3c3d285ab16881c6e4ebfe33f5f04117f3 --- regress/cmdline/fetch.sh +++ regress/cmdline/fetch.sh @@ -641,7 +641,93 @@ function test_fetch_update_tag { fi test_done "$testroot" "$ret" } + +function test_fetch_reference { + local testroot=`test_init fetch_reference` + 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 branch -r $testroot/repo -c $commit_id foo + got ref -r $testroot/repo refs/hoo/boo/zoo $commit_id + got tag -r $testroot/repo -c $commit_id -m tag "1.0" >/dev/null + local tag_id=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + + echo "modified alpha on master" > $testroot/repo/alpha + git_commit $testroot/repo -m "modified alpha" + local commit_id2=`git_show_head $testroot/repo` + + (cd $testroot/repo && git checkout -q foo) + echo "modified alpha on foo" > $testroot/repo/alpha + git_commit $testroot/repo -m "modified alpha" + local commit_id3=`git_show_head $testroot/repo` + (cd $testroot/repo && git checkout -q master) + + got fetch -q -r $testroot/repo-clone -R refs/remotes/origin/main \ + > $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 -n > $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 + + echo "got: refs/remotes/origin/main: reference cannot be fetched" \ + > $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 -R refs/hoo + ret="$?" + if [ "$ret" != "0" ]; then + echo "got fetch 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/hoo/boo/zoo: $commit_id" \ + >> $testroot/stdout.expected + echo "refs/remotes/origin/master: $commit_id2" \ + >> $testroot/stdout.expected + echo "refs/tags/1.0: $tag_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" + +} + run_test test_fetch_basic run_test test_fetch_list run_test test_fetch_branch @@ -649,3 +735,4 @@ run_test test_fetch_all run_test test_fetch_empty_packfile run_test test_fetch_delete_branch run_test test_fetch_update_tag +run_test test_fetch_reference