summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Schubert <schu@schu.io>2012-10-24 13:03:13 +0200
committerMichael Schubert <schu@schu.io>2012-10-24 13:39:29 +0200
commitb91d2e0ed429251bfcc9ef2c456ee0b23c67af1f (patch)
tree6dc5cd9a164ba9d4ebf5897578422383064215d4
parent9c5c1293eace17e796a4f74df2f7aa1f9cad67b2 (diff)
downloadlibgit2-b91d2e0ed429251bfcc9ef2c456ee0b23c67af1f.tar.gz
push: add report-status capability
-rw-r--r--include/git2/push.h21
-rw-r--r--src/pkt.c94
-rw-r--r--src/pkt.h19
-rw-r--r--src/push.c175
-rw-r--r--src/transport.h4
5 files changed, 303 insertions, 10 deletions
diff --git a/include/git2/push.h b/include/git2/push.h
index 5b3613924..900a1833e 100644
--- a/include/git2/push.h
+++ b/include/git2/push.h
@@ -48,6 +48,27 @@ GIT_EXTERN(int) git_push_add_refspec(git_push *push, const char *refspec);
GIT_EXTERN(int) git_push_finish(git_push *push);
/**
+ * Check if remote side successfully unpacked
+ *
+ * @param push The push object
+ *
+ * @return true if equal, false otherwise
+ */
+GIT_EXTERN(int) git_push_unpack_ok(git_push *push);
+
+/**
+ * Call callback `cb' on each status
+ *
+ * @param push The push object
+ * @param cb The callback to call on each object
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_push_status_foreach(git_push *push,
+ int (*cb)(const char *ref, const char *msg, void *data),
+ void *data);
+
+/**
* Free the given push object
*
* @param push The push object
diff --git a/src/pkt.c b/src/pkt.c
index 91f9b65c2..8a8c48a4a 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -215,6 +215,83 @@ error_out:
return error;
}
+static int ok_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_ok *pkt;
+ char *ptr;
+
+ pkt = git__malloc(sizeof(*pkt));
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_OK;
+
+ line += 3; /* skip "ok " */
+ ptr = strchr(line, '\n');
+ len = ptr - line;
+
+ pkt->ref = git__malloc(len);
+ GITERR_CHECK_ALLOC(pkt->ref);
+
+ memcpy(pkt->ref, line, len);
+ pkt->ref[len] = '\0';
+
+ *out = (git_pkt *)pkt;
+ return 0;
+}
+
+static int ng_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_ng *pkt;
+ char *ptr;
+
+ pkt = git__malloc(sizeof(*pkt));
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_NG;
+
+ line += 3; /* skip "ng " */
+ ptr = strchr(line, ' ');
+ len = ptr - line;
+
+ pkt->ref = git__malloc(len);
+ GITERR_CHECK_ALLOC(pkt->ref);
+
+ memcpy(pkt->ref, line, len);
+ pkt->ref[len] = '\0';
+
+ line = ptr + 1;
+ ptr = strchr(line, '\n');
+ len = ptr - line;
+
+ pkt->msg = git__malloc(len);
+ GITERR_CHECK_ALLOC(pkt->msg);
+
+ memcpy(pkt->msg, line, len);
+ pkt->msg[len] = '\0';
+
+ *out = (git_pkt *)pkt;
+ return 0;
+}
+
+static int unpack_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_unpack *pkt;
+
+ GIT_UNUSED(len);
+
+ pkt = git__malloc(sizeof(*pkt));
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_UNPACK;
+ if (!git__prefixcmp(line, "unpack ok"))
+ pkt->unpack_ok = 1;
+ else
+ pkt->unpack_ok = 0;
+
+ *out = (git_pkt *)pkt;
+ return 0;
+}
+
static int32_t parse_len(const char *line)
{
char num[PKT_LEN_SIZE + 1];
@@ -312,6 +389,12 @@ int git_pkt_parse_line(
ret = err_pkt(head, line, len);
else if (*line == '#')
ret = comment_pkt(head, line, len);
+ else if (!git__prefixcmp(line, "ok"))
+ ret = ok_pkt(head, line, len);
+ else if (!git__prefixcmp(line, "ng"))
+ ret = ng_pkt(head, line, len);
+ else if (!git__prefixcmp(line, "unpack"))
+ ret = unpack_pkt(head, line, len);
else
ret = ref_pkt(head, line, len);
@@ -327,6 +410,17 @@ void git_pkt_free(git_pkt *pkt)
git__free(p->head.name);
}
+ if (pkt->type == GIT_PKT_OK) {
+ git_pkt_ok *p = (git_pkt_ok *)pkt;
+ git__free(p->ref);
+ }
+
+ if (pkt->type == GIT_PKT_NG) {
+ git_pkt_ng *p = (git_pkt_ng *)pkt;
+ git__free(p->ref);
+ git__free(p->msg);
+ }
+
git__free(pkt);
}
diff --git a/src/pkt.h b/src/pkt.h
index 0fdb5c7cd..9ae86beb2 100644
--- a/src/pkt.h
+++ b/src/pkt.h
@@ -26,6 +26,9 @@ enum git_pkt_type {
GIT_PKT_ERR,
GIT_PKT_DATA,
GIT_PKT_PROGRESS,
+ GIT_PKT_OK,
+ GIT_PKT_NG,
+ GIT_PKT_UNPACK,
};
/* Used for multi-ack */
@@ -80,6 +83,22 @@ typedef struct {
char error[GIT_FLEX_ARRAY];
} git_pkt_err;
+typedef struct {
+ enum git_pkt_type type;
+ char *ref;
+} git_pkt_ok;
+
+typedef struct {
+ enum git_pkt_type type;
+ char *ref;
+ char *msg;
+} git_pkt_ng;
+
+typedef struct {
+ enum git_pkt_type type;
+ int unpack_ok;
+} git_pkt_unpack;
+
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
int git_pkt_buffer_flush(git_buf *buf);
int git_pkt_send_flush(GIT_SOCKET s);
diff --git a/src/push.c b/src/push.c
index 0b39f9c04..d65b0ce3e 100644
--- a/src/push.c
+++ b/src/push.c
@@ -8,6 +8,7 @@
#include "common.h"
#include "pack.h"
#include "pack-objects.h"
+#include "pkt.h"
#include "remote.h"
#include "transport.h"
#include "vector.h"
@@ -31,11 +32,23 @@ typedef struct push_spec {
bool force;
} push_spec;
+typedef struct push_status {
+ bool ok;
+
+ char *ref;
+ char *msg;
+} push_status;
+
struct git_push {
git_repository *repo;
git_packbuilder *pb;
git_remote *remote;
git_vector specs;
+ git_transport_caps caps;
+
+ /* report-status */
+ bool unpack_ok;
+ git_vector status;
};
int git_push_new(git_push **out, git_remote *remote)
@@ -49,12 +62,19 @@ int git_push_new(git_push **out, git_remote *remote)
p->repo = remote->repo;
p->remote = remote;
+ p->caps.report_status = 1;
if (git_vector_init(&p->specs, 0, NULL) < 0) {
git__free(p);
return -1;
}
+ if (git_vector_init(&p->status, 0, NULL) < 0) {
+ git_vector_free(&p->specs);
+ git__free(p);
+ return -1;
+ }
+
*out = p;
return 0;
}
@@ -73,6 +93,18 @@ static void free_refspec(push_spec *spec)
git__free(spec);
}
+static void free_status(push_status *status)
+{
+ if (status == NULL)
+ return;
+
+ if (status->msg)
+ git__free(status->msg);
+
+ git__free(status->ref);
+ git__free(status);
+}
+
static int check_ref(char *ref)
{
if (strcmp(ref, "HEAD") &&
@@ -160,6 +192,12 @@ static int gen_pktline(git_buf *buf, git_push *push)
git_vector_foreach(&push->specs, i, spec) {
len = 2*GIT_OID_HEXSZ + 7;
+ if (i == 0) {
+ len +=1; /* '\0' */
+ if (push->caps.report_status)
+ len += strlen(GIT_CAP_REPORT_STATUS);
+ }
+
if (spec->lref) {
if (git_reference_name_to_oid(
&spec->loid, push->repo, spec->lref) < 0) {
@@ -184,7 +222,7 @@ static int gen_pktline(git_buf *buf, git_push *push)
git_buf_printf(buf, "%04x%s ", len, hex);
git_oid_fmt(hex, &spec->loid);
- git_buf_printf(buf, "%s %s\n", hex,
+ git_buf_printf(buf, "%s %s", hex,
spec->lref);
break;
@@ -196,7 +234,7 @@ static int gen_pktline(git_buf *buf, git_push *push)
* Create remote reference
*/
git_oid_fmt(hex, &spec->loid);
- git_buf_printf(buf, "%04x%s %s %s\n", len,
+ git_buf_printf(buf, "%04x%s %s %s", len,
GIT_OID_HEX_ZERO, hex, spec->lref);
}
} else {
@@ -215,7 +253,7 @@ static int gen_pktline(git_buf *buf, git_push *push)
git_buf_printf(buf, "%04x%s ", len, hex);
git_oid_fmt(hex, &spec->loid);
- git_buf_printf(buf, "%s %s\n", hex,
+ git_buf_printf(buf, "%s %s", hex,
spec->rref);
break;
@@ -227,7 +265,7 @@ static int gen_pktline(git_buf *buf, git_push *push)
* Create remote reference
*/
git_oid_fmt(hex, &spec->loid);
- git_buf_printf(buf, "%04x%s %s %s\n", len,
+ git_buf_printf(buf, "%04x%s %s %s", len,
GIT_OID_HEX_ZERO, hex, spec->rref);
}
}
@@ -241,13 +279,21 @@ static int gen_pktline(git_buf *buf, git_push *push)
len += strlen(spec->rref);
git_oid_fmt(hex, &head->oid);
- git_buf_printf(buf, "%04x%s %s %s\n", len,
+ git_buf_printf(buf, "%04x%s %s %s", len,
hex, GIT_OID_HEX_ZERO, head->name);
break;
}
}
}
+
+ if (i == 0) {
+ git_buf_putc(buf, '\0');
+ if (push->caps.report_status)
+ git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
+ }
+
+ git_buf_putc(buf, '\n');
}
git_buf_puts(buf, "0000");
return git_buf_oom(buf) ? -1 : 0;
@@ -442,6 +488,93 @@ on_error:
return -1;
}
+static int parse_report(git_push *push)
+{
+ gitno_buffer *buf = &push->remote->transport->buffer;
+ git_pkt *pkt;
+ const char *line_end;
+ int error, recvd;
+
+ for (;;) {
+ if (buf->offset > 0)
+ error = git_pkt_parse_line(&pkt, buf->data,
+ &line_end, buf->offset);
+ else
+ error = GIT_EBUFS;
+
+ if (error < 0 && error != GIT_EBUFS)
+ return -1;
+
+ if (error == GIT_EBUFS) {
+ if ((recvd = gitno_recv(buf)) < 0)
+ return -1;
+
+ if (recvd == 0) {
+ giterr_set(GITERR_NET, "Early EOF");
+ return -1;
+ }
+ continue;
+ }
+
+ gitno_consume(buf, line_end);
+
+ if (pkt->type == GIT_PKT_OK) {
+ push_status *status = git__malloc(sizeof(*status));
+ GITERR_CHECK_ALLOC(status);
+ status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
+ status->msg = NULL;
+ git_pkt_free(pkt);
+ if (git_vector_insert(&push->status, status) < 0) {
+ git__free(status);
+ return -1;
+ }
+ continue;
+ }
+
+ if (pkt->type == GIT_PKT_NG) {
+ push_status *status = git__malloc(sizeof(*status));
+ GITERR_CHECK_ALLOC(status);
+ status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
+ status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
+ git_pkt_free(pkt);
+ if (git_vector_insert(&push->status, status) < 0) {
+ git__free(status);
+ return -1;
+ }
+ continue;
+ }
+
+ if (pkt->type == GIT_PKT_UNPACK) {
+ push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
+ git_pkt_free(pkt);
+ continue;
+ }
+
+ if (pkt->type == GIT_PKT_FLUSH) {
+ git_pkt_free(pkt);
+ return 0;
+ }
+
+ git_pkt_free(pkt);
+ giterr_set(GITERR_NET, "report-status: protocol error");
+ return -1;
+ }
+}
+
+static int finish_push(git_push *push)
+{
+ int error = -1;
+
+ if (push->caps.report_status && parse_report(push) < 0)
+ goto on_error;
+
+ error = 0;
+
+on_error:
+ git_remote_disconnect(push->remote);
+ return error;
+}
+
static int cb_filter_refs(git_remote_head *ref, void *data)
{
git_remote *remote = data;
@@ -456,23 +589,42 @@ static int filter_refs(git_remote *remote)
int git_push_finish(git_push *push)
{
- if (!git_remote_connected(push->remote)) {
- if (git_remote_connect(push->remote, GIT_DIR_PUSH) < 0)
+ if (!git_remote_connected(push->remote) &&
+ git_remote_connect(push->remote, GIT_DIR_PUSH) < 0)
return -1;
- }
if (filter_refs(push->remote) < 0 || do_push(push) < 0) {
git_remote_disconnect(push->remote);
return -1;
}
- git_remote_disconnect(push->remote);
+ return finish_push(push);
+}
+
+int git_push_unpack_ok(git_push *push)
+{
+ return push->unpack_ok;
+}
+
+int git_push_status_foreach(git_push *push,
+ int (*cb)(const char *ref, const char *msg, void *data),
+ void *data)
+{
+ push_status *status;
+ unsigned int i;
+
+ git_vector_foreach(&push->status, i, status) {
+ if (cb(status->ref, status->msg, data) < 0)
+ return GIT_EUSER;
+ }
+
return 0;
}
void git_push_free(git_push *push)
{
push_spec *spec;
+ push_status *status;
unsigned int i;
if (push == NULL)
@@ -483,5 +635,10 @@ void git_push_free(git_push *push)
}
git_vector_free(&push->specs);
+ git_vector_foreach(&push->status, i, status) {
+ free_status(status);
+ }
+ git_vector_free(&push->status);
+
git__free(push);
}
diff --git a/src/transport.h b/src/transport.h
index 2a9a6f86f..0152fcdfa 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -28,6 +28,7 @@
#define GIT_CAP_SIDE_BAND_64K "side-band-64k"
#define GIT_CAP_INCLUDE_TAG "include-tag"
#define GIT_CAP_DELETE_REFS "delete-refs"
+#define GIT_CAP_REPORT_STATUS "report-status"
typedef struct git_transport_caps {
int common:1,
@@ -36,7 +37,8 @@ typedef struct git_transport_caps {
side_band:1,
side_band_64k:1,
include_tag:1,
- delete_refs:1;
+ delete_refs:1,
+ report_status:1;
} git_transport_caps;
#ifdef GIT_SSL