| ... | @@ -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 | } |
| 126 | | 126 | |
| 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 | | |
| 131 | pub 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 | | | |
| 140 | pub 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 { | 127 | pub 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 | }; |
| 1661 | | 1697 | |
| 1662 | const z = @cImport({ | 1698 | const 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. |
| | 2153 | pub 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 | } |