diff options
author | Michael Schubert <schu@schu.io> | 2012-10-24 13:03:13 +0200 |
---|---|---|
committer | Michael Schubert <schu@schu.io> | 2012-10-24 13:39:29 +0200 |
commit | b91d2e0ed429251bfcc9ef2c456ee0b23c67af1f (patch) | |
tree | 6dc5cd9a164ba9d4ebf5897578422383064215d4 | |
parent | 9c5c1293eace17e796a4f74df2f7aa1f9cad67b2 (diff) | |
download | libgit2-b91d2e0ed429251bfcc9ef2c456ee0b23c67af1f.tar.gz |
push: add report-status capability
-rw-r--r-- | include/git2/push.h | 21 | ||||
-rw-r--r-- | src/pkt.c | 94 | ||||
-rw-r--r-- | src/pkt.h | 19 | ||||
-rw-r--r-- | src/push.c | 175 | ||||
-rw-r--r-- | src/transport.h | 4 |
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 @@ -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); } @@ -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 |