mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-20 01:10:02 +00:00
fetch: use the streaming indexer when downloading a pack
This changes the git_remote_download() API, but the existing one is silly, so you don't get to complain. The new API allows to know how much data has been downloaded, how many objects we expect in total and how many we've processed.
This commit is contained in:
parent
f9f2344bd4
commit
7a520f5d8a
@ -150,7 +150,7 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void
|
||||
* @param filename where to store the temproray filename
|
||||
* @return GIT_SUCCESS or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_download(char **filename, git_remote *remote);
|
||||
GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
|
||||
|
||||
/**
|
||||
* Check whether the remote is connected
|
||||
|
60
src/fetch.c
60
src/fetch.c
@ -9,6 +9,7 @@
|
||||
#include "git2/oid.h"
|
||||
#include "git2/refs.h"
|
||||
#include "git2/revwalk.h"
|
||||
#include "git2/indexer.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "transport.h"
|
||||
@ -101,30 +102,27 @@ int git_fetch_negotiate(git_remote *remote)
|
||||
return t->negotiate_fetch(t, remote->repo, &remote->refs);
|
||||
}
|
||||
|
||||
int git_fetch_download_pack(char **out, git_remote *remote)
|
||||
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
|
||||
{
|
||||
if(!remote->need_pack) {
|
||||
*out = NULL;
|
||||
if(!remote->need_pack)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return remote->transport->download_pack(out, remote->transport, remote->repo);
|
||||
return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);
|
||||
}
|
||||
|
||||
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
|
||||
int git_fetch__download_pack(
|
||||
char **out,
|
||||
const char *buffered,
|
||||
size_t buffered_size,
|
||||
GIT_SOCKET fd,
|
||||
git_repository *repo)
|
||||
git_repository *repo,
|
||||
git_off_t *bytes,
|
||||
git_indexer_stats *stats)
|
||||
{
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
int error;
|
||||
int recvd;
|
||||
char buff[1024];
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
static const char suff[] = "/objects/pack/pack-received";
|
||||
gitno_buffer buf;
|
||||
git_indexer_stream *idx;
|
||||
|
||||
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
|
||||
|
||||
@ -133,41 +131,33 @@ int git_fetch__download_pack(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (git_buf_joinpath(&path, repo->path_repository, suff) < 0)
|
||||
if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
|
||||
return -1;
|
||||
|
||||
memset(stats, 0, sizeof(git_indexer_stats));
|
||||
if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY) < 0)
|
||||
goto on_error;
|
||||
*bytes = buffered_size;
|
||||
|
||||
/* Part of the packfile has been received, don't loose it */
|
||||
if (git_filebuf_write(&file, buffered, buffered_size) < 0)
|
||||
goto on_error;
|
||||
|
||||
while (1) {
|
||||
if (git_filebuf_write(&file, buf.data, buf.offset) < 0)
|
||||
do {
|
||||
if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
gitno_consume_n(&buf, buf.offset);
|
||||
error = gitno_recv(&buf);
|
||||
if (error < GIT_SUCCESS)
|
||||
if ((recvd = gitno_recv(&buf)) < 0)
|
||||
goto on_error;
|
||||
if (error == 0) /* Orderly shutdown */
|
||||
break;
|
||||
}
|
||||
|
||||
*out = git__strdup(file.path_lock);
|
||||
if (*out == NULL)
|
||||
*bytes += recvd;
|
||||
} while(recvd > 0);
|
||||
|
||||
if (git_indexer_stream_finalize(idx, stats))
|
||||
goto on_error;
|
||||
|
||||
/* A bit dodgy, but we need to keep the pack at the temporary path */
|
||||
if (git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
git_indexer_stream_free(idx);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_buf_free(&path);
|
||||
git_filebuf_cleanup(&file);
|
||||
git_indexer_stream_free(idx);
|
||||
return -1;
|
||||
}
|
||||
|
@ -10,9 +10,9 @@
|
||||
#include "netops.h"
|
||||
|
||||
int git_fetch_negotiate(git_remote *remote);
|
||||
int git_fetch_download_pack(char **out, git_remote *remote);
|
||||
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
|
||||
|
||||
int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
|
||||
GIT_SOCKET fd, git_repository *repo);
|
||||
int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
|
||||
git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
|
||||
|
||||
#endif
|
||||
|
@ -297,16 +297,16 @@ int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
|
||||
return remote->transport->ls(remote->transport, list_cb, payload);
|
||||
}
|
||||
|
||||
int git_remote_download(char **filename, git_remote *remote)
|
||||
int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
|
||||
{
|
||||
int error;
|
||||
|
||||
assert(filename && remote);
|
||||
assert(remote && bytes && stats);
|
||||
|
||||
if ((error = git_fetch_negotiate(remote)) < 0)
|
||||
return error;
|
||||
|
||||
return git_fetch_download_pack(filename, remote);
|
||||
return git_fetch_download_pack(remote, bytes, stats);
|
||||
}
|
||||
|
||||
int git_remote_update_tips(git_remote *remote)
|
||||
|
@ -81,7 +81,7 @@ struct git_transport {
|
||||
/**
|
||||
* Download the packfile
|
||||
*/
|
||||
int (*download_pack)(char **out, struct git_transport *transport, git_repository *repo);
|
||||
int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
|
||||
/**
|
||||
* Fetch the changes
|
||||
*/
|
||||
|
@ -382,7 +382,7 @@ static int git_send_done(git_transport *transport)
|
||||
return git_pkt_send_done(t->socket);
|
||||
}
|
||||
|
||||
static int git_download_pack(char **out, git_transport *transport, git_repository *repo)
|
||||
static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
|
||||
{
|
||||
transport_git *t = (transport_git *) transport;
|
||||
int error = 0, read_bytes;
|
||||
@ -410,7 +410,7 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
|
||||
|
||||
if (pkt->type == GIT_PKT_PACK) {
|
||||
git__free(pkt);
|
||||
return git_fetch__download_pack(out, buf->data, buf->offset, t->socket, repo);
|
||||
return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
|
||||
}
|
||||
|
||||
/* For now we don't care about anything */
|
||||
|
@ -529,7 +529,8 @@ cleanup:
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_filebuf *file;
|
||||
git_indexer_stream *idx;
|
||||
git_indexer_stats *stats;
|
||||
transport_http *transport;
|
||||
} download_pack_cbdata;
|
||||
|
||||
@ -545,10 +546,10 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
|
||||
{
|
||||
download_pack_cbdata *data = (download_pack_cbdata *) parser->data;
|
||||
transport_http *t = data->transport;
|
||||
git_filebuf *file = data->file;
|
||||
git_indexer_stream *idx = data->idx;
|
||||
git_indexer_stats *stats = data->stats;
|
||||
|
||||
|
||||
return t->error = git_filebuf_write(file, str, len);
|
||||
return t->error = git_indexer_stream_add(idx, str, len, stats);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -557,30 +558,16 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
|
||||
* the simple downloader. Furthermore, we're using keep-alive
|
||||
* connections, so the simple downloader would just hang.
|
||||
*/
|
||||
static int http_download_pack(char **out, git_transport *transport, git_repository *repo)
|
||||
static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
|
||||
{
|
||||
transport_http *t = (transport_http *) transport;
|
||||
git_buf *oldbuf = &t->buf;
|
||||
int ret = 0;
|
||||
int recvd;
|
||||
http_parser_settings settings;
|
||||
char buffer[1024];
|
||||
gitno_buffer buf;
|
||||
git_indexer_stream *idx = NULL;
|
||||
download_pack_cbdata data;
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
char suff[] = "/objects/pack/pack-received\0";
|
||||
|
||||
/*
|
||||
* This is part of the previous response, so we don't want to
|
||||
* re-init the parser, just set these two callbacks.
|
||||
*/
|
||||
data.file = &file;
|
||||
data.transport = t;
|
||||
t->parser.data = &data;
|
||||
t->transfer_finished = 0;
|
||||
memset(&settings, 0x0, sizeof(settings));
|
||||
settings.on_message_complete = on_message_complete_download_pack;
|
||||
settings.on_body = on_body_download_pack;
|
||||
|
||||
gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
|
||||
|
||||
@ -589,48 +576,50 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (git_buf_joinpath(&path, repo->path_repository, suff) < 0)
|
||||
if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
/*
|
||||
* This is part of the previous response, so we don't want to
|
||||
* re-init the parser, just set these two callbacks.
|
||||
*/
|
||||
memset(stats, 0, sizeof(git_indexer_stats));
|
||||
data.stats = stats;
|
||||
data.idx = idx;
|
||||
data.transport = t;
|
||||
t->parser.data = &data;
|
||||
t->transfer_finished = 0;
|
||||
memset(&settings, 0x0, sizeof(settings));
|
||||
settings.on_message_complete = on_message_complete_download_pack;
|
||||
settings.on_body = on_body_download_pack;
|
||||
*bytes = oldbuf->size;
|
||||
|
||||
if (git_indexer_stream_add(idx, oldbuf->ptr, oldbuf->size, stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY) < 0)
|
||||
goto on_error;
|
||||
|
||||
/* Part of the packfile has been received, don't loose it */
|
||||
if (git_filebuf_write(&file, oldbuf->ptr, oldbuf->size) < 0)
|
||||
goto on_error;
|
||||
|
||||
while(1) {
|
||||
do {
|
||||
size_t parsed;
|
||||
|
||||
ret = gitno_recv(&buf);
|
||||
if (ret < 0)
|
||||
if ((recvd = gitno_recv(&buf)) < 0)
|
||||
goto on_error;
|
||||
|
||||
parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
|
||||
/* Both should happen at the same time */
|
||||
if (parsed != buf.offset || t->error < 0)
|
||||
return t->error;
|
||||
goto on_error;
|
||||
|
||||
*bytes += recvd;
|
||||
gitno_consume_n(&buf, parsed);
|
||||
} while (recvd > 0 && !t->transfer_finished);
|
||||
|
||||
if (ret == 0 || t->transfer_finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*out = git__strdup(file.path_lock);
|
||||
GITERR_CHECK_ALLOC(*out);
|
||||
|
||||
/* A bit dodgy, but we need to keep the pack at the temporary path */
|
||||
ret = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
|
||||
|
||||
git_buf_free(&path);
|
||||
if (git_indexer_stream_finalize(idx, stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_indexer_stream_free(idx);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_filebuf_cleanup(&file);
|
||||
git_buf_free(&path);
|
||||
git_indexer_stream_free(idx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user