aboutsummaryrefslogtreecommitdiff
path: root/git2d
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--git2d/.gitignore1
-rw-r--r--git2d/bare.c17
-rw-r--r--git2d/bare.h38
-rw-r--r--git2d/cmd1.c15
-rw-r--r--git2d/cmd2.c13
-rw-r--r--git2d/cmd_commit.c188
-rw-r--r--git2d/cmd_diff.c366
-rw-r--r--git2d/cmd_init.c65
-rw-r--r--git2d/cmd_ref.c113
-rw-r--r--git2d/cmd_tree.c120
-rw-r--r--git2d/main.c9
-rw-r--r--git2d/session.c91
-rw-r--r--git2d/x.h26
13 files changed, 987 insertions, 75 deletions
diff --git a/git2d/.gitignore b/git2d/.gitignore
deleted file mode 100644
index 635d84d..0000000
--- a/git2d/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/git2d
diff --git a/git2d/bare.c b/git2d/bare.c
index b580980..307f3d8 100644
--- a/git2d/bare.c
+++ b/git2d/bare.c
@@ -169,13 +169,7 @@ bare_error bare_get_u64(struct bare_reader *ctx, uint64_t *x)
if (err == BARE_ERROR_NONE) {
*x = (uint64_t) ((uint8_t *) x)[0]
- | (uint64_t) ((uint8_t *) x)[1] << 8
- | (uint64_t) ((uint8_t *) x)[2] << 16
- | (uint64_t) ((uint8_t *) x)[3] << 24
- | (uint64_t) ((uint8_t *) x)[4] << 32
- | (uint64_t) ((uint8_t *) x)[5] << 40
- | (uint64_t) ((uint8_t *) x)[6] << 48
- | (uint64_t) ((uint8_t *) x)[7] << 56;
+ | (uint64_t) ((uint8_t *) x)[1] << 8 | (uint64_t) ((uint8_t *) x)[2] << 16 | (uint64_t) ((uint8_t *) x)[3] << 24 | (uint64_t) ((uint8_t *) x)[4] << 32 | (uint64_t) ((uint8_t *) x)[5] << 40 | (uint64_t) ((uint8_t *) x)[6] << 48 | (uint64_t) ((uint8_t *) x)[7] << 56;
}
return err;
@@ -257,20 +251,17 @@ bare_error bare_get_bool(struct bare_reader *ctx, bool *x)
return bare_get_u8(ctx, (uint8_t *) x);
}
-bare_error
-bare_put_fixed_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz)
+bare_error bare_put_fixed_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz)
{
return ctx->write(ctx->buffer, (void *)src, sz);
}
-bare_error
-bare_get_fixed_data(struct bare_reader *ctx, uint8_t *dst, uint64_t sz)
+bare_error bare_get_fixed_data(struct bare_reader *ctx, uint8_t *dst, uint64_t sz)
{
return ctx->read(ctx->buffer, dst, sz);
}
-bare_error
-bare_put_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz)
+bare_error bare_put_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz)
{
bare_error err = BARE_ERROR_NONE;
diff --git a/git2d/bare.h b/git2d/bare.h
index e813464..e049dd0 100644
--- a/git2d/bare.h
+++ b/git2d/bare.h
@@ -17,8 +17,8 @@ typedef enum {
BARE_ERROR_INVALID_UTF8,
} bare_error;
-typedef bare_error (*bare_write_func)(void *buffer, const void *src, uint64_t sz);
-typedef bare_error (*bare_read_func)(void *buffer, void *dst, uint64_t sz);
+typedef bare_error(*bare_write_func) (void *buffer, const void *src, uint64_t sz);
+typedef bare_error(*bare_read_func) (void *buffer, void *dst, uint64_t sz);
struct bare_writer {
void *buffer;
@@ -30,27 +30,27 @@ struct bare_reader {
bare_read_func read;
};
-bare_error bare_put_uint(struct bare_writer *ctx, uint64_t x); /* varuint */
-bare_error bare_get_uint(struct bare_reader *ctx, uint64_t *x); /* varuint */
+bare_error bare_put_uint(struct bare_writer *ctx, uint64_t x); /* varuint */
+bare_error bare_get_uint(struct bare_reader *ctx, uint64_t * x); /* varuint */
bare_error bare_put_u8(struct bare_writer *ctx, uint8_t x);
-bare_error bare_get_u8(struct bare_reader *ctx, uint8_t *x);
+bare_error bare_get_u8(struct bare_reader *ctx, uint8_t * x);
bare_error bare_put_u16(struct bare_writer *ctx, uint16_t x);
-bare_error bare_get_u16(struct bare_reader *ctx, uint16_t *x);
+bare_error bare_get_u16(struct bare_reader *ctx, uint16_t * x);
bare_error bare_put_u32(struct bare_writer *ctx, uint32_t x);
-bare_error bare_get_u32(struct bare_reader *ctx, uint32_t *x);
+bare_error bare_get_u32(struct bare_reader *ctx, uint32_t * x);
bare_error bare_put_u64(struct bare_writer *ctx, uint64_t x);
-bare_error bare_get_u64(struct bare_reader *ctx, uint64_t *x);
+bare_error bare_get_u64(struct bare_reader *ctx, uint64_t * x);
-bare_error bare_put_int(struct bare_writer *ctx, int64_t x); /* varint */
-bare_error bare_get_int(struct bare_reader *ctx, int64_t *x); /* varint */
+bare_error bare_put_int(struct bare_writer *ctx, int64_t x); /* varint */
+bare_error bare_get_int(struct bare_reader *ctx, int64_t * x); /* varint */
bare_error bare_put_i8(struct bare_writer *ctx, int8_t x);
-bare_error bare_get_i8(struct bare_reader *ctx, int8_t *x);
+bare_error bare_get_i8(struct bare_reader *ctx, int8_t * x);
bare_error bare_put_i16(struct bare_writer *ctx, int16_t x);
-bare_error bare_get_i16(struct bare_reader *ctx, int16_t *x);
+bare_error bare_get_i16(struct bare_reader *ctx, int16_t * x);
bare_error bare_put_i32(struct bare_writer *ctx, int32_t x);
-bare_error bare_get_i32(struct bare_reader *ctx, int32_t *x);
+bare_error bare_get_i32(struct bare_reader *ctx, int32_t * x);
bare_error bare_put_i64(struct bare_writer *ctx, int64_t x);
-bare_error bare_get_i64(struct bare_reader *ctx, int64_t *x);
+bare_error bare_get_i64(struct bare_reader *ctx, int64_t * x);
bare_error bare_put_f32(struct bare_writer *ctx, float x);
bare_error bare_get_f32(struct bare_reader *ctx, float *x);
@@ -60,13 +60,13 @@ bare_error bare_get_f64(struct bare_reader *ctx, double *x);
bare_error bare_put_bool(struct bare_writer *ctx, bool x);
bare_error bare_get_bool(struct bare_reader *ctx, bool *x);
-bare_error bare_put_fixed_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz);
-bare_error bare_get_fixed_data(struct bare_reader *ctx, uint8_t *dst, uint64_t sz);
-bare_error bare_put_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz);
-bare_error bare_get_data(struct bare_reader *ctx, uint8_t *dst, uint64_t sz);
+bare_error bare_put_fixed_data(struct bare_writer *ctx, const uint8_t * src, uint64_t sz);
+bare_error bare_get_fixed_data(struct bare_reader *ctx, uint8_t * dst, uint64_t sz);
+bare_error bare_put_data(struct bare_writer *ctx, const uint8_t * src, uint64_t sz);
+bare_error bare_get_data(struct bare_reader *ctx, uint8_t * dst, uint64_t sz);
bare_error bare_put_str(struct bare_writer *ctx, const char *src, uint64_t sz);
bare_error bare_get_str(struct bare_reader *ctx, char *dst, uint64_t sz);
/* Note that the _str implementation here does not check for UTF-8 validity. */
-#endif /* BARE_H */
+#endif /* BARE_H */
diff --git a/git2d/cmd1.c b/git2d/cmd1.c
index a7d8b07..ec3d1ad 100644
--- a/git2d/cmd1.c
+++ b/git2d/cmd1.c
@@ -90,18 +90,15 @@ int cmd_index(git_repository *repo, struct bare_writer *writer)
/* Title */
size_t msg_len = msg ? strlen(msg) : 0;
- bare_put_data(writer, (const uint8_t *)(msg ? msg : ""),
- msg_len);
+ bare_put_data(writer, (const uint8_t *)(msg ? msg : ""), msg_len);
/* Author's name */
const char *author_name = author ? author->name : "";
- bare_put_data(writer, (const uint8_t *)author_name,
- strlen(author_name));
+ bare_put_data(writer, (const uint8_t *)author_name, strlen(author_name));
/* Author's email */
const char *author_email = author ? author->email : "";
- bare_put_data(writer, (const uint8_t *)author_email,
- strlen(author_email));
+ bare_put_data(writer, (const uint8_t *)author_email, strlen(author_email));
/* Author's date */
/* TODO: Pass the integer instead of a string */
@@ -109,12 +106,10 @@ int cmd_index(git_repository *repo, struct bare_writer *writer)
char timebuf[64];
struct tm *tm = localtime(&time);
if (tm)
- strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S",
- tm);
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
else
strcpy(timebuf, "unknown");
- bare_put_data(writer, (const uint8_t *)timebuf,
- strlen(timebuf));
+ bare_put_data(writer, (const uint8_t *)timebuf, strlen(timebuf));
git_commit_free(commit);
count++;
diff --git a/git2d/cmd2.c b/git2d/cmd2.c
index dd72ddb..33947c6 100644
--- a/git2d/cmd2.c
+++ b/git2d/cmd2.c
@@ -5,9 +5,7 @@
#include "x.h"
-int
-cmd_treeraw(git_repository *repo, struct bare_reader *reader,
- struct bare_writer *writer)
+int cmd_treeraw(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
{
/* Path */
char path[4096] = { 0 };
@@ -62,8 +60,7 @@ cmd_treeraw(git_repository *repo, struct bare_reader *reader,
bare_put_uint(writer, 1);
bare_put_uint(writer, count);
for (size_t i = 0; i < count; i++) {
- const git_tree_entry *subentry =
- git_tree_entry_byindex(subtree, i);
+ const git_tree_entry *subentry = git_tree_entry_byindex(subtree, i);
const char *name = git_tree_entry_name(subentry);
git_otype type = git_tree_entry_type(subentry);
uint32_t mode = git_tree_entry_filemode(subentry);
@@ -77,8 +74,7 @@ cmd_treeraw(git_repository *repo, struct bare_reader *reader,
entry_type = 2;
git_object *subobj = NULL;
- if (git_tree_entry_to_object
- (&subobj, repo, subentry) == 0) {
+ if (git_tree_entry_to_object(&subobj, repo, subentry) == 0) {
git_blob *b = (git_blob *) subobj;
size = git_blob_rawsize(b);
git_blob_free(b);
@@ -88,8 +84,7 @@ cmd_treeraw(git_repository *repo, struct bare_reader *reader,
bare_put_uint(writer, entry_type);
bare_put_uint(writer, mode);
bare_put_uint(writer, size);
- bare_put_data(writer, (const uint8_t *)name,
- strlen(name));
+ bare_put_data(writer, (const uint8_t *)name, strlen(name));
}
if (entry != NULL) {
git_tree_free(subtree);
diff --git a/git2d/cmd_commit.c b/git2d/cmd_commit.c
new file mode 100644
index 0000000..3031088
--- /dev/null
+++ b/git2d/cmd_commit.c
@@ -0,0 +1,188 @@
+/*-
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+ */
+
+#include "x.h"
+
+int cmd_commit_tree_oid(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char hex[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) hex, sizeof(hex) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid oid;
+ if (git_oid_fromstr(&oid, hex) != 0) {
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+ git_commit *commit = NULL;
+ if (git_commit_lookup(&commit, repo, &oid) != 0) {
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+ git_tree *tree = NULL;
+ if (git_commit_tree(&tree, commit) != 0) {
+ git_commit_free(commit);
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+ const git_oid *toid = git_tree_id(tree);
+ bare_put_uint(writer, 0);
+ bare_put_data(writer, toid->id, GIT_OID_RAWSZ);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ return 0;
+}
+
+int cmd_commit_create(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char treehex[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) treehex, sizeof(treehex) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid tree_oid;
+ if (git_oid_fromstr(&tree_oid, treehex) != 0) {
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ uint64_t pcnt = 0;
+ if (bare_get_uint(reader, &pcnt) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_commit **parents = NULL;
+ if (pcnt > 0) {
+ parents = (git_commit **) calloc(pcnt, sizeof(git_commit *));
+ if (!parents) {
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ for (uint64_t i = 0; i < pcnt; i++) {
+ char phex[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) phex, sizeof(phex) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ goto fail;
+ }
+ git_oid poid;
+ if (git_oid_fromstr(&poid, phex) != 0) {
+ bare_put_uint(writer, 15);
+ goto fail;
+ }
+ if (git_commit_lookup(&parents[i], repo, &poid) != 0) {
+ bare_put_uint(writer, 15);
+ goto fail;
+ }
+ }
+ }
+ char aname[512] = { 0 };
+ char aemail[512] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) aname, sizeof(aname) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ goto fail;
+ }
+ if (bare_get_data(reader, (uint8_t *) aemail, sizeof(aemail) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ goto fail;
+ }
+ int64_t when = 0;
+ int64_t tzoff = 0;
+ if (bare_get_i64(reader, &when) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ goto fail;
+ }
+ if (bare_get_i64(reader, &tzoff) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ goto fail;
+ }
+ char *message = NULL;
+ {
+ uint64_t msz = 0;
+ if (bare_get_uint(reader, &msz) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ goto fail;
+ }
+ message = (char *)malloc(msz + 1);
+ if (!message) {
+ bare_put_uint(writer, 15);
+ goto fail;
+ }
+ if (bare_get_fixed_data(reader, (uint8_t *) message, msz) != BARE_ERROR_NONE) {
+ free(message);
+ bare_put_uint(writer, 11);
+ goto fail;
+ }
+ message[msz] = '\0';
+ }
+ git_signature *sig = NULL;
+ if (git_signature_new(&sig, aname, aemail, (git_time_t) when, (int)tzoff) != 0) {
+ free(message);
+ bare_put_uint(writer, 19);
+ goto fail;
+ }
+ git_tree *tree = NULL;
+ if (git_tree_lookup(&tree, repo, &tree_oid) != 0) {
+ git_signature_free(sig);
+ free(message);
+ bare_put_uint(writer, 19);
+ goto fail;
+ }
+ git_oid out;
+ int rc = git_commit_create(&out, repo, NULL, sig, sig, NULL, message, tree,
+ (int)pcnt, (const git_commit **)parents);
+ git_tree_free(tree);
+ git_signature_free(sig);
+ free(message);
+ if (rc != 0) {
+ bare_put_uint(writer, 19);
+ goto fail;
+ }
+ bare_put_uint(writer, 0);
+ bare_put_data(writer, out.id, GIT_OID_RAWSZ);
+ if (parents) {
+ for (uint64_t i = 0; i < pcnt; i++)
+ if (parents[i])
+ git_commit_free(parents[i]);
+ free(parents);
+ }
+ return 0;
+ fail:
+ if (parents) {
+ for (uint64_t i = 0; i < pcnt; i++)
+ if (parents[i])
+ git_commit_free(parents[i]);
+ free(parents);
+ }
+ return -1;
+}
+
+int cmd_update_ref(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char refname[4096] = { 0 };
+ char commithex[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) refname, sizeof(refname) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ if (bare_get_data(reader, (uint8_t *) commithex, sizeof(commithex) - 1)
+ != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid oid;
+ if (git_oid_fromstr(&oid, commithex) != 0) {
+ bare_put_uint(writer, 18);
+ return -1;
+ }
+ git_reference *out = NULL;
+ int rc = git_reference_create(&out, repo, refname, &oid, 1, NULL);
+ if (rc != 0) {
+ bare_put_uint(writer, 18);
+ return -1;
+ }
+ git_reference_free(out);
+ bare_put_uint(writer, 0);
+ return 0;
+}
diff --git a/git2d/cmd_diff.c b/git2d/cmd_diff.c
new file mode 100644
index 0000000..a7bf0b8
--- /dev/null
+++ b/git2d/cmd_diff.c
@@ -0,0 +1,366 @@
+/*-
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+ */
+
+#include "x.h"
+
+static int diff_stats_to_string(git_diff *diff, git_buf *out)
+{
+ git_diff_stats *stats = NULL;
+ if (git_diff_get_stats(&stats, diff) != 0) {
+ return -1;
+ }
+ int rc = git_diff_stats_to_buf(out, stats, GIT_DIFF_STATS_FULL, 80);
+ git_diff_stats_free(stats);
+ return rc;
+}
+
+static void split_message(const char *message, char **title_out, char **body_out)
+{
+ *title_out = NULL;
+ *body_out = NULL;
+ if (!message)
+ return;
+ const char *nl = strchr(message, '\n');
+ if (!nl) {
+ *title_out = strdup(message);
+ *body_out = strdup("");
+ return;
+ }
+ size_t title_len = (size_t)(nl - message);
+ *title_out = (char *)malloc(title_len + 1);
+ if (*title_out) {
+ memcpy(*title_out, message, title_len);
+ (*title_out)[title_len] = '\0';
+ }
+ const char *rest = nl + 1;
+ if (*rest == '\n')
+ rest++;
+ *body_out = strdup(rest);
+}
+
+int cmd_format_patch(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char hex[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) hex, sizeof(hex) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid oid;
+ if (git_oid_fromstr(&oid, hex) != 0) {
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+
+ git_commit *commit = NULL;
+ if (git_commit_lookup(&commit, repo, &oid) != 0) {
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+
+ git_tree *tree = NULL;
+ if (git_commit_tree(&tree, commit) != 0) {
+ git_commit_free(commit);
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+
+ git_diff *diff = NULL;
+ if (git_commit_parentcount(commit) == 0) {
+ if (git_diff_tree_to_tree(&diff, repo, NULL, tree, NULL) != 0) {
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ } else {
+ git_commit *parent = NULL;
+ git_tree *ptree = NULL;
+ if (git_commit_parent(&parent, commit, 0) != 0 || git_commit_tree(&ptree, parent) != 0) {
+ if (parent)
+ git_commit_free(parent);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ if (git_diff_tree_to_tree(&diff, repo, ptree, tree, NULL) != 0) {
+ git_tree_free(ptree);
+ git_commit_free(parent);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ git_tree_free(ptree);
+ git_commit_free(parent);
+ }
+
+ git_buf stats = { 0 };
+ if (diff_stats_to_string(diff, &stats) != 0) {
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+
+ git_buf patch = { 0 };
+ if (git_diff_to_buf(&patch, diff, GIT_DIFF_FORMAT_PATCH) != 0) {
+ git_buf_dispose(&stats);
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+
+ const git_signature *author = git_commit_author(commit);
+ char *title = NULL, *body = NULL;
+ split_message(git_commit_message(commit), &title, &body);
+
+ char header[2048];
+ char timebuf[64];
+ {
+ time_t t = git_commit_time(commit);
+ struct tm *tm = localtime(&t);
+ if (tm)
+ strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %z", tm);
+ else
+ strcpy(timebuf, "unknown");
+ }
+ snprintf(header, sizeof(header), "From %s Mon Sep 17 00:00:00 2001\nFrom: %s <%s>\nDate: %s\nSubject: [PATCH] %s\n\n", git_oid_tostr_s(&oid), author && author->name ? author->name : "", author && author->email ? author->email : "", timebuf, title ? title : "");
+
+ const char *trailer = "\n-- \n2.48.1\n";
+ size_t header_len = strlen(header);
+ size_t body_len = body ? strlen(body) : 0;
+ size_t trailer_len = strlen(trailer);
+ size_t total = header_len + body_len + (body_len ? 1 : 0) + 4 + stats.size + 1 + patch.size + trailer_len;
+
+ uint8_t *buf = (uint8_t *) malloc(total);
+ if (!buf) {
+ free(title);
+ free(body);
+ git_buf_dispose(&patch);
+ git_buf_dispose(&stats);
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ size_t off = 0;
+ memcpy(buf + off, header, header_len);
+ off += header_len;
+ if (body_len) {
+ memcpy(buf + off, body, body_len);
+ off += body_len;
+ buf[off++] = '\n';
+ }
+ memcpy(buf + off, "---\n", 4);
+ off += 4;
+ memcpy(buf + off, stats.ptr, stats.size);
+ off += stats.size;
+ buf[off++] = '\n';
+ memcpy(buf + off, patch.ptr, patch.size);
+ off += patch.size;
+ memcpy(buf + off, trailer, trailer_len);
+ off += trailer_len;
+
+ bare_put_uint(writer, 0);
+ bare_put_data(writer, buf, off);
+
+ free(buf);
+ free(title);
+ free(body);
+ git_buf_dispose(&patch);
+ git_buf_dispose(&stats);
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ return 0;
+}
+
+int cmd_commit_diff(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char hex[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) hex, sizeof(hex) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid oid;
+ if (git_oid_fromstr(&oid, hex) != 0) {
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+
+ git_commit *commit = NULL;
+ if (git_commit_lookup(&commit, repo, &oid) != 0) {
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+
+ git_tree *tree = NULL;
+ if (git_commit_tree(&tree, commit) != 0) {
+ git_commit_free(commit);
+ bare_put_uint(writer, 14);
+ return -1;
+ }
+
+ git_diff *diff = NULL;
+ git_oid parent_oid = { 0 };
+ if (git_commit_parentcount(commit) == 0) {
+ if (git_diff_tree_to_tree(&diff, repo, NULL, tree, NULL) != 0) {
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ } else {
+ git_commit *parent = NULL;
+ git_tree *ptree = NULL;
+ if (git_commit_parent(&parent, commit, 0) != 0 || git_commit_tree(&ptree, parent) != 0) {
+ if (parent)
+ git_commit_free(parent);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ git_oid_cpy(&parent_oid, git_commit_id(parent));
+ if (git_diff_tree_to_tree(&diff, repo, ptree, tree, NULL) != 0) {
+ git_tree_free(ptree);
+ git_commit_free(parent);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ git_tree_free(ptree);
+ git_commit_free(parent);
+ }
+
+ git_buf stats = { 0 };
+ if (diff_stats_to_string(diff, &stats) != 0) {
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ git_buf patch = { 0 };
+ if (git_diff_to_buf(&patch, diff, GIT_DIFF_FORMAT_PATCH) != 0) {
+ git_buf_dispose(&stats);
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+
+ bare_put_uint(writer, 0);
+ bare_put_data(writer, parent_oid.id, GIT_OID_RAWSZ);
+ bare_put_data(writer, (const uint8_t *)stats.ptr, stats.size);
+ bare_put_data(writer, (const uint8_t *)patch.ptr, patch.size);
+
+ git_buf_dispose(&patch);
+ git_buf_dispose(&stats);
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ return 0;
+}
+
+int cmd_merge_base(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char hex1[64] = { 0 };
+ char hex2[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) hex1, sizeof(hex1) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ if (bare_get_data(reader, (uint8_t *) hex2, sizeof(hex2) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid a, b, out;
+ if (git_oid_fromstr(&a, hex1) != 0 || git_oid_fromstr(&b, hex2) != 0) {
+ bare_put_uint(writer, 17);
+ return -1;
+ }
+ int rc = git_merge_base(&out, repo, &a, &b);
+ if (rc == GIT_ENOTFOUND) {
+ bare_put_uint(writer, 16);
+ return -1;
+ }
+ if (rc != 0) {
+ bare_put_uint(writer, 17);
+ return -1;
+ }
+ bare_put_uint(writer, 0);
+ bare_put_data(writer, out.id, GIT_OID_RAWSZ);
+ return 0;
+}
+
+int cmd_log(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char spec[4096] = { 0 };
+ uint64_t limit = 0;
+ if (bare_get_data(reader, (uint8_t *) spec, sizeof(spec) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ if (bare_get_uint(reader, &limit) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+
+ git_object *obj = NULL;
+ if (spec[0] == '\0')
+ strcpy(spec, "HEAD");
+ if (git_revparse_single(&obj, repo, spec) != 0) {
+ bare_put_uint(writer, 4);
+ return -1;
+ }
+ git_commit *start = (git_commit *) obj;
+
+ git_revwalk *walk = NULL;
+ if (git_revwalk_new(&walk, repo) != 0) {
+ git_commit_free(start);
+ bare_put_uint(writer, 9);
+ return -1;
+ }
+ git_revwalk_sorting(walk, GIT_SORT_TIME);
+ git_revwalk_push(walk, git_commit_id(start));
+ git_commit_free(start);
+
+ bare_put_uint(writer, 0);
+ git_oid oid;
+ uint64_t count = 0;
+ while ((limit == 0 || count < limit)
+ && git_revwalk_next(&oid, walk) == 0) {
+ git_commit *c = NULL;
+ if (git_commit_lookup(&c, repo, &oid) != 0)
+ break;
+ const char *msg = git_commit_summary(c);
+ const git_signature *author = git_commit_author(c);
+ time_t t = git_commit_time(c);
+ char timebuf[64];
+ struct tm *tm = localtime(&t);
+ if (tm)
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
+ else
+ strcpy(timebuf, "unknown");
+
+ bare_put_data(writer, oid.id, GIT_OID_RAWSZ);
+ bare_put_data(writer, (const uint8_t *)(msg ? msg : ""), msg ? strlen(msg) : 0);
+ bare_put_data(writer, (const uint8_t *)(author && author->name ? author->name : ""), author && author->name ? strlen(author->name) : 0);
+ bare_put_data(writer, (const uint8_t *)(author && author->email ? author->email : ""), author && author->email ? strlen(author->email) : 0);
+ bare_put_data(writer, (const uint8_t *)timebuf, strlen(timebuf));
+ git_commit_free(c);
+ count++;
+ }
+ git_revwalk_free(walk);
+ return 0;
+}
diff --git a/git2d/cmd_init.c b/git2d/cmd_init.c
new file mode 100644
index 0000000..962d229
--- /dev/null
+++ b/git2d/cmd_init.c
@@ -0,0 +1,65 @@
+/*-
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+ */
+
+#include "x.h"
+
+int cmd_init_repo(const char *path, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char hooks[4096] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) hooks, sizeof(hooks) - 1) != BARE_ERROR_NONE) {
+ fprintf(stderr, "init_repo: protocol error reading hooks for path '%s'\n", path);
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+
+ fprintf(stderr, "init_repo: starting for path='%s' hooks='%s'\n", path, hooks);
+
+ if (mkdir(path, 0700) != 0 && errno != EEXIST) {
+ fprintf(stderr, "init_repo: mkdir failed for '%s': %s\n", path, strerror(errno));
+ bare_put_uint(writer, 24);
+ return -1;
+ }
+
+ git_repository *repo = NULL;
+ git_repository_init_options opts;
+ git_repository_init_options_init(&opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION);
+ opts.flags = GIT_REPOSITORY_INIT_BARE;
+ if (git_repository_init_ext(&repo, path, &opts) != 0) {
+ const git_error *ge = git_error_last();
+ fprintf(stderr, "init_repo: git_repository_init_ext failed: %s (klass=%d)\n", ge && ge->message ? ge->message : "(no message)", ge ? ge->klass : 0);
+ bare_put_uint(writer, 20);
+ return -1;
+ }
+ git_config *cfg = NULL;
+ if (git_repository_config(&cfg, repo) != 0) {
+ git_repository_free(repo);
+ const git_error *ge = git_error_last();
+ fprintf(stderr, "init_repo: open config failed: %s (klass=%d)\n", ge && ge->message ? ge->message : "(no message)", ge ? ge->klass : 0);
+ bare_put_uint(writer, 21);
+ return -1;
+ }
+ if (git_config_set_string(cfg, "core.hooksPath", hooks) != 0) {
+ git_config_free(cfg);
+ git_repository_free(repo);
+ const git_error *ge = git_error_last();
+ fprintf(stderr, "init_repo: set hooksPath failed: %s (klass=%d) hooks='%s'\n", ge && ge->message ? ge->message : "(no message)", ge ? ge->klass : 0, hooks);
+ bare_put_uint(writer, 22);
+ return -1;
+ }
+ if (git_config_set_bool(cfg, "receive.advertisePushOptions", 1) != 0) {
+ git_config_free(cfg);
+ git_repository_free(repo);
+ const git_error *ge = git_error_last();
+ fprintf(stderr, "init_repo: set advertisePushOptions failed: %s (klass=%d)\n", ge && ge->message ? ge->message : "(no message)", ge ? ge->klass : 0);
+ bare_put_uint(writer, 23);
+ return -1;
+ }
+ git_config_free(cfg);
+
+ git_repository_free(repo);
+ fprintf(stderr, "init_repo: success for path='%s'\n", path);
+ bare_put_uint(writer, 0);
+ return 0;
+}
diff --git a/git2d/cmd_ref.c b/git2d/cmd_ref.c
new file mode 100644
index 0000000..f4bae4a
--- /dev/null
+++ b/git2d/cmd_ref.c
@@ -0,0 +1,113 @@
+/*-
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+ */
+
+#include "x.h"
+
+static int write_oid(struct bare_writer *writer, const git_oid *oid)
+{
+ return bare_put_data(writer, oid->id, GIT_OID_RAWSZ) == BARE_ERROR_NONE ? 0 : -1;
+}
+
+int cmd_resolve_ref(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char type[32] = { 0 };
+ char name[4096] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) type, sizeof(type) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ if (bare_get_data(reader, (uint8_t *) name, sizeof(name) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+
+ git_oid oid = { 0 };
+ int err = 0;
+
+ if (type[0] == '\0') {
+ git_object *obj = NULL;
+ err = git_revparse_single(&obj, repo, "HEAD^{commit}");
+ if (err != 0) {
+ bare_put_uint(writer, 12);
+ return -1;
+ }
+ git_commit *c = (git_commit *) obj;
+ git_oid_cpy(&oid, git_commit_id(c));
+ git_commit_free(c);
+ } else if (strcmp(type, "commit") == 0) {
+ err = git_oid_fromstr(&oid, name);
+ if (err != 0) {
+ bare_put_uint(writer, 12);
+ return -1;
+ }
+ } else if (strcmp(type, "branch") == 0) {
+ char fullref[4608];
+ snprintf(fullref, sizeof(fullref), "refs/heads/%s", name);
+ git_object *obj = NULL;
+ err = git_revparse_single(&obj, repo, fullref);
+ if (err != 0) {
+ bare_put_uint(writer, 12);
+ return -1;
+ }
+ git_commit *c = (git_commit *) obj;
+ git_oid_cpy(&oid, git_commit_id(c));
+ git_commit_free(c);
+ } else if (strcmp(type, "tag") == 0) {
+ char spec[4608];
+ snprintf(spec, sizeof(spec), "refs/tags/%s^{commit}", name);
+ git_object *obj = NULL;
+ err = git_revparse_single(&obj, repo, spec);
+ if (err != 0) {
+ bare_put_uint(writer, 12);
+ return -1;
+ }
+ git_commit *c = (git_commit *) obj;
+ git_oid_cpy(&oid, git_commit_id(c));
+ git_commit_free(c);
+ } else {
+ bare_put_uint(writer, 12);
+ return -1;
+ }
+
+ bare_put_uint(writer, 0);
+ return write_oid(writer, &oid);
+}
+
+int cmd_list_branches(git_repository *repo, struct bare_writer *writer)
+{
+ git_branch_iterator *it = NULL;
+ int err = git_branch_iterator_new(&it, repo, GIT_BRANCH_LOCAL);
+ if (err != 0) {
+ bare_put_uint(writer, 13);
+ return -1;
+ }
+ size_t count = 0;
+ git_reference *ref;
+ git_branch_t type;
+ while (git_branch_next(&ref, &type, it) == 0) {
+ count++;
+ git_reference_free(ref);
+ }
+ git_branch_iterator_free(it);
+
+ err = git_branch_iterator_new(&it, repo, GIT_BRANCH_LOCAL);
+ if (err != 0) {
+ bare_put_uint(writer, 13);
+ return -1;
+ }
+
+ bare_put_uint(writer, 0);
+ bare_put_uint(writer, count);
+ while (git_branch_next(&ref, &type, it) == 0) {
+ const char *name = NULL;
+ git_branch_name(&name, ref);
+ if (name == NULL)
+ name = "";
+ bare_put_data(writer, (const uint8_t *)name, strlen(name));
+ git_reference_free(ref);
+ }
+ git_branch_iterator_free(it);
+ return 0;
+}
diff --git a/git2d/cmd_tree.c b/git2d/cmd_tree.c
new file mode 100644
index 0000000..d18e817
--- /dev/null
+++ b/git2d/cmd_tree.c
@@ -0,0 +1,120 @@
+/*-
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+ */
+
+#include "x.h"
+
+int cmd_tree_list_by_oid(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ char hex[64] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) hex, sizeof(hex) - 1) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid oid;
+ if (git_oid_fromstr(&oid, hex) != 0) {
+ bare_put_uint(writer, 4);
+ return -1;
+ }
+ git_tree *tree = NULL;
+ if (git_tree_lookup(&tree, repo, &oid) != 0) {
+ bare_put_uint(writer, 4);
+ return -1;
+ }
+ size_t count = git_tree_entrycount(tree);
+ bare_put_uint(writer, 0);
+ bare_put_uint(writer, count);
+ for (size_t i = 0; i < count; i++) {
+ const git_tree_entry *e = git_tree_entry_byindex(tree, i);
+ const char *name = git_tree_entry_name(e);
+ uint32_t mode = git_tree_entry_filemode(e);
+ const git_oid *id = git_tree_entry_id(e);
+ bare_put_uint(writer, mode);
+ bare_put_data(writer, (const uint8_t *)name, strlen(name));
+ bare_put_data(writer, id->id, GIT_OID_RAWSZ);
+ }
+ git_tree_free(tree);
+ return 0;
+}
+
+int cmd_write_tree(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ uint64_t count = 0;
+ if (bare_get_uint(reader, &count) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_treebuilder *bld = NULL;
+ if (git_treebuilder_new(&bld, repo, NULL) != 0) {
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ for (uint64_t i = 0; i < count; i++) {
+ uint64_t mode = 0;
+ if (bare_get_uint(reader, &mode) != BARE_ERROR_NONE) {
+ git_treebuilder_free(bld);
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ char name[4096] = { 0 };
+ if (bare_get_data(reader, (uint8_t *) name, sizeof(name) - 1) != BARE_ERROR_NONE) {
+ git_treebuilder_free(bld);
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ uint8_t idraw[GIT_OID_RAWSZ] = { 0 };
+ if (bare_get_fixed_data(reader, idraw, GIT_OID_RAWSZ) != BARE_ERROR_NONE) {
+ git_treebuilder_free(bld);
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid id;
+ memcpy(id.id, idraw, GIT_OID_RAWSZ);
+ git_filemode_t fm = (git_filemode_t) mode;
+ if (git_treebuilder_insert(NULL, bld, name, &id, fm) != 0) {
+ git_treebuilder_free(bld);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ }
+ git_oid out;
+ if (git_treebuilder_write(&out, bld) != 0) {
+ git_treebuilder_free(bld);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ git_treebuilder_free(bld);
+ bare_put_uint(writer, 0);
+ bare_put_data(writer, out.id, GIT_OID_RAWSZ);
+ return 0;
+}
+
+int cmd_blob_write(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
+{
+ uint64_t sz = 0;
+ if (bare_get_uint(reader, &sz) != BARE_ERROR_NONE) {
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ uint8_t *data = (uint8_t *) malloc(sz);
+ if (!data) {
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ if (bare_get_fixed_data(reader, data, sz) != BARE_ERROR_NONE) {
+ free(data);
+ bare_put_uint(writer, 11);
+ return -1;
+ }
+ git_oid oid;
+ if (git_blob_create_frombuffer(&oid, repo, data, sz) != 0) {
+ free(data);
+ bare_put_uint(writer, 15);
+ return -1;
+ }
+ free(data);
+ bare_put_uint(writer, 0);
+ bare_put_data(writer, oid.id, GIT_OID_RAWSZ);
+ return 0;
+}
diff --git a/git2d/main.c b/git2d/main.c
index 9140c1d..8518960 100644
--- a/git2d/main.c
+++ b/git2d/main.c
@@ -34,9 +34,7 @@ int main(int argc, char **argv)
if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un))) {
if (errno == EADDRINUSE) {
unlink(argv[1]);
- if (bind
- (sock, (struct sockaddr *)&addr,
- sizeof(struct sockaddr_un)))
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)))
err(1, "bind");
} else {
err(1, "bind");
@@ -50,7 +48,8 @@ int main(int argc, char **argv)
if (pthread_attr_init(&pthread_attr) != 0)
err(1, "pthread_attr_init");
- if (pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED) != 0)
+ if (pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED)
+ != 0)
err(1, "pthread_attr_setdetachstate");
for (;;) {
@@ -69,7 +68,7 @@ int main(int argc, char **argv)
pthread_t thread;
- if (pthread_create (&thread, &pthread_attr, session, (void *)conn) != 0) {
+ if (pthread_create(&thread, &pthread_attr, session, (void *)conn) != 0) {
close(*conn);
free(conn);
warn("pthread_create");
diff --git a/git2d/session.c b/git2d/session.c
index 0a945ee..b5691d3 100644
--- a/git2d/session.c
+++ b/git2d/session.c
@@ -29,25 +29,30 @@ void *session(void *_conn)
goto close;
}
path[sizeof(path) - 1] = '\0';
-
- /* Open repo */
- git_repository *repo = NULL;
- err =
- git_repository_open_ext(&repo, path,
- GIT_REPOSITORY_OPEN_NO_SEARCH |
- GIT_REPOSITORY_OPEN_BARE |
- GIT_REPOSITORY_OPEN_NO_DOTGIT, NULL);
- if (err != 0) {
- bare_put_uint(&writer, 1);
- goto close;
- }
+ fprintf(stderr, "session: path='%s'\n", path);
/* Command */
uint64_t cmd = 0;
err = bare_get_uint(&reader, &cmd);
if (err != BARE_ERROR_NONE) {
bare_put_uint(&writer, 2);
- goto free_repo;
+ goto close;
+ }
+ fprintf(stderr, "session: cmd=%llu\n", (unsigned long long)cmd);
+
+ /* Repo init does not require opening an existing repo so let's just do it here */
+ if (cmd == 15) {
+ fprintf(stderr, "session: handling init for '%s'\n", path);
+ if (cmd_init_repo(path, &reader, &writer) != 0) {
+ }
+ goto close;
+ }
+
+ git_repository *repo = NULL;
+ err = git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT, NULL);
+ if (err != 0) {
+ bare_put_uint(&writer, 1);
+ goto close;
}
switch (cmd) {
case 1:
@@ -60,6 +65,66 @@ void *session(void *_conn)
if (err != 0)
goto free_repo;
break;
+ case 3:
+ err = cmd_resolve_ref(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 4:
+ err = cmd_list_branches(repo, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 5:
+ err = cmd_format_patch(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 6:
+ err = cmd_commit_diff(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 7:
+ err = cmd_merge_base(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 8:
+ err = cmd_log(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 9:
+ err = cmd_tree_list_by_oid(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 10:
+ err = cmd_write_tree(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 11:
+ err = cmd_blob_write(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 12:
+ err = cmd_commit_tree_oid(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 13:
+ err = cmd_commit_create(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
+ case 14:
+ err = cmd_update_ref(repo, &reader, &writer);
+ if (err != 0)
+ goto free_repo;
+ break;
case 0:
bare_put_uint(&writer, 3);
goto free_repo;
diff --git a/git2d/x.h b/git2d/x.h
index a6da50f..b5b299a 100644
--- a/git2d/x.h
+++ b/git2d/x.h
@@ -26,13 +26,29 @@ typedef struct {
int fd;
} conn_io_t;
-
bare_error conn_read(void *buffer, void *dst, uint64_t sz);
bare_error conn_write(void *buffer, const void *src, uint64_t sz);
-void * session(void *_conn);
+void *session(void *_conn);
+
+int cmd_index(git_repository * repo, struct bare_writer *writer);
+int cmd_treeraw(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+
+int cmd_resolve_ref(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_list_branches(git_repository * repo, struct bare_writer *writer);
+int cmd_format_patch(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_commit_diff(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_merge_base(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_log(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+
+int cmd_tree_list_by_oid(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_write_tree(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_blob_write(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+
+int cmd_commit_tree_oid(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_commit_create(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_update_ref(git_repository * repo, struct bare_reader *reader, struct bare_writer *writer);
-int cmd_index(git_repository *repo, struct bare_writer *writer);
-int cmd_treeraw(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer);
+int cmd_init_repo(const char *path, struct bare_reader *reader, struct bare_writer *writer);
-#endif // X_H
+#endif // X_H