1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
#include "common.h"
#include <git2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
struct dl_data {
git_remote *remote;
git_off_t *bytes;
int ret;
int finished;
};
static void progress_cb(const char *str, int len, void *data)
{
data = data;
printf("remote: %.*s", len, str);
fflush(stdout); /* We don't have the \n to force the flush */
}
static void *download(void *ptr)
{
struct dl_data *data = (struct dl_data *)ptr;
// Connect to the remote end specifying that we want to fetch
// information from it.
if (git_remote_connect(data->remote, GIT_DIR_FETCH) < 0) {
data->ret = -1;
goto exit;
}
// Download the packfile and index it. This function updates the
// amount of received data and the indexer stats which lets you
// inform the user about progress.
if (git_remote_download(data->remote, data->bytes, NULL, NULL) < 0) {
data->ret = -1;
goto exit;
}
data->ret = 0;
exit:
data->finished = 1;
pthread_exit(&data->ret);
}
static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
{
char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
data = data;
git_oid_fmt(b_str, b);
b_str[GIT_OID_HEXSZ] = '\0';
if (git_oid_iszero(a)) {
printf("[new] %.20s %s\n", b_str, refname);
} else {
git_oid_fmt(a_str, a);
a_str[GIT_OID_HEXSZ] = '\0';
printf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname);
}
return 0;
}
int fetch(git_repository *repo, int argc, char **argv)
{
git_remote *remote = NULL;
git_off_t bytes = 0;
const git_indexer_stats *stats;
pthread_t worker;
struct dl_data data;
git_remote_callbacks callbacks;
argc = argc;
// Figure out whether it's a named remote or a URL
printf("Fetching %s for repo %p\n", argv[1], repo);
if (git_remote_load(&remote, repo, argv[1]) < 0) {
if (git_remote_new(&remote, repo, NULL, argv[1], NULL) < 0)
return -1;
}
// Set up the callbacks (only update_tips for now)
memset(&callbacks, 0, sizeof(callbacks));
callbacks.update_tips = &update_cb;
callbacks.progress = &progress_cb;
git_remote_set_callbacks(remote, &callbacks);
// Set up the information for the background worker thread
data.remote = remote;
data.bytes = &bytes;
data.ret = 0;
data.finished = 0;
stats = git_remote_stats(remote);
pthread_create(&worker, NULL, download, &data);
// Loop while the worker thread is still running. Here we show processed
// and total objects in the pack and the amount of received
// data. Most frontends will probably want to show a percentage and
// the download rate.
do {
usleep(10000);
if (stats->total > 0)
printf("Received %d/%d objects (%d) in %d bytes\r",
stats->received, stats->total, stats->processed, bytes);
} while (!data.finished);
if (data.ret < 0)
goto on_error;
pthread_join(worker, NULL);
printf("\rReceived %d/%d objects in %zu bytes\n", stats->processed, stats->total, bytes);
// Disconnect the underlying connection to prevent from idling.
git_remote_disconnect(remote);
// Update the references in the remote's namespace to point to the
// right commits. This may be needed even if there was no packfile
// to download, which can happen e.g. when the branches have been
// changed but all the neede objects are available locally.
if (git_remote_update_tips(remote) < 0)
return -1;
git_remote_free(remote);
return 0;
on_error:
git_remote_free(remote);
return -1;
}