summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/transports/smart.h1
-rw-r--r--src/transports/smart_pkt.c22
-rw-r--r--src/transports/smart_protocol.c133
3 files changed, 120 insertions, 36 deletions
diff --git a/src/transports/smart.h b/src/transports/smart.h
index a9e894b65..c52401a3c 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -88,6 +88,7 @@ typedef git_pkt_data git_pkt_progress;
typedef struct {
enum git_pkt_type type;
+ int len;
char error[GIT_FLEX_ARRAY];
} git_pkt_err;
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 51edd9179..99da37567 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -122,6 +122,7 @@ static int err_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ERR;
+ pkt->len = (int)len;
memcpy(pkt->error, line, len);
pkt->error[len] = '\0';
@@ -166,6 +167,25 @@ static int progress_pkt(git_pkt **out, const char *line, size_t len)
return 0;
}
+static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_err *pkt;
+
+ line++;
+ len--;
+ pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_ERR;
+ pkt->len = (int)len;
+ memcpy(pkt->error, line, len);
+ pkt->error[len] = '\0';
+
+ *out = (git_pkt *)pkt;
+
+ return 0;
+}
+
/*
* Parse an other-ref line.
*/
@@ -380,6 +400,8 @@ int git_pkt_parse_line(
ret = data_pkt(head, line, len);
else if (*line == GIT_SIDE_BAND_PROGRESS)
ret = progress_pkt(head, line, len);
+ else if (*line == GIT_SIDE_BAND_ERROR)
+ ret = sideband_error_pkt(head, line, len);
else if (!git__prefixcmp(line, "ACK"))
ret = ack_pkt(head, line, len);
else if (!git__prefixcmp(line, "NAK"))
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 75494b2c7..a7734d39b 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -536,7 +536,8 @@ static int gen_pktline(git_buf *buf, git_push *push)
if (i == 0) {
++len; /* '\0' */
if (push->report_status)
- len += strlen(GIT_CAP_REPORT_STATUS);
+ len += strlen(GIT_CAP_REPORT_STATUS) + 1;
+ len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
}
git_oid_fmt(old_id, &spec->roid);
@@ -546,8 +547,13 @@ static int gen_pktline(git_buf *buf, git_push *push)
if (i == 0) {
git_buf_putc(buf, '\0');
- if (push->report_status)
+ /* Core git always starts their capabilities string with a space */
+ if (push->report_status) {
+ git_buf_putc(buf, ' ');
git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
+ }
+ git_buf_putc(buf, ' ');
+ git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
}
git_buf_putc(buf, '\n');
@@ -557,6 +563,74 @@ static int gen_pktline(git_buf *buf, git_push *push)
return git_buf_oom(buf) ? -1 : 0;
}
+static int add_push_report_pkt(git_push *push, git_pkt *pkt)
+{
+ push_status *status;
+
+ switch (pkt->type) {
+ case GIT_PKT_OK:
+ status = git__malloc(sizeof(push_status));
+ GITERR_CHECK_ALLOC(status);
+ status->msg = NULL;
+ status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
+ if (!status->ref ||
+ git_vector_insert(&push->status, status) < 0) {
+ git_push_status_free(status);
+ return -1;
+ }
+ break;
+ case GIT_PKT_NG:
+ status = git__calloc(sizeof(push_status), 1);
+ GITERR_CHECK_ALLOC(status);
+ status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
+ status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
+ if (!status->ref || !status->msg ||
+ git_vector_insert(&push->status, status) < 0) {
+ git_push_status_free(status);
+ return -1;
+ }
+ break;
+ case GIT_PKT_UNPACK:
+ push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
+ break;
+ case GIT_PKT_FLUSH:
+ return GIT_ITEROVER;
+ default:
+ giterr_set(GITERR_NET, "report-status: protocol error");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
+{
+ git_pkt *pkt;
+ const char *line = data_pkt->data, *line_end;
+ size_t line_len = data_pkt->len;
+ int error;
+
+ while (line_len > 0) {
+ error = git_pkt_parse_line(&pkt, line, &line_end, line_len);
+
+ if (error < 0)
+ return error;
+
+ /* Advance in the buffer */
+ line_len -= (line_end - line);
+ line = line_end;
+
+ error = add_push_report_pkt(push, pkt);
+
+ git_pkt_free(pkt);
+
+ if (error < 0 && GIT_ITEROVER != error)
+ return error;
+ }
+
+ return 0;
+}
+
static int parse_report(gitno_buffer *buf, git_push *push)
{
git_pkt *pkt;
@@ -586,46 +660,33 @@ static int parse_report(gitno_buffer *buf, git_push *push)
gitno_consume(buf, line_end);
- if (pkt->type == GIT_PKT_OK) {
- push_status *status = git__malloc(sizeof(push_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;
- }
+ error = 0;
- if (pkt->type == GIT_PKT_NG) {
- push_status *status = git__malloc(sizeof(push_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;
+ switch (pkt->type) {
+ case GIT_PKT_DATA:
+ /* This is a sideband packet which contains other packets */
+ error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt);
+ break;
+ case GIT_PKT_ERR:
+ giterr_set(GITERR_NET, "report-status: Error reported: %s",
+ ((git_pkt_err *)pkt)->error);
+ error = -1;
+ break;
+ case GIT_PKT_PROGRESS:
+ break;
+ default:
+ error = add_push_report_pkt(push, pkt);
+ break;
}
- if (pkt->type == GIT_PKT_UNPACK) {
- push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
- git_pkt_free(pkt);
- continue;
- }
+ git_pkt_free(pkt);
- if (pkt->type == GIT_PKT_FLUSH) {
- git_pkt_free(pkt);
+ /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
+ if (GIT_ITEROVER == error)
return 0;
- }
- git_pkt_free(pkt);
- giterr_set(GITERR_NET, "report-status: protocol error");
- return -1;
+ if (error < 0)
+ return error;
}
}