Merge pull request #992 from carlosmn/fetch-cancel remote: support fetch cancelation
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
diff --git a/include/git2/remote.h b/include/git2/remote.h
index a40daec..6471acc 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -199,6 +199,14 @@ GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_in
GIT_EXTERN(int) git_remote_connected(git_remote *remote);
/**
+ * Cancel the operation
+ *
+ * At certain points in its operation, the network code checks whether
+ * the operation has been cancelled and if so stops the operation.
+ */
+GIT_EXTERN(void) git_remote_stop(git_remote *remote);
+
+/**
* Disconnect from the remote
*
* Close the connection to the remote and free the underlying
diff --git a/src/fetch.c b/src/fetch.c
index 737a1b4..4f3d73d 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -152,7 +152,7 @@ int git_fetch_negotiate(git_remote *remote)
gitno_buffer *buf = &t->buffer;
git_buf data = GIT_BUF_INIT;
git_revwalk *walk = NULL;
- int error, pkt_type;
+ int error = -1, pkt_type;
unsigned int i;
git_oid oid;
@@ -190,6 +190,12 @@ int git_fetch_negotiate(git_remote *remote)
git_pkt_buffer_have(&oid, &data);
i++;
if (i % 20 == 0) {
+ if (t->cancel.val) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ error = GIT_EUSER;
+ goto on_error;
+ }
+
git_pkt_buffer_flush(&data);
if (git_buf_oom(&data))
goto on_error;
@@ -254,6 +260,11 @@ int git_fetch_negotiate(git_remote *remote)
}
git_pkt_buffer_done(&data);
+ if (t->cancel.val) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ error = GIT_EUSER;
+ goto on_error;
+ }
if (t->negotiation_step(t, data.ptr, data.size) < 0)
goto on_error;
@@ -288,7 +299,7 @@ int git_fetch_negotiate(git_remote *remote)
on_error:
git_revwalk_free(walk);
git_buf_free(&data);
- return -1;
+ return error;
}
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
@@ -305,11 +316,16 @@ int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_st
}
-static int no_sideband(git_indexer_stream *idx, gitno_buffer *buf, git_off_t *bytes, git_indexer_stats *stats)
+static int no_sideband(git_transport *t, git_indexer_stream *idx, gitno_buffer *buf, git_off_t *bytes, git_indexer_stats *stats)
{
int recvd;
do {
+ if (t->cancel.val) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ return GIT_EUSER;
+ }
+
if (git_indexer_stream_add(idx, buf->data, buf->offset, stats) < 0)
return -1;
@@ -337,6 +353,7 @@ int git_fetch__download_pack(
git_buf path = GIT_BUF_INIT;
gitno_buffer *buf = &t->buffer;
git_indexer_stream *idx = NULL;
+ int error = -1;
if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0)
return -1;
@@ -354,7 +371,7 @@ int git_fetch__download_pack(
* check which one belongs there.
*/
if (!t->caps.side_band && !t->caps.side_band_64k) {
- if (no_sideband(idx, buf, bytes, stats) < 0)
+ if (no_sideband(t, idx, buf, bytes, stats) < 0)
goto on_error;
git_indexer_stream_free(idx);
@@ -362,6 +379,12 @@ int git_fetch__download_pack(
}
do {
+ if (t->cancel.val) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ error = GIT_EUSER;
+ goto on_error;
+ }
+
git_pkt *pkt;
if (recv_pkt(&pkt, buf) < 0)
goto on_error;
@@ -395,7 +418,7 @@ int git_fetch__download_pack(
on_error:
git_buf_free(&path);
git_indexer_stream_free(idx);
- return -1;
+ return error;
}
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)
diff --git a/src/remote.c b/src/remote.c
index b73af01..c47f2d1 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -558,6 +558,11 @@ int git_remote_connected(git_remote *remote)
return remote->transport == NULL ? 0 : remote->transport->connected;
}
+void git_remote_stop(git_remote *remote)
+{
+ git_atomic_set(&remote->transport->cancel, 1);
+}
+
void git_remote_disconnect(git_remote *remote)
{
assert(remote);
diff --git a/src/transport.h b/src/transport.h
index 9c91afd..4c944b9 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -91,6 +91,8 @@ struct git_transport {
GIT_SOCKET socket;
git_transport_caps caps;
void *cb_data;
+ git_atomic cancel;
+
/**
* Connect and store the remote heads
*/