Merge pull request #1316 from ben/clone-cancel Allow network operations to cancel
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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
diff --git a/examples/network/clone.c b/examples/network/clone.c
index 5b0a810..80e80af 100644
--- a/examples/network/clone.c
+++ b/examples/network/clone.c
@@ -44,11 +44,12 @@ static void print_progress(const progress_data *pd)
pd->path);
}
-static void fetch_progress(const git_transfer_progress *stats, void *payload)
+static int fetch_progress(const git_transfer_progress *stats, void *payload)
{
progress_data *pd = (progress_data*)payload;
pd->fetch_progress = *stats;
print_progress(pd);
+ return 0;
}
static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
{
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index c428d43..151f5b4 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -25,9 +25,13 @@ typedef struct git_transfer_progress {
/**
- * Type for progress callbacks during indexing
+ * Type for progress callbacks during indexing. Return a value less than zero
+ * to cancel the transfer.
+ *
+ * @param stats Structure containing information about the state of the transfer
+ * @param payload Payload provided by caller
*/
-typedef void (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
+typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
typedef struct git_indexer git_indexer;
typedef struct git_indexer_stream git_indexer_stream;
diff --git a/src/clone.c b/src/clone.c
index 4b72b83..409a77f 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -355,8 +355,8 @@ static int setup_remotes_and_fetch(
/* Connect and download everything */
if (!git_remote_connect(origin, GIT_DIRECTION_FETCH)) {
- if (!git_remote_download(origin, options->fetch_progress_cb,
- options->fetch_progress_payload)) {
+ if (!(retcode = git_remote_download(origin, options->fetch_progress_cb,
+ options->fetch_progress_payload))) {
/* Create "origin/foo" branches for all remote branches */
if (!git_remote_update_tips(origin)) {
/* Point HEAD to the requested branch */
diff --git a/src/indexer.c b/src/indexer.c
index 3f6b107..4ff5e72 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -394,15 +394,15 @@ on_error:
return -1;
}
-static void do_progress_callback(git_indexer_stream *idx, git_transfer_progress *stats)
+static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *stats)
{
- if (!idx->progress_cb) return;
- idx->progress_cb(stats, idx->progress_payload);
+ if (!idx->progress_cb) return 0;
+ return idx->progress_cb(stats, idx->progress_payload);
}
int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
{
- int error;
+ int error = -1;
struct git_pack_header hdr;
size_t processed;
git_mwindow_file *mwf = &idx->pack->mwf;
@@ -536,14 +536,17 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
}
stats->received_objects++;
- do_progress_callback(idx, stats);
+ if (do_progress_callback(idx, stats) != 0) {
+ error = GIT_EUSER;
+ goto on_error;
+ }
}
return 0;
on_error:
git_mwindow_free_all(mwf);
- return -1;
+ return error;
}
static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 0fae086..596dba6 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -493,7 +493,7 @@ int git_smart__download_pack(
git__free(pkt);
} else if (pkt->type == GIT_PKT_DATA) {
git_pkt_data *p = (git_pkt_data *) pkt;
- if (writepack->add(writepack, p->data, p->len, stats) < 0)
+ if ((error = writepack->add(writepack, p->data, p->len, stats)) < 0)
goto on_error;
git__free(pkt);
diff --git a/tests-clar/network/fetchlocal.c b/tests-clar/network/fetchlocal.c
index ee3bd9d..e2ba675 100644
--- a/tests-clar/network/fetchlocal.c
+++ b/tests-clar/network/fetchlocal.c
@@ -4,11 +4,12 @@
#include "path.h"
#include "remote.h"
-static void transfer_cb(const git_transfer_progress *stats, void *payload)
+static int transfer_cb(const git_transfer_progress *stats, void *payload)
{
int *callcount = (int*)payload;
GIT_UNUSED(stats);
(*callcount)++;
+ return 0;
}
static void cleanup_local_repo(void *path)
diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c
index 6a46fa5..c1a9a9a 100644
--- a/tests-clar/online/clone.c
+++ b/tests-clar/online/clone.c
@@ -81,11 +81,12 @@ static void checkout_progress(const char *path, size_t cur, size_t tot, void *pa
(*was_called) = true;
}
-static void fetch_progress(const git_transfer_progress *stats, void *payload)
+static int fetch_progress(const git_transfer_progress *stats, void *payload)
{
bool *was_called = (bool*)payload;
GIT_UNUSED(stats);
(*was_called) = true;
+ return 0;
}
void test_online_clone__can_checkout_a_cloned_repo(void)
@@ -182,3 +183,18 @@ void test_online_clone__bitbucket_style(void)
git_repository_free(g_repo); g_repo = NULL;
cl_fixture_cleanup("./foo");
}
+
+static int cancel_at_half(const git_transfer_progress *stats, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (stats->received_objects > (stats->total_objects/2))
+ return 1;
+ return 0;
+}
+
+void test_online_clone__can_cancel(void)
+{
+ g_options.fetch_progress_cb = cancel_at_half;
+ cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER);
+}
diff --git a/tests-clar/online/fetch.c b/tests-clar/online/fetch.c
index 41cdb30..a0ee7aa 100644
--- a/tests-clar/online/fetch.c
+++ b/tests-clar/online/fetch.c
@@ -25,10 +25,11 @@ static int update_tips(const char *refname, const git_oid *a, const git_oid *b,
return 0;
}
-static void progress(const git_transfer_progress *stats, void *payload)
+static int progress(const git_transfer_progress *stats, void *payload)
{
size_t *bytes_received = (size_t *)payload;
*bytes_received = stats->received_bytes;
+ return 0;
}
static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
@@ -73,12 +74,13 @@ void test_online_fetch__no_tags_http(void)
do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
}
-static void transferProgressCallback(const git_transfer_progress *stats, void *payload)
+static int transferProgressCallback(const git_transfer_progress *stats, void *payload)
{
bool *invoked = (bool *)payload;
GIT_UNUSED(stats);
*invoked = true;
+ return 0;
}
void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date(void)
@@ -110,3 +112,25 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date
git_remote_free(remote);
git_repository_free(_repository);
}
+
+static int cancel_at_half(const git_transfer_progress *stats, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (stats->received_objects > (stats->total_objects/2))
+ return -1;
+ return 0;
+}
+
+void test_online_fetch__can_cancel(void)
+{
+ git_remote *remote;
+ size_t bytes_received = 0;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+ cl_git_fail_with(git_remote_download(remote, cancel_at_half, &bytes_received), GIT_EUSER);
+ git_remote_disconnect(remote);
+ git_remote_free(remote);
+}