authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-11 23:52:22 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-11 23:54:03 -07:00
logce95745420ddca38d636ea152df4698c1cc55c64
tree8662554c1ff0d6ef9a1ed02d686c813099eb5892
parent0d3672c5140a76b4dfdffdf7c72f37f7218bee51
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

Repository: add getHeads and getTags


1 files changed, 52 insertions(+), 58 deletions(-)

git.zig+52-58
......@@ -770,64 +770,6 @@ pub const TreeDiff = struct {
770770 };
771771};
772772
773pub fn getBranches(alloc: std.mem.Allocator, dir: nfs.Dir) ![]Ref {
774 const t = tracer.trace(@src(), "", .{});
775 defer t.end();
776
777 const result = try std.process.Child.run(.{
778 .allocator = alloc,
779 .cwd_dir = dir.to_std(),
780 .argv = &.{ "git", "show-ref", "--heads" },
781 .max_output_bytes = 1024 * 1024 * 1024,
782 });
783 std.debug.assert(result.term == .Exited and result.term.Exited == 0);
784 const output = std.mem.trimRight(u8, result.stdout, "\n");
785 var iter = std.mem.splitScalar(u8, output, '\n');
786 var list = std.ArrayList(Ref).init(alloc);
787 errdefer list.deinit();
788 while (iter.next()) |line| {
789 var jter = std.mem.splitScalar(u8, line, ' ');
790 try list.append(Ref{
791 .oid = ensureObjId(CommitId, jter.next().?).id,
792 .label = extras.trimPrefixEnsure(jter.rest(), "refs/heads/").?,
793 });
794 }
795 return list.toOwnedSlice();
796}
797
798pub fn getTags(alloc: std.mem.Allocator, dir: nfs.Dir) ![]Ref {
799 const t = tracer.trace(@src(), "", .{});
800 defer t.end();
801
802 // 97bc4b5f87656a34139e1a8122866c8c5b432598 refs/tags/1.2.5
803 // a450a23e318c5a8fcba5a52c8fdc2e23584650b3 refs/tags/1.2.5^{}
804 // 71264720050572b7bad24532ff39951f47d9296a refs/tags/15.3.1
805 // 7dfd3948a9095f0253bfba60fed52895ffbf84bb refs/tags/15.3.2
806 const result = try std.process.Child.run(.{
807 .allocator = alloc,
808 .cwd_dir = dir.to_std(),
809 .argv = &.{ "git", "show-ref", "--tags" },
810 .max_output_bytes = 1024 * 1024 * 1024,
811 });
812 if (result.term == .Exited and result.term.Exited == 1) return &.{}; // show-ref exits 1 when there are no tags
813 std.debug.assert(result.term == .Exited and result.term.Exited == 0);
814 const output = std.mem.trimRight(u8, result.stdout, "\n");
815 var iter = std.mem.splitScalar(u8, output, '\n');
816 var list = std.ArrayList(Ref).init(alloc);
817 errdefer list.deinit();
818 while (iter.next()) |line| {
819 var jter = std.mem.splitScalar(u8, line, ' ');
820 const id = ensureObjId(CommitId, jter.next().?).id;
821 const label = extras.trimPrefixEnsure(jter.rest(), "refs/tags/").?;
822
823 try list.append(Ref{
824 .oid = id,
825 .label = label,
826 });
827 }
828 return list.toOwnedSlice();
829}
830
831773pub const Ref = struct {
832774 oid: Id,
833775 label: string,
......@@ -1248,4 +1190,56 @@ pub const Repository = struct {
12481190 }
12491191 return null;
12501192 }
1193
1194 pub fn getHeads(r: *Repository, arena: std.mem.Allocator) ![]Ref {
1195 return r.getRefs(arena, "heads");
1196 }
1197
1198 pub fn getTags(r: *Repository, arena: std.mem.Allocator) ![]Ref {
1199 return r.getRefs(arena, "tags");
1200 }
1201
1202 pub fn getRefs(r: *Repository, arena: std.mem.Allocator, comptime kind: [:0]const u8) ![]Ref {
1203 var map: std.StringArrayHashMapUnmanaged(Id) = .empty;
1204 try r.addPackedRefs(&map, arena, kind);
1205 try r.addDirRefs(&map, arena, kind);
1206 var list: std.ArrayListUnmanaged(Ref) = .empty;
1207 try list.ensureUnusedCapacity(arena, map.count());
1208 for (map.keys(), map.values()) |label, oid| list.appendAssumeCapacity(.{ .oid = oid, .label = label });
1209 return list.items;
1210 }
1211
1212 fn addPackedRefs(r: *Repository, map: *std.StringArrayHashMapUnmanaged(Id), arena: std.mem.Allocator, comptime kind: [:0]const u8) !void {
1213 var file = try r.gitdir.openFile("packed-refs", .{});
1214 defer file.close();
1215 const content = try file.mmap();
1216 defer nfs.munmap(content);
1217 var iter = std.mem.splitScalar(u8, content, '\n');
1218 while (iter.next()) |line| {
1219 if (line.len == 0) break;
1220 if (line[0] == '^') continue;
1221 if (line[0] == '#') continue;
1222 std.debug.assert(extras.matchesAll(u8, line[0..40], std.ascii.isHex));
1223 std.debug.assert(line[40] == ' ');
1224 const rest = extras.trimPrefixEnsure(line[41..], "refs/" ++ kind ++ "/") orelse continue;
1225 const oid = try arena.dupe(u8, line[0..40]);
1226 const label = try arena.dupeZ(u8, rest);
1227 try map.put(arena, label, oid[0..40]);
1228 }
1229 }
1230
1231 fn addDirRefs(r: *Repository, map: *std.StringArrayHashMapUnmanaged(Id), arena: std.mem.Allocator, comptime kind: [:0]const u8) !void {
1232 var dir = try r.gitdir.openDir("refs/" ++ kind, .{});
1233 defer dir.close();
1234 var walker = try dir.walk(arena);
1235 defer walker.deinit();
1236 while (try walker.next()) |entry| {
1237 if (entry.type != .REG) continue;
1238 var file = try dir.openFile(entry.path, .{});
1239 defer file.close();
1240 const label = try arena.dupeZ(u8, entry.path);
1241 const oid = try file.readAlloc(arena, 40);
1242 try map.put(arena, label, oid[0..40]);
1243 }
1244 }
12511245};