Lindenii Project Forge
Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.
/git/obj_commit.ha (raw)
use bytes;
use encoding::utf8;
use errors;
use strconv;
use strings;
// A Git commit object.
export type commit = struct {
tree: oid,
parents: []oid,
author: ident,
committer: ident,
message: []u8,
// other raw headers?
};
// Frees resources associated with a [[commit]].
export fn commit_finish(c: commit) void = {
free(c.parents);
ident_finish(c.author);
ident_finish(c.committer);
free(c.message);
};
// Parses a commit from its raw data and object ID.
export fn parse_commit(
body: []u8,
) (commit | errors::invalid | strconv::invalid | strconv::overflow | utf8::invalid | nomem) = {
let c = commit {
tree = [0...],
parents = [],
author = ident { name = [], email = [], when = 0, ofs = 0 },
committer = ident { name = [], email = [], when = 0, ofs = 0 },
message = [],
};
let i = 0z;
for (true) {
let mrel = bytes::index(body[i..], '\n');
if (mrel is void) {
return errors::invalid;
};
let rel = mrel: size;
const line = body[i .. i + rel];
if (len(line) == 0) {
i += rel + 1z;
break;
};
if (bytes::hasprefix(line, strings::toutf8("tree "))) {
const hex = strings::fromutf8(line[5..])?;
match (parse_oid(hex)) {
case let o: oid =>
c.tree = o;
case nomem =>
return nomem;
case =>
return errors::invalid;
};
} else if (bytes::hasprefix(line, strings::toutf8("parent "))) {
const hex = strings::fromutf8(line[7..])?;
match (parse_oid(hex)) {
case let o: oid =>
append(c.parents, o)!;
case nomem =>
return nomem;
case =>
return errors::invalid;
};
} else if (bytes::hasprefix(line, strings::toutf8("author "))) {
const per = parse_ident(line[7..])?;
ident_finish(c.author);
c.author = per;
} else if (bytes::hasprefix(line, strings::toutf8("committer "))) {
const per = parse_ident(line[10..])?;
ident_finish(c.committer);
c.committer = per;
} else if (
bytes::hasprefix(line, strings::toutf8("gpgsig ")) ||
bytes::hasprefix(line, strings::toutf8("gpgsig-sha256 "))
) {
i += rel + 1z;
for (true) {
if (i >= len(body)) {
return errors::invalid;
};
let mnext = bytes::index(body[i..], '\n');
if (mnext is void) {
return errors::invalid;
};
let next = mnext: size;
if (body[i] != ' ') {
break;
};
i += next + 1z;
};
continue;
};
i += rel + 1z;
};
c.message = alloc(body[i..]...)?;
return c;
};