Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
9ddff5ced5151938a0c6c7933bac8d9fdad315c3
Author
Runxi Yu <me@runxiyu.org>
Author date
Fri, 02 May 2025 10:46:11 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Fri, 02 May 2025 10:46:11 +0800
Actions
git2c: Some perror
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>

// TODO: Make the C part report detailed error messages too

package git2c

import "errors"

var (
	Success            error
	ErrUnknown         = errors.New("git2c: unknown error")
	ErrPath            = errors.New("git2c: get tree entry by path failed")
	ErrRevparse        = errors.New("git2c: revparse failed")
	ErrReadme          = errors.New("git2c: no readme")
	ErrBlobExpected    = errors.New("git2c: blob expected")
	ErrEntryToObject   = errors.New("git2c: tree entry to object conversion failed")
	ErrBlobRawContent  = errors.New("git2c: get blob raw content failed")
	ErrRevwalk         = errors.New("git2c: revwalk failed")
	ErrRevwalkPushHead = errors.New("git2c: revwalk push head failed")
	ErrBareProto       = errors.New("git2c: bare protocol error")
)

func Perror(errno uint) error {
	switch errno {
	case 0:
		return Success
	case 3:
		return ErrPath
	case 4:
		return ErrRevparse
	case 5:
		return ErrReadme
	case 6:
		return ErrBlobExpected
	case 7:
		return ErrEntryToObject
	case 8:
		return ErrBlobRawContent
	case 9:
		return ErrRevwalk
	case 10:
		return ErrRevwalkPushHead
	case 11:
		return ErrBareProto
	}
	return ErrUnknown
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

int
cmd_index(git_repository *repo, struct bare_writer *writer)
{
	/* HEAD tree */

	git_object *obj = NULL;
	int err = git_revparse_single(&obj, repo, "HEAD^{tree}");
	if (err != 0) {
		bare_put_uint(writer, 4);
		return -1;
	}
	git_tree *tree = (git_tree *) obj;

	/* README */

	git_tree_entry *entry = NULL;
	err = git_tree_entry_bypath(&entry, tree, "README.md");
	if (err != 0) {
		bare_put_uint(writer, 5);
		git_tree_free(tree);
		return -1;
	}
	git_otype objtype = git_tree_entry_type(entry);
	if (objtype != GIT_OBJECT_BLOB) {
		bare_put_uint(writer, 6);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}
	git_object *obj2 = NULL;
	err = git_tree_entry_to_object(&obj2, repo, entry);
	if (err != 0) {
		bare_put_uint(writer, 7);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}
	git_blob *blob = (git_blob *) obj2;
	const void *content = git_blob_rawcontent(blob);
	if (content == NULL) {
		bare_put_uint(writer, 8);
		git_blob_free(blob);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}
	bare_put_uint(writer, 0);
	bare_put_data(writer, content, git_blob_rawsize(blob));

	/* Commits */

	/* TODO BUG: This might be a different commit from the displayed README due to races */

	git_revwalk *walker = NULL;
	if (git_revwalk_new(&walker, repo) != 0) {
		bare_put_uint(writer, 9);
		git_blob_free(blob);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}

	if (git_revwalk_push_head(walker) != 0) {
		bare_put_uint(writer, 9);
		git_revwalk_free(walker);
		git_blob_free(blob);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}

	int count = 0;
	git_oid oid;
	while (count < 3 && git_revwalk_next(&oid, walker) == 0) {
		git_commit *commit = NULL;
		if (git_commit_lookup(&commit, repo, &oid) != 0)
			break;

		const char *msg = git_commit_summary(commit);
		const git_signature *author = git_commit_author(commit);

		/* ID */
		bare_put_data(writer, oid.id, GIT_OID_RAWSZ);

		/* Title */
		size_t msg_len = msg ? strlen(msg) : 0;
		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));

		/* Author's email */
		const char *author_email = author ? 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 */
		time_t time = git_commit_time(commit);
		char timebuf[64];
		struct tm *tm = localtime(&time);
		if (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));

		git_commit_free(commit);
		count++;
	}

	git_revwalk_free(walker);
	git_blob_free(blob);
	git_tree_entry_free(entry);
	git_tree_free(tree);

	return 0;
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

int
cmd_treeraw(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
{
	/* Path */
	char path[4096] = {0};
	int err = bare_get_data(reader, (uint8_t *)path, sizeof(path) - 1);
	if (err != BARE_ERROR_NONE) {
		bare_put_uint(writer, 10);
		bare_put_uint(writer, 11);
		return -1;
	}
	path[sizeof(path) - 1] = '\0';

	/* HEAD^{tree} */
	git_object *head_obj = NULL;
	err = git_revparse_single(&head_obj, repo, "HEAD^{tree}");
	if (err != 0) {
		bare_put_uint(writer, 11);
		bare_put_uint(writer, 4);
		return -1;
	}
	git_tree *tree = (git_tree *)head_obj;

	/* Path in tree */
	git_tree_entry *entry = NULL;
	git_otype objtype;
	if (strlen(path) == 0) {
		entry = NULL;
		objtype = GIT_OBJECT_TREE;
	} else {
		err = git_tree_entry_bypath(&entry, tree, path);
		if (err != 0) {
			bare_put_uint(writer, 3);
			git_tree_free(tree);
			return 0;
		}
		objtype = git_tree_entry_type(entry);
	}

	if (objtype == GIT_OBJECT_TREE) {
		/* Tree */
		git_object *tree_obj = NULL;
		if (entry == NULL) {
			tree_obj = (git_object *)tree;
		} else {
			err = git_tree_entry_to_object(&tree_obj, repo, entry);
			if (err != 0) {
				bare_put_uint(writer, 3);
				bare_put_uint(writer, 7);
				goto cleanup;
			}
		}
		git_tree *subtree = (git_tree *)tree_obj;

		size_t count = git_tree_entrycount(subtree);
		bare_put_uint(writer, 0);
		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 char *name = git_tree_entry_name(subentry);
			git_otype type = git_tree_entry_type(subentry);
			uint32_t mode = git_tree_entry_filemode(subentry);

			uint8_t entry_type = 0;
			uint64_t size = 0;

			if (type == GIT_OBJECT_TREE) {
				entry_type = 1;
			} else if (type == GIT_OBJECT_BLOB) {
				entry_type = 2;

				git_object *subobj = NULL;
				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);
				}
			}

			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));
		}
		if (entry != NULL) {
			git_tree_free(subtree);
		}
	} else if (objtype == GIT_OBJECT_BLOB) {
		/* Blob */
		git_object *blob_obj = NULL;
		err = git_tree_entry_to_object(&blob_obj, repo, entry);
		if (err != 0) {
			bare_put_uint(writer, 3);
			bare_put_uint(writer, 7);
			goto cleanup;
		}
		git_blob *blob = (git_blob *)blob_obj;
		const void *content = git_blob_rawcontent(blob);
		if (content == NULL) {
			bare_put_uint(writer, 3);
			bare_put_uint(writer, 8);
			git_blob_free(blob);
			goto cleanup;
		}
		bare_put_uint(writer, 0);
		bare_put_uint(writer, 2);
		bare_put_data(writer, content, git_blob_rawsize(blob));
		git_blob_free(blob);
	} else {
		/* Unknown */
		bare_put_uint(writer, 3);
		bare_put_uint(writer, -1);
	}

cleanup:
	if (entry != NULL)
		git_tree_entry_free(entry);
	git_tree_free(tree);
	return 0;
}