authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-07-02 13:24:52 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-07-02 13:24:52 -07:00
loge4c3ea47cec1669dc173f0573160901de690e5e7
tree636631886fd46c8c3f67e55365c9c6dab66e88a2
parentfb0cc022af2799fee172d9e8bfd37561ad68c6e6
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

replace revListAll with a native version in Repository


1 files changed, 74 insertions(+), 13 deletions(-)

git.zig+74-13
...@@ -124,19 +124,6 @@ pub fn ensureObjId(comptime T: type, input: string) T {...@@ -124,19 +124,6 @@ pub fn ensureObjId(comptime T: type, input: string) T {
124 return .{ .id = input[0..40] };124 return .{ .id = input[0..40] };
125}125}
126126
127// TODO make this inspect .git/objects manually
128// TODO make a version of this that accepts an array of sub_paths and searches all of them at once, so as to not lose spot in history when searching for many old paths
129// https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
130// https://git-scm.com/book/en/v2/Git-Internals-Packfiles
131pub fn revListAll(alloc: std.mem.Allocator, dir: nfs.Dir, from: CommitId, sub_path: string) !string {
132 const t = tracer.trace(@src(), " {s} -- {s}", .{ from.id, sub_path });
133 defer t.end();
134
135 const result = try root.child_process.run(alloc, dir, .ignore, .pipe, .pipe, 1024 * 1024 * 10, &.{ "git", "rev-list", from.id, "--", sub_path });
136 std.debug.assert(result.term == .exited and result.term.exited == 0);
137 return std.mem.trimEnd(u8, result.stdout, "\n");
138}
139
140pub fn parseCommit(alloc: std.mem.Allocator, commitfile: string, mailmap: *const std.hash_map.StringHashMapUnmanaged([]const u8), mailmap_names: *const std.hash_map.StringHashMapUnmanaged([]const u8)) !Commit {127pub fn parseCommit(alloc: std.mem.Allocator, commitfile: string, mailmap: *const std.hash_map.StringHashMapUnmanaged([]const u8), mailmap_names: *const std.hash_map.StringHashMapUnmanaged([]const u8)) !Commit {
141 const t = tracer.trace(@src(), "", .{});128 const t = tracer.trace(@src(), "", .{});
142 defer t.end();129 defer t.end();
...@@ -1657,6 +1644,55 @@ pub const Repository = struct {...@@ -1657,6 +1644,55 @@ pub const Repository = struct {
1657 };1644 };
1658 return diffFileIterator(r, writable, parentid, commitid, S);1645 return diffFileIterator(r, writable, parentid, commitid, S);
1659 }1646 }
1647
1648 pub fn revListAll(r: *Repository, alloc: std.mem.Allocator, from: CommitId, sub_path: string) ![]const u8 {
1649 const t = tracer.trace(@src(), " {s} -- {s}", .{ from.id, sub_path });
1650 defer t.end();
1651
1652 var list: std.ArrayList(u8) = .empty;
1653 errdefer list.deinit(alloc);
1654
1655 const base = try r.getCommitA(from.id, .no_cache);
1656 const base_tree = try r.getTreeA(base.tree.id, .no_cache);
1657 const base_id, const base_id_parent = (try idFor(r, base_tree, sub_path)).?;
1658
1659 var prev_prev_commit: ?*Commit = null;
1660 var prev_commit = base;
1661 var prev_tree = base_id_parent;
1662 var prev_id = try r.gpa.dupe(u8, base_id);
1663 defer r.gpa.free(prev_id);
1664
1665 while (true) {
1666 if (prev_commit.parents.len == 0) {
1667 try list.appendSlice(alloc, prev_prev_commit.?.parents[0].id ++ "\n");
1668 break;
1669 }
1670 const next_commit = try r.getCommitA(prev_commit.parents[0].id, .no_cache);
1671 const next_commit_tree = try r.getTreeA(next_commit.tree.id, .no_cache);
1672 const next_id, const next_tree = try idFor(r, next_commit_tree, sub_path) orelse {
1673 try list.appendSlice(alloc, prev_prev_commit.?.parents[0].id ++ "\n");
1674 break;
1675 };
1676 if (std.mem.eql(u8, next_id, prev_id)) {
1677 if (prev_prev_commit) |p| p.destroy(r);
1678 prev_prev_commit = prev_commit;
1679 prev_commit = next_commit;
1680 prev_tree = next_tree;
1681 continue;
1682 }
1683 try list.appendSlice(alloc, prev_prev_commit.?.parents[0].id ++ "\n");
1684 if (prev_prev_commit) |p| p.destroy(r);
1685 prev_prev_commit = prev_commit;
1686 prev_commit = next_commit;
1687 prev_tree = next_tree;
1688
1689 r.gpa.free(prev_id);
1690 prev_id = try r.gpa.dupe(u8, next_id);
1691 continue;
1692 }
1693
1694 return list.toOwnedSlice(alloc);
1695 }
1660};1696};
16611697
1662const z = @cImport({1698const z = @cImport({
...@@ -2112,3 +2148,28 @@ const PathListNode = struct {...@@ -2112,3 +2148,28 @@ const PathListNode = struct {
2112 try writable.writevAll(&.{ "/", node.data });2148 try writable.writevAll(&.{ "/", node.data });
2113 }2149 }
2114};2150};
2151
2152/// Consumes 'tree' and returns a new owned 'Tree' in the tuple.
2153pub fn idFor(r: *Repository, tree: *Tree, sub_path: []const u8) !?struct { Id, *Tree } {
2154 const s_idx = std.mem.indexOfScalar(u8, sub_path, '/');
2155 const seg = sub_path[0 .. s_idx orelse sub_path.len];
2156 const obj = tree.get(seg) orelse {
2157 tree.destroy(r);
2158 return null;
2159 };
2160 switch (obj.id) {
2161 .tree => |id| {
2162 defer tree.destroy(r);
2163 if (s_idx == null) return null;
2164 const new_tree = try r.getTreeA(id.id, .no_cache);
2165 return idFor(r, new_tree, sub_path[s_idx.? + 1 ..]);
2166 },
2167 else => |id| {
2168 if (s_idx != null) {
2169 tree.destroy(r);
2170 return null;
2171 }
2172 return .{ id.erase(), tree };
2173 },
2174 }
2175}