commit - 5a48964292096032ae4d1425bdf8ce32c465fa62
commit + 7848a0e1655d11a8d85bb505fc6232bd779cddae
blob - 6020acf53f18e27979ff823bb2a70cffabc55a18
blob + 6a2b9b414c22a2c1659c1119036c102f15e0f703
--- got/got.1
+++ got/got.1
Short alias for git+ssh.
.El
.Pp
+.Cm got clone
+creates a remote repository entry in the
+.Pa config
+file of the cloned repository to store the
+.Ar repository-url
+for future use by
+.Cm got fetch
+and
+.Xr git-fetch 1 .
+.Pp
The options for
.Cm got clone
are as follows:
.It Cm cl
Short alias for
.Cm clone .
+.It Cm fetch Oo Fl r Ar repository-path Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository-name
+Fetch new changes from a remote repository.
+If no
+.Ar remote-repository-name
+is specified the name
+.Dq origin
+will be used.
+The remote repository's URL is obtained from the corresponding entry in the
+.Pa config
+file of the repository, as created by
+.Cm got clone .
+.Pp
+Branch references in the
+.Dq refs/remotes/
+reference namespace will be updated to point at the newly fetched commits.
+The
+.Cm got rebase
+command can then be used to make new changes visible on branches in the
+.Dq refs/heads/
+reference namespace.
+.Pp
+Existing references in the
+.Dq refs/tags/
+namespace will be changed to match tags contained in the remote repository.
+.Pp
+The options for
+.Cm got fetch
+are as follows:
+.Bl -tag -width Ds
+.It Fl r Ar repository-path
+Use the repository at the specified path.
+If not specified, assume the repository is located at or above the current
+working directory.
+If this directory is a
+.Nm
+work tree, use the repository path associated with this work tree.
+.It Fl q
+Suppress progress reporting output.
+The same option will be passed to
+.Xr ssh 1
+if applicable.
+.It Fl v
+Increase the verbosity of progress reporting output.
+The same option will be passed to
+.Xr ssh 1
+if applicable.
+Multiple -v options increase the verbosity.
+The maximum is 3.
+.El
+.It Cm fe
+Short alias for
+.Cm fetch .
.It Cm checkout Oo Fl E Oc Oo Fl b Ar branch Oc Oo Fl c Ar commit Oc Oo Fl p Ar path-prefix Oc Ar repository-path Op Ar work-tree-path
Copy files from a repository into a new work tree.
Show the status of each affected file, using the following status codes:
.Pp
Additional steps are necessary if local changes need to be pushed back
to the remote repository, which currently requires
-.Cm git fetch
-and
.Cm git push .
Before working against existing branches in a repository cloned with
-.Dq git clone --bare ,
+.Dq git clone --bare
+instead of
+.Cm got clone ,
a Git
.Dq refspec
must be configured to map all references in the remote repository
.Dq remotes/origin
namespace can be updated with incoming changes from the remote
repository with
-.Cm git fetch :
+.Cm got fetch :
.Pp
.Dl $ cd /var/git/repo
-.Dl $ git fetch
+.Dl $ got fetch
.Pp
To make changes fetched from the remote repository appear on the
.Dq master
blob - 086fa5e94b0ac5144f558461eee4c5016a4898ff
blob + 0da589c91c4b3f77a139cfb32d30413a5afd5b84
--- got/got.c
+++ got/got.c
__dead static void usage_init(void);
__dead static void usage_import(void);
__dead static void usage_clone(void);
+__dead static void usage_fetch(void);
__dead static void usage_checkout(void);
__dead static void usage_update(void);
__dead static void usage_log(void);
static const struct got_error* cmd_init(int, char *[]);
static const struct got_error* cmd_import(int, char *[]);
static const struct got_error* cmd_clone(int, char *[]);
+static const struct got_error* cmd_fetch(int, char *[]);
static const struct got_error* cmd_checkout(int, char *[]);
static const struct got_error* cmd_update(int, char *[]);
static const struct got_error* cmd_log(int, char *[]);
{ "init", cmd_init, usage_init, "in" },
{ "import", cmd_import, usage_import, "im" },
{ "clone", cmd_clone, usage_clone, "cl" },
+ { "fetch", cmd_fetch, usage_fetch, "fe" },
{ "checkout", cmd_checkout, usage_checkout, "co" },
{ "update", cmd_update, usage_update, "up" },
{ "log", cmd_log, usage_log, "" },
fpa.last_p_indexed = -1;
fpa.last_p_resolved = -1;
fpa.verbosity = verbosity;
- error = got_fetch_pack(&pack_hash, &refs, &symrefs, fetchfd,
- repo, fetch_progress, &fpa);
+ error = got_fetch_pack(&pack_hash, &refs, &symrefs,
+ GOT_FETCH_DEFAULT_REMOTE_NAME, fetchfd, repo,
+ fetch_progress, &fpa);
if (error)
goto done;
free(gitconfig_path);
free(git_url);
return error;
+}
+
+static const struct got_error *
+create_ref(const char *refname, struct got_object_id *id,
+ const char *id_str, struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_reference *ref;
+
+ printf("Creating %s: %s\n", refname, id_str);
+
+ err = got_ref_alloc(&ref, refname, id);
+ if (err)
+ return err;
+
+ err = got_ref_write(ref, repo);
+ got_ref_close(ref);
+ return err;
+}
+
+static const struct got_error *
+update_ref(struct got_reference *ref, struct got_object_id *new_id,
+ struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ char *new_id_str = NULL;
+ struct got_object_id *old_id = NULL;
+
+ err = got_object_id_str(&new_id_str, new_id);
+ if (err)
+ goto done;
+
+ if (got_ref_is_symbolic(ref)) {
+ struct got_reference *new_ref;
+ err = got_ref_alloc(&new_ref, got_ref_get_name(ref), new_id);
+ if (err)
+ goto done;
+ printf("Deleting symbolic reference %s -> %s\n",
+ got_ref_get_name(ref), got_ref_get_symref_target(ref));
+ err = got_ref_delete(ref, repo);
+ if (err)
+ goto done;
+ printf("Setting %s to %s\n", got_ref_get_name(ref),
+ new_id_str);
+ err = got_ref_write(new_ref, repo);
+ if (err)
+ goto done;
+ } else {
+ err = got_ref_resolve(&old_id, repo, ref);
+ if (err)
+ goto done;
+ if (got_object_id_cmp(old_id, new_id) != 0) {
+ printf("Setting %s to %s\n",
+ got_ref_get_name(ref), new_id_str);
+ err = got_ref_change_ref(ref, new_id);
+ if (err)
+ goto done;
+ err = got_ref_write(ref, repo);
+ if (err)
+ goto done;
+ }
+ }
+done:
+ free(old_id);
+ free(new_id_str);
+ return err;
}
__dead static void
+usage_fetch(void)
+{
+ fprintf(stderr, "usage: %s fetch [-r repository-path] [-q] [-v] "
+ "[remote-repository-name]\n", getprogname());
+ exit(1);
+}
+
+static const struct got_error *
+cmd_fetch(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ char *cwd = NULL, *repo_path = NULL;
+ const char *remote_name;
+ char *proto = NULL, *host = NULL, *port = NULL;
+ char *repo_name = NULL, *server_path = NULL;
+ struct got_remote_repo *remotes, *remote = NULL;
+ int nremotes;
+ char *id_str = NULL;
+ struct got_repository *repo = NULL;
+ struct got_worktree *worktree = NULL;
+ struct got_pathlist_head refs, symrefs;
+ struct got_pathlist_entry *pe;
+ struct got_object_id *pack_hash = NULL;
+ int i, ch, fetchfd = -1;
+ struct got_fetch_progress_arg fpa;
+ int verbosity = 0;
+
+ TAILQ_INIT(&refs);
+ TAILQ_INIT(&symrefs);
+
+ while ((ch = getopt(argc, argv, "r:vq")) != -1) {
+ switch (ch) {
+ case 'r':
+ repo_path = realpath(optarg, NULL);
+ if (repo_path == NULL)
+ return got_error_from_errno2("realpath",
+ optarg);
+ got_path_strip_trailing_slashes(repo_path);
+ break;
+ case 'v':
+ if (verbosity < 0)
+ verbosity = 0;
+ else if (verbosity < 3)
+ verbosity++;
+ break;
+ case 'q':
+ verbosity = -1;
+ break;
+ default:
+ usage_fetch();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
+ else if (argc == 1)
+ remote_name = argv[0];
+ else
+ usage_fetch();
+
+ cwd = getcwd(NULL, 0);
+ if (cwd == NULL) {
+ error = got_error_from_errno("getcwd");
+ goto done;
+ }
+
+ if (repo_path == NULL) {
+ error = got_worktree_open(&worktree, cwd);
+ if (error && error->code != GOT_ERR_NOT_WORKTREE)
+ goto done;
+ else
+ error = NULL;
+ if (worktree) {
+ repo_path =
+ strdup(got_worktree_get_repo_path(worktree));
+ if (repo_path == NULL)
+ error = got_error_from_errno("strdup");
+ if (error)
+ goto done;
+ } else {
+ repo_path = strdup(cwd);
+ if (repo_path == NULL) {
+ error = got_error_from_errno("strdup");
+ goto done;
+ }
+ }
+ }
+
+ error = got_repo_open(&repo, repo_path, NULL);
+ if (error)
+ goto done;
+
+ got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
+ for (i = 0; i < nremotes; i++) {
+ remote = &remotes[i];
+ if (strcmp(remote->name, remote_name) == 0)
+ break;
+ }
+ if (i == nremotes) {
+ error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
+ goto done;
+ }
+
+ error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
+ &repo_name, remote->url);
+ if (error)
+ goto done;
+
+ if (strcmp(proto, "git") == 0) {
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec "
+ "sendfd dns inet unveil", NULL) == -1)
+ err(1, "pledge");
+#endif
+ } else if (strcmp(proto, "git+ssh") == 0 ||
+ strcmp(proto, "ssh") == 0) {
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec "
+ "sendfd unveil", NULL) == -1)
+ err(1, "pledge");
+#endif
+ } else if (strcmp(proto, "http") == 0 ||
+ strcmp(proto, "git+http") == 0) {
+ error = got_error_path(proto, GOT_ERR_NOT_IMPL);
+ goto done;
+ } else {
+ error = got_error_path(proto, GOT_ERR_BAD_PROTO);
+ goto done;
+ }
+
+ if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
+ if (unveil(GOT_FETCH_PATH_SSH, "x") != 0) {
+ error = got_error_from_errno2("unveil",
+ GOT_FETCH_PATH_SSH);
+ goto done;
+ }
+ }
+ error = apply_unveil(got_repo_get_path(repo), 0, NULL);
+ if (error)
+ goto done;
+
+ error = got_fetch_connect(&fetchfd, proto, host, port, server_path,
+ verbosity);
+ if (error)
+ goto done;
+
+ if (verbosity >= 0)
+ printf("Connected to \"%s\" %s:%s\n", remote->name, host, port);
+
+ fpa.last_scaled_size[0] = '\0';
+ fpa.last_p_indexed = -1;
+ fpa.last_p_resolved = -1;
+ fpa.verbosity = verbosity;
+ error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
+ fetchfd, repo, fetch_progress, &fpa);
+ if (error)
+ goto done;
+
+ if (pack_hash == NULL) {
+ if (verbosity >= 0)
+ printf("Already up-to-date\n");
+ goto done;
+ }
+
+ error = got_object_id_str(&id_str, pack_hash);
+ if (error)
+ goto done;
+ if (verbosity >= 0)
+ printf("Fetched %s.pack\n", id_str);
+ free(id_str);
+ id_str = NULL;
+
+ /* Update references provided with the pack file. */
+ TAILQ_FOREACH(pe, &refs, entry) {
+ const char *refname = pe->path;
+ struct got_object_id *id = pe->data;
+ struct got_reference *ref;
+ char *remote_refname;
+
+ error = got_object_id_str(&id_str, id);
+ if (error)
+ goto done;
+
+ if (strncmp("refs/tags/", refname, 10) == 0) {
+ error = got_ref_open(&ref, repo, refname, 0);
+ if (error) {
+ if (error->code != GOT_ERR_NOT_REF)
+ goto done;
+ error = create_ref(refname, id, id_str, repo);
+ if (error)
+ goto done;
+ } else {
+ error = update_ref(ref, id, repo);
+ got_ref_close(ref);
+ if (error)
+ goto done;
+ }
+ } else if (strncmp("refs/heads/", refname, 11) == 0) {
+ if (asprintf(&remote_refname, "refs/remotes/%s/%s",
+ remote_name, refname + 11) == -1) {
+ error = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ error = got_ref_open(&ref, repo, remote_refname, 0);
+ if (error) {
+ if (error->code != GOT_ERR_NOT_REF)
+ goto done;
+ error = create_ref(refname, id, id_str, repo);
+ if (error)
+ goto done;
+ } else {
+ error = update_ref(ref, id, repo);
+ got_ref_close(ref);
+ if (error)
+ goto done;
+ }
+ }
+ free(id_str);
+ id_str = NULL;
+ }
+done:
+ if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
+ error = got_error_from_errno("close");
+ if (repo)
+ got_repo_close(repo);
+ if (worktree)
+ got_worktree_close(worktree);
+ TAILQ_FOREACH(pe, &refs, entry) {
+ free((void *)pe->path);
+ free(pe->data);
+ }
+ got_pathlist_free(&refs);
+ TAILQ_FOREACH(pe, &symrefs, entry) {
+ free((void *)pe->path);
+ free(pe->data);
+ }
+ got_pathlist_free(&symrefs);
+ free(id_str);
+ free(cwd);
+ free(repo_path);
+ free(pack_hash);
+ free(proto);
+ free(host);
+ free(port);
+ free(server_path);
+ free(repo_name);
+ return error;
+}
+
+
+__dead static void
usage_checkout(void)
{
fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] "
blob - 55d93ab70d98f9f6dfbba566cc862f46d032a1ef
blob + d29011ab619ea8abc514f16e7e1fa5a63b015a55
--- include/got_error.h
+++ include/got_error.h
#define GOT_ERR_BAD_PROTO 120
#define GOT_ERR_ADDRINFO 121
#define GOT_ERR_BAD_PACKET 122
+#define GOT_ERR_NO_REMOTE 123
static const struct got_error {
int code;
{ GOT_ERR_BAD_PROTO, "unknown protocol" },
{ GOT_ERR_ADDRINFO, "getaddrinfo failed" },
{ GOT_ERR_BAD_PACKET, "bad packet received" },
+ { GOT_ERR_NO_REMOTE, "remote repository not found" },
};
/*
blob - d7b583b6dcab6977ecda8490496bbba79462bc20
blob + 8b9f1d33c0c131a2bcf34368195401b48de3bead
--- include/got_fetch.h
+++ include/got_fetch.h
* references and symbolic references learned from the server.
*/
const struct got_error *got_fetch_pack(struct got_object_id **,
- struct got_pathlist_head *, struct got_pathlist_head *, int,
- struct got_repository *, got_fetch_progress_cb, void *);
+ struct got_pathlist_head *, struct got_pathlist_head *, const char *,
+ int, struct got_repository *, got_fetch_progress_cb, void *);
blob - 0d45d173276f8ac93d898c2c552e2430bdbb95eb
blob + 1f30cd2190941327b8e9cfdaa88af0b083e996d6
--- lib/fetch.c
+++ lib/fetch.c
const struct got_error*
got_fetch_pack(struct got_object_id **pack_hash, struct got_pathlist_head *refs,
- struct got_pathlist_head *symrefs, int fetchfd, struct got_repository *repo,
- got_fetch_progress_cb progress_cb, void *progress_arg)
+ struct got_pathlist_head *symrefs, const char *remote_name, int fetchfd,
+ struct got_repository *repo, got_fetch_progress_cb progress_cb,
+ void *progress_arg)
{
int imsg_fetchfds[2], imsg_idxfds[2];
int packfd = -1, npackfd = -1, idxfd = -1, nidxfd = -1, nfetchfd = -1;
const char *repo_path = got_repo_get_path(repo);
struct got_pathlist_head have_refs;
struct got_pathlist_entry *pe;
+ struct got_reflist_head my_refs;
+ struct got_reflist_entry *re;
off_t packfile_size = 0;
+ char *ref_prefix = NULL;
+ size_t ref_prefixlen = 0;
char *path;
*pack_hash = NULL;
tmpfds[i] = -1;
TAILQ_INIT(&have_refs);
+ SIMPLEQ_INIT(&my_refs);
+
+ if (asprintf(&ref_prefix, "refs/remotes/%s/", remote_name) == -1)
+ return got_error_from_errno("asprintf");
+ ref_prefixlen = strlen(ref_prefix);
+
+ err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
+ if (err)
+ goto done;
+
+ SIMPLEQ_FOREACH(re, &my_refs, entry) {
+ struct got_object_id *id;
+ const char *refname;
+
+ if (got_ref_is_symbolic(re->ref))
+ continue;
+ refname = got_ref_get_name(re->ref);
+ if (strncmp("refs/tags/", refname, 10) == 0) {
+ char *tagname;
+
+ err = got_ref_resolve(&id, repo, re->ref);
+ if (err)
+ goto done;
+ tagname = strdup(refname);
+ if (tagname == NULL) {
+ err = got_error_from_errno("strdup");
+ goto done;
+ }
+ err = got_pathlist_append(&have_refs, tagname, id);
+ if (err) {
+ free(tagname);
+ goto done;
+ }
+ }
+
+ if (strncmp(ref_prefix, refname, ref_prefixlen) == 0) {
+ char *branchname;
+
+ err = got_ref_resolve(&id, repo, re->ref);
+ if (err)
+ goto done;
+
+ if (asprintf(&branchname, "refs/heads/%s",
+ refname + ref_prefixlen) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ err = got_pathlist_append(&have_refs, branchname, id);
+ if (err) {
+ free(branchname);
+ goto done;
+ }
+ }
+ }
+
if (asprintf(&path, "%s/%s/fetching.pack",
repo_path, GOT_OBJECTS_PACK_DIR) == -1) {
err = got_error_from_errno("asprintf");
struct got_object_id *id = NULL;
char *refname = NULL;
char *server_progress = NULL;
- off_t packfile_size_cur;
+ off_t packfile_size_cur = 0;
err = got_privsep_recv_fetch_progress(&done,
&id, &refname, symrefs, &server_progress,
&packfile_size_cur, &fetchibuf);
if (err != NULL)
goto done;
- if (done)
- *pack_hash = id;
+ if (done) {
+ if (packfile_size > 0)
+ *pack_hash = id;
+ else
+ free(id);
+ }
else if (refname && id) {
err = got_pathlist_append(refs, refname, id);
if (err)
goto done;
}
+ /* If zero data was fetched without error we are already up-to-date. */
+ if (packfile_size == 0)
+ goto done;
+
if (lseek(packfd, 0, SEEK_SET) == -1) {
err = got_error_from_errno("lseek");
goto done;
err = got_error_from_errno3("rename", tmppackpath, packpath);
goto done;
}
+ free(tmppackpath);
+ tmppackpath = NULL;
if (rename(tmpidxpath, idxpath) == -1) {
err = got_error_from_errno3("rename", tmpidxpath, idxpath);
goto done;
}
+ free(tmpidxpath);
+ tmpidxpath = NULL;
done:
+ if (tmppackpath && unlink(tmppackpath) == -1 && err == NULL)
+ err = got_error_from_errno2("unlink", tmppackpath);
+ if (tmpidxpath && unlink(tmpidxpath) == -1 && err == NULL)
+ err = got_error_from_errno2("unlink", tmpidxpath);
if (nfetchfd != -1 && close(nfetchfd) == -1 && err == NULL)
err = got_error_from_errno("close");
if (npackfd != -1 && close(npackfd) == -1 && err == NULL)
free(tmpidxpath);
free(idxpath);
free(packpath);
+ free(ref_prefix);
+ TAILQ_FOREACH(pe, &have_refs, entry) {
+ free((char *)pe->path);
+ free(pe->data);
+ }
+ got_pathlist_free(&have_refs);
+ got_ref_list_free(&my_refs);
if (err) {
free(*pack_hash);
*pack_hash = NULL;
blob - 060d60ceb22e8d199bea83b95a778e986b0bfc62
blob + 11db131869f793fff0ce97b621c4c2b7624386b5
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
uint8_t id[SHA1_DIGEST_LENGTH];
size_t name_len;
/* Followed by name_len data bytes. */
-};
+} __attribute__((__packed__));
+
struct got_imsg_fetch_have_refs {
size_t n_have_refs;
/* Followed by n_have_refs times of got_imsg_fetch_have_ref data. */
blob - 03fb686ffdb8eb99293c6996252cfa4738d6fa99
blob + 72ebc3760b2c98899dac5ee47669da3c7ec6e7e3
--- lib/privsep.c
+++ lib/privsep.c
len = sizeof(struct got_imsg_fetch_symrefs);
TAILQ_FOREACH(pe, have_refs, entry) {
- struct got_object_id *id = pe->data;
- len += sizeof(struct got_imsg_fetch_have_ref) +
- pe->path_len + sizeof(id->sha1);
+ len += sizeof(struct got_imsg_fetch_have_ref) + pe->path_len;
n_have_refs++;
}
if (len >= MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
blob - 6a3bed95d5319a66b970cafd9a692e11ff2877d6
blob + 548824f71f789dc7064065fc3f8a3347db2cb07e
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
readpkt(int *outlen, int fd, char *buf, int buflen)
{
const struct got_error *err = NULL;
- int datalen;
+ int datalen, i;
ssize_t n;
err = read_pkthdr(&datalen, fd);
if (n != datalen)
return got_error_msg(GOT_ERR_BAD_PACKET, "short packet");
+ if (chattygit) {
+ fprintf(stderr, "readpkt: %zd:\t", n);
+ fwrite(buf, 1, n, stderr);
+ for (i = 0; i < n; i++) {
+ if (isprint(buf[i]))
+ fputc(buf[i], stderr);
+ }
+ fputc('\n', stderr);
+ }
+
*outlen = n;
return NULL;
}
return NULL;
}
-static const struct got_error *
-match_remote_ref(struct got_pathlist_head *have_refs, struct got_object_id *id,
- char *refname, char *id_str)
+static void
+match_remote_ref(struct got_pathlist_head *have_refs,
+ struct got_object_id *my_id, char *refname)
{
struct got_pathlist_entry *pe;
- memset(id, 0, sizeof(*id));
+ /* XXX zero-hash signifies we don't have this ref;
+ * we should use a flag instead */
+ memset(my_id, 0, sizeof(*my_id));
TAILQ_FOREACH(pe, have_refs, entry) {
- if (strcmp(pe->path, refname) == 0) {
- if (!got_parse_sha1_digest(id->sha1, id_str))
- return got_error(GOT_ERR_BAD_OBJ_ID_STR);
+ struct got_object_id *id = pe->data;
+ if (strcmp(pe->path, refname) == 0) {
+ memcpy(my_id, id, sizeof(*my_id));
break;
}
}
- return NULL;
}
static int
char hashstr[SHA1_DIGEST_STRING_LENGTH];
struct got_object_id *have, *want;
int is_firstpkt = 1, nref = 0, refsz = 16;
- int i, n, req;
+ int i, n, nwant = 0, nhave = 0, acked = 0;
off_t packsz = 0, last_reported_packsz = 0;
char *id_str = NULL, *refname = NULL;
char *server_capabilities = NULL, *my_capabilities = NULL;
err = got_privsep_send_fetch_symrefs(ibuf, &symrefs);
if (err)
goto done;
+ is_firstpkt = 0;
+ continue;
}
- is_firstpkt = 0;
if (strstr(refname, "^{}"))
continue;
if (fetchbranch && !match_branch(refname, fetchbranch))
err = got_error(GOT_ERR_BAD_OBJ_ID_STR);
goto done;
}
-
- err = match_remote_ref(have_refs, &have[nref], id_str, refname);
- if (err)
- goto done;
-
+ match_remote_ref(have_refs, &have[nref], refname);
err = got_privsep_send_fetch_ref(ibuf, &want[nref],
refname);
if (err)
goto done;
+
if (chattygit)
fprintf(stderr, "remote %s\n", refname);
nref++;
}
- req = 0;
for (i = 0; i < nref; i++) {
if (got_object_id_cmp(&have[i], &want[i]) == 0)
continue;
err = writepkt(fd, buf, n);
if (err)
goto done;
- req = 1;
+ nwant++;
}
err = flushpkt(fd);
if (err)
goto done;
+
+ if (nwant == 0) {
+ if (chattygit)
+ fprintf(stderr, "up to date\n");
+ goto done;
+ }
+
for (i = 0; i < nref; i++) {
if (got_object_id_cmp(&have[i], &zhash) == 0)
continue;
- got_sha1_digest_to_str(want[i].sha1, hashstr, sizeof(hashstr));
+ got_sha1_digest_to_str(have[i].sha1, hashstr, sizeof(hashstr));
n = snprintf(buf, sizeof(buf), "have %s\n", hashstr);
if (n >= sizeof(buf)) {
err = got_error(GOT_ERR_NO_SPACE);
err = writepkt(fd, buf, n + 1);
if (err)
goto done;
+ nhave++;
}
- if (!req) {
- if (chattygit)
- fprintf(stderr, "up to date\n");
- err = flushpkt(fd);
+
+ while (nhave > 0 && !acked) {
+ struct got_object_id common_id;
+
+ /* The server should ACK the object IDs we need. */
+ err = readpkt(&n, fd, buf, sizeof(buf));
if (err)
goto done;
+ if (n >= 4 && strncmp(buf, "ERR ", 4) == 0) {
+ err = fetch_error(&buf[4], n - 4);
+ goto done;
+ }
+ if (n >= 4 && strncmp(buf, "NAK\n", 4) == 0) {
+ /* Server has not located our objects yet. */
+ continue;
+ }
+ if (n < 4 + SHA1_DIGEST_STRING_LENGTH ||
+ strncmp(buf, "ACK ", 4) != 0) {
+ err = got_error_msg(GOT_ERR_BAD_PACKET,
+ "unexpected message from server");
+ goto done;
+ }
+ if (!got_parse_sha1_digest(common_id.sha1, buf + 4)) {
+ err = got_error_msg(GOT_ERR_BAD_PACKET,
+ "bad object ID in ACK packet from server");
+ goto done;
+ }
+ acked++;
}
+
n = snprintf(buf, sizeof(buf), "done\n");
err = writepkt(fd, buf, n);
if (err)
goto done;
- if (!req)
- return 0;
- err = readpkt(&n, fd, buf, sizeof(buf));
- if (err)
- goto done;
- /*
- * For now, we only support a full clone, in which case the server
- * will now send a "NAK" (meaning no common objects were found).
- */
- if (n != 4 || strncmp(buf, "NAK\n", n) != 0) {
- err = got_error_msg(GOT_ERR_BAD_PACKET,
- "unexpected message from server");
- goto done;
+ if (nhave == 0) {
+ err = readpkt(&n, fd, buf, sizeof(buf));
+ if (err)
+ goto done;
+ if (n != 4 || strncmp(buf, "NAK\n", n) != 0) {
+ err = got_error_msg(GOT_ERR_BAD_PACKET,
+ "unexpected message from server");
+ goto done;
+ }
}
if (chattygit)
main(int argc, char **argv)
{
const struct got_error *err = NULL;
- int fetchfd, packfd = -1;
+ int fetchfd, packfd = -1, i;
struct got_object_id packid;
struct imsgbuf ibuf;
struct imsg imsg;
struct got_pathlist_head have_refs;
+ struct got_pathlist_entry *pe;
struct got_imsg_fetch_have_refs *fetch_have_refs = NULL;
+ struct got_imsg_fetch_have_ref *href = NULL;
size_t datalen;
+#if 0
+ static int attached;
+ while (!attached)
+ sleep (1);
+#endif
TAILQ_INIT(&have_refs);
goto done;
}
fetch_have_refs = (struct got_imsg_fetch_have_refs *)imsg.data;
- if (datalen != sizeof(struct got_imsg_fetch_have_refs) +
+ if (datalen < sizeof(struct got_imsg_fetch_have_refs) +
sizeof(struct got_imsg_fetch_have_ref) *
fetch_have_refs->n_have_refs) {
err = got_error(GOT_ERR_PRIVSEP_LEN);
goto done;
}
- if (fetch_have_refs->n_have_refs != 0) {
- /* TODO: Incremental fetch support */
- err = got_error(GOT_ERR_NOT_IMPL);
- goto done;
- }
+ href = (struct got_imsg_fetch_have_ref *)(
+ (uint8_t *)fetch_have_refs + sizeof(fetch_have_refs->n_have_refs));
+ for (i = 0; i < fetch_have_refs->n_have_refs; i++) {
+ struct got_object_id *id;
+ char *refname;
+
+ if (datalen < sizeof(*href) + href->name_len) {
+ err = got_error(GOT_ERR_PRIVSEP_LEN);
+ goto done;
+ }
+ datalen -= sizeof(*href) + href->name_len;
+ refname = strndup((uint8_t *)href + sizeof(href->id) +
+ sizeof(href->name_len), href->name_len);
+ if (refname == NULL) {
+ err = got_error_from_errno("strndump");
+ goto done;
+ }
+ id = malloc(sizeof(*id));
+ if (id == NULL) {
+ free(refname);
+ err = got_error_from_errno("malloc");
+ goto done;
+ }
+ memcpy(id->sha1, href->id, SHA1_DIGEST_LENGTH);
+ err = got_pathlist_append(&have_refs, refname, id);
+ if (err) {
+ free(refname);
+ free(id);
+ goto done;
+ }
+ href = (struct got_imsg_fetch_have_ref *)(
+ (uint8_t *)href + sizeof(*href) + href->name_len);
+ }
fetchfd = imsg.fd;
if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) {
err = fetch_pack(fetchfd, packfd, &packid, &have_refs, &ibuf);
done:
+ TAILQ_FOREACH(pe, &have_refs, entry) {
+ free((char *)pe->path);
+ free(pe->data);
+ }
+ got_pathlist_free(&have_refs);
if (packfd != -1 && close(packfd) == -1 && err == NULL)
err = got_error_from_errno("close");
if (err != NULL)