Lindenii Project Forge
Login

hare-git

Git library for Hare
Commit info
ID
4f2ca1f4f146d3956969571454cb892852ff04fd
Author
Runxi Yu <me@runxiyu.org>
Author date
Sun, 21 Sep 2025 14:52:27 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Sun, 21 Sep 2025 15:07:19 +0800
Actions
Add random test program to write
use errors;
use fmt;
use fs;
use getopt;
use io;
use os;
use strings;
use time;
use time::date;

use git;

fn dupu8(s: str) []u8 = {
	const src = strings::toutf8(s);
	let dst: []u8 = alloc([], len(src))!;
	dst[..] = src;
	return dst;
};

fn mkident(name: str, email: str) git::ident = {
	let now = time::now(time::clock::REALTIME);
	let d = date::localnow();
	let z = date::zone(&d);
	let ofs = (z.zoff / time::SECOND): i32; // seconds?

	return git::ident{
		name = dupu8(name),
		email = dupu8(email),
		when = now.sec,
		ofs = ofs,
	};
};

fn bail(msg: str) never = {
	fmt::errorfln("fatal: {}", msg)!;
	os::exit(1);
};

fn usage(cmd: *getopt::command) void = {
	getopt::printusage(os::stderr, os::args[0], cmd.help)!;
};

export fn main() void = {
	let repo_path: (str | void) = void;

	let cmd = getopt::parse(
		os::args,
		"create a test commit with random blobs and a tree, then print its OID",
		('r', "repo", "path to bare repo or .git"),
	);
	defer getopt::finish(&cmd);

	for (let opt .. cmd.opts) {
		switch (opt.0) {
		case 'r' =>
			repo_path = opt.1;
		case =>
			abort();
		};
	};

	let rp: str = match (repo_path) {
	case void =>
		usage(&cmd);
		os::exit(2);
	case let s: str =>
		yield s;
	};

	let r = match (git::repo_open(rp)) {
	case let rr: git::repo =>
		fmt::errorfln("opened repo: {}", rp)!;
		yield rr;
	case let fe: fs::error =>
		fmt::errorfln("open repo: {}", fs::strerror(fe))!;
		os::exit(1);
	};
	defer git::repo_close(r);

	// Blobs

	const blob1_body = "blabla\n";
	const blob2_body = "woah\n";

	let b1 = match (git::blob_parse(strings::toutf8(blob1_body))) {
	case let bb: git::blob =>
		fmt::errorfln("parsed blob #1 ({} bytes)", len(strings::toutf8(blob1_body)))!;
		yield bb;
	case =>
		bail("OOM parsing blob #1");
	};

	let id1 = match (git::loose_write(r, b1)) {
	case let oid1: git::oid =>
		const hex = git::oid_stringify(oid1)!;
		defer free(hex);
		fmt::errorfln("wrote blob #1: {}", hex)!;
		yield oid1;
	case let fe: fs::error =>
		fmt::errorfln("write blob #1: {}", fs::strerror(fe))!;
		os::exit(1);
	case let ioe: io::error =>
		fmt::errorfln("write blob #1: {}", io::strerror(ioe))!;
		os::exit(1);
	case let ee: errors::invalid =>
		fmt::errorfln("write blob #1: {}", errors::strerror(ee))!;
		os::exit(1);
	case =>
		bail("write blob #1: OOM");
	};
	git::blob_finish(b1);

	let b2 = match (git::blob_parse(strings::toutf8(blob2_body))) {
	case let bb: git::blob =>
		fmt::errorfln("parsed blob #2 ({} bytes)", len(strings::toutf8(blob2_body)))!;
		yield bb;
	case =>
		bail("OOM parsing blob #2");
	};

	let id2 = match (git::loose_write(r, b2)) {
	case let oid2: git::oid =>
		const hex = git::oid_stringify(oid2)!;
		defer free(hex);
		fmt::errorfln("wrote blob #2: {}", hex)!;
		yield oid2;
	case let fe: fs::error =>
		fmt::errorfln("write blob #2: {}", fs::strerror(fe))!;
		os::exit(1);
	case let ioe: io::error =>
		fmt::errorfln("write blob #2: {}", io::strerror(ioe))!;
		os::exit(1);
	case let ee: errors::invalid =>
		fmt::errorfln("write blob #2: {}", errors::strerror(ee))!;
		os::exit(1);
	case =>
		bail("write blob #2: OOM");
	};
	git::blob_finish(b2);

	// Tree entries

	const MODE_BLOB: u32 = (0o100644: u32);

	let ents: []git::tree_entry = alloc([], 2)!;
	append(ents, git::tree_entry{
		mode = MODE_BLOB,
		name = dupu8("a.txt"),
		oid = id1,
	})!;
	append(ents, git::tree_entry{
		mode = MODE_BLOB,
		name = dupu8("b.txt"),
		oid = id2,
	})!;
	fmt::errorfln("constructed 2 tree entries")!;

	let t = git::tree{
		entries = ents,
	};

	// Tree

	let tree_id = match (git::loose_write(r, t)) {
	case let toid: git::oid =>
		const hex = git::oid_stringify(toid)!;
		defer free(hex);
		fmt::errorfln("wrote tree: {}", hex)!;
		yield toid;
	case let fe: fs::error =>
		fmt::errorfln("write tree: {}", fs::strerror(fe))!;
		os::exit(1);
	case let ioe: io::error =>
		fmt::errorfln("write tree: {}", io::strerror(ioe))!;
		os::exit(1);
	case let ee: errors::invalid =>
		fmt::errorfln("write tree: {}", errors::strerror(ee))!;
		os::exit(1);
	case =>
		bail("write tree: OOM");
	};
	git::tree_finish(t);

	// Commit

	let author = mkident("Hare Example", "author@example.com");
	let committer = mkident("Hare Example", "author@example.com");

	let parents: []git::oid = [];
	let msg = dupu8("adfawjahlejfljljlj\n");

	let c = git::commit{
		tree = tree_id,
		parents = parents,
		author = author,
		committer = committer,
		message = msg,
	};
	fmt::errorfln("constructed commit object")!;

	let commit_id = match (git::loose_write(r, c)) {
	case let coid: git::oid =>
		const hex = git::oid_stringify(coid)!;
		defer free(hex);
		fmt::errorfln("wrote commit: {}", hex)!;
		fmt::println(hex)!;
		yield coid;
	case let fe: fs::error =>
		fmt::errorfln("write commit: {}", fs::strerror(fe))!;
		os::exit(1);
	case let ioe: io::error =>
		fmt::errorfln("write commit: {}", io::strerror(ioe))!;
		os::exit(1);
	case let ee: errors::invalid =>
		fmt::errorfln("write commit: {}", errors::strerror(ee))!;
		os::exit(1);
	case =>
		bail("write commit: OOM");
	};

	git::commit_finish(c);
};