srctree

Gregory Mullen parent bb85448b 9f1c30ce
tags unpacking and /repo/{name}/tags page

If you're reading this, I'm sorry! This is a pretty trash implementation and it leaks like a sieve. But I'm still trying to decide how hard I want to try, to give the git internals a zero alloc API.
inlinesplit
src/endpoints/repos.zig added: 137, removed: 8, total 129
@@ -774,6 +774,33 @@ fn tree(ctx: *Context, repo: *Git.Repo, files: *Git.Tree) Error!void {
try ctx.sendPage(&page);
}
 
const TagPage = Template.PageData("repo-tags.html");
 
fn tags(ctx: *Context) Error!void {
ctx.response.redirect("/", true) catch return error.Unrouteable;
const rd = RouteData.make(&ctx.uri) orelse return error.Unrouteable;
 
var cwd = std.fs.cwd();
const filename = try aPrint(ctx.alloc, "./repos/{s}", .{rd.name});
const dir = cwd.openDir(filename, .{}) catch return error.Unknown;
var repo = Git.Repo.init(dir) catch return error.Unknown;
repo.loadData(ctx.alloc) catch return error.Unknown;
defer repo.raze(ctx.alloc);
 
repo.loadTags(ctx.alloc) catch unreachable;
 
const tstack = try ctx.alloc.alloc(Template.Structs.Tags, repo.tags.?.len);
 
for (repo.tags.?, tstack) |tag, *html| {
html.name = tag.name;
}
 
var btns = navButtons(ctx) catch return error.Unknown;
var page = TagPage.init(.{
.meta_head = .{ .open_graph = .{} },
.body_header = .{ .nav = .{ .nav_auth = undefined, .nav_buttons = &btns } },
.upstream = null,
.tags = tstack,
});
 
try ctx.sendPage(&page);
}
 
src/git.zig added: 137, removed: 8, total 129
@@ -1,9 +1,12 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const startsWith = std.mem.startsWith;
const splitScalar = std.mem.splitScalar;
const eql = std.mem.eql;
const indexOf = std.mem.indexOf;
const zlib = std.compress.zlib;
const hexLower = std.fmt.fmtSliceHexLower;
const bufPrint = std.fmt.bufPrint;
const AnyReader = std.io.AnyReader;
 
pub const Actor = @import("git/actor.zig");
@@ -111,6 +114,8 @@ pub const Repo = struct {
refs: []Ref,
current: ?[]u8 = null,
head: ?Ref = null,
// Leaks, badly
tags: ?[]Tag = null,
 
repo_name: ?[]const u8 = null,
 
@@ -376,6 +381,75 @@ pub const Repo = struct {
return self.head.?;
}
 
fn loadTag(self: *Repo, a: Allocator, lsha: SHA) !Tag {
var sha: [20]u8 = lsha[0..20].*;
if (lsha.len == 40) {
for (&sha, 0..) |*s, i| {
s.* = try std.fmt.parseInt(u8, lsha[i * 2 .. (i + 1) * 2], 16);
}
}
const tag_blob = try self.findBlob(a, sha[0..]);
return try Tag.fromSlice(lsha, tag_blob);
}
 
pub fn loadTags(self: *Repo, a: Allocator) !void {
var rbuf: [2048]u8 = undefined;
 
var tagdir = try self.dir.openDir("refs/tags", .{ .iterate = true });
 
const rpath = try tagdir.realpath(".", &rbuf);
std.debug.print("ready {s}\n", .{rpath});
 
const pk_refs: ?[]const u8 = self.dir.readFileAlloc(a, "packed-refs", 0xffff) catch |err| pk: {
std.debug.print("packed-refs {any}\n", .{err});
break :pk null;
};
defer if (pk_refs) |pr| a.free(pr);
 
defer tagdir.close();
var itr = tagdir.iterate();
var count: usize = if (pk_refs) |p| std.mem.count(u8, p, "refs/tags/") else 0;
while (try itr.next()) |next| {
std.debug.print("next {s} {any}\n", .{ next.name, next.kind });
if (next.kind != .file) continue;
count += 1;
}
if (count == 0) return;
self.tags = try a.alloc(Tag, count);
errdefer a.free(self.tags.?);
errdefer self.tags = null;
 
var index: usize = 0;
if (pk_refs) |pkrefs| {
var lines = splitScalar(u8, pkrefs, '\n');
while (lines.next()) |line| {
if (indexOf(u8, line, "refs/tags/") != null) {
self.tags.?[index] = try self.loadTag(a, line[0..40]);
index += 1;
}
}
}
 
itr.reset();
while (try itr.next()) |next| {
var fnbuf: [2048]u8 = undefined;
if (next.kind != .file) continue;
const fname = try bufPrint(&fnbuf, "refs/tags/{s}", .{next.name});
var conbuf: [44]u8 = undefined;
const contents = self.dir.readFile(fname, &conbuf) catch |err| {
std.debug.print("unexpected tag format for {s}\n", .{fname});
return err;
};
if (contents.len != 40) {
std.debug.print("unexpected tag format for {s}\n", .{fname});
return error.InvalidTagFound;
}
self.tags.?[index] = try self.loadTag(a, contents);
index += 1;
}
if (index != self.tags.?.len) return error.UnexpectedError;
}
 
pub fn resolvePartial(_: *const Repo, _: SHA) !SHA {
return error.NotImplemented;
}
@@ -439,6 +513,8 @@ pub const Repo = struct {
.branch => |b| a.free(b.name),
else => {}, //a.free(h);
};
 
// TODO self.tags leaks, badly
}
 
// functions that might move or be removed...
@@ -543,7 +619,8 @@ pub const Tag = struct {
};
}
 
pub fn fromReader(sha: SHA, reader: AnyReader) !Tag {
/// LOL, don't use this
fn fromReader(sha: SHA, reader: AnyReader) !Tag {
var buffer: [0xFFFF]u8 = undefined;
const len = try reader.readAll(&buffer);
return try fromSlice(sha, buffer[0..len]);
 
src/git/pack.zig added: 137, removed: 8, total 129
@@ -279,10 +279,10 @@ pub fn loadObj(self: Pack, a: Allocator, offset: usize, repo: Repo) Error![]u8 {
const h = parseObjHeader(&reader);
 
switch (h.kind) {
.commit, .tree, .blob => return loadBlob(a, &reader) catch return error.PackCorrupt,
.commit, .tree, .blob, .tag => return loadBlob(a, &reader) catch return error.PackCorrupt,
.ofs_delta => return try self.loadDelta(a, &reader, offset, repo),
.ref_delta => return try self.loadRefDelta(a, &reader, offset, repo),
else => {
.invalid => {
std.debug.print("obj type ({}) not implemened\n", .{h.kind});
unreachable; // not implemented
},
 
filename was Deleted added: 137, removed: 8, total 129
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>srctree</title>
<_meta_head.html>
</head>
<body>
<_body_header.html>
<content>
<div class="act-btns">
<With Upstream>
<a class="btn" href="<URI>">Go To Upstream</a>
</With>
<a class="btn" href="<Repo_name ORNULL>/commits">commits</a>
<a class="btn" href="<Repo_name ORNULL>/issues/new">New Issue</a>
<a class="btn" href="<Repo_name ORNULL>/diffs/new">Suggest Diff</a>
</div>
 
<For Tags>
<Name><br>
</For>
 
</content>
</body>
</html>