authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-06-15 01:42:34 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-06-15 01:42:34 -07:00
log62f291be2c039c9292a01e1c50984fd19f8b92ba
tree919dcb7d4f33c6673391b93a6a596907371ea6ef
parent1a725deb03db9738ebb5d1cc7fc065ce16d9f903
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

replace getTreeDiffOnlyRaw with pure zig writeTreeDiffOnlyRaw


1 files changed, 247 insertions(+), 17 deletions(-)

git.zig+247-17
......@@ -261,23 +261,6 @@ pub fn getTreeDiff(alloc: std.mem.Allocator, dir: nfs.Dir, commitid: CommitId, p
261261 return std.mem.trim(u8, result.stdout, "\n");
262262}
263263
264// TODO make this inspect .git manually
265pub fn getTreeDiffOnlyRaw(alloc: std.mem.Allocator, dir: nfs.Dir, commitid: CommitId, parentid: ?CommitId) !string {
266 const t = tracer.trace(@src(), "", .{});
267 defer t.end();
268
269 if (parentid == null) {
270 // 4b825dc642cb6eb9a060e54bf8d69288fbee4904 is a hardcode for the empty tree in git sha1
271 // result of `printf | git hash-object -t tree --stdin`
272 const result = try root.child_process.run(alloc, dir, .ignore, .pipe, .ignore, 1024 * 1024 * 1024, &.{ "git", "diff-tree", "--raw", "-r", "4b825dc642cb6eb9a060e54bf8d69288fbee4904", commitid.id });
273 std.debug.assert(result.term == .exited and result.term.exited == 0);
274 return result.stdout;
275 }
276 const result = try root.child_process.run(alloc, dir, .ignore, .pipe, .ignore, 1024 * 1024 * 1024, &.{ "git", "diff-tree", "--raw", "-r", parentid.?.id, commitid.id });
277 std.debug.assert(result.term == .exited and result.term.exited == 0);
278 return result.stdout;
279}
280
281264// TODO make this inspect .git manually
282265pub fn getTreeDiffOnlyStat(alloc: std.mem.Allocator, dir: nfs.Dir, commitid: CommitId, parentid: ?CommitId) !string {
283266 const t = tracer.trace(@src(), "", .{});
......@@ -1321,6 +1304,217 @@ pub const Repository = struct {
13211304
13221305 return try arena.dupe(CommitId, result.values());
13231306 }
1307
1308 pub fn writeTreeDiffOnlyRaw(r: *Repository, writable: anytype, commitid: CommitId, parentid: ?CommitId) !void {
1309 const S = struct {
1310 fn item(w: anytype, b_mode: Tree.Object.Mode, a_mode: Tree.Object.Mode, b_id: Id, a_id: Id, action: TreeDiff.Action, p: ?*const PathListNode, name: []const u8) !void {
1311 if (p == null) {
1312 try w.writevAll(&.{ ":", &b_mode.intbytes(), " ", &a_mode.intbytes(), " ", b_id, " ", a_id, " ", @tagName(action), "\t", name, "\n" });
1313 return;
1314 }
1315 try w.writevAll(&.{ ":", &b_mode.intbytes(), " ", &a_mode.intbytes(), " ", b_id, " ", a_id, " ", @tagName(action), "\t" });
1316 try p.?.nprint(w);
1317 try w.writevAll(&.{ "/", name, "\n" });
1318 }
1319 };
1320 const A = struct {
1321 fn item(w: anytype, mode: Tree.Object.Mode, id: Id, p: ?*const PathListNode, name: []const u8) !void {
1322 try S.item(w, .none, mode, &@splat('0'), id, .A, p, name);
1323 }
1324 fn dir(e: *Repository, w: anytype, t: Id, p: ?*const PathListNode, o: usize) !void {
1325 const tree = try e.getTreeA(e.gpa, t);
1326 for (tree.children[o..]) |obj| {
1327 if (obj.mode.type == .directory) {
1328 try dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);
1329 continue;
1330 }
1331 try item(w, obj.mode, obj.id.erase(), p, obj.name);
1332 }
1333 }
1334 pub fn either(e: *Repository, w: anytype, p: ?*const PathListNode, obj: Tree.Object) !void {
1335 if (obj.mode.type == .directory) {
1336 return dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);
1337 }
1338 return item(w, obj.mode, obj.id.erase(), p, obj.name);
1339 }
1340 };
1341 const D = struct {
1342 fn item(w: anytype, mode: Tree.Object.Mode, id: Id, p: ?*const PathListNode, name: []const u8) !void {
1343 try S.item(w, mode, .none, id, &@splat('0'), .D, p, name);
1344 }
1345 fn dir(e: *Repository, w: anytype, t: Id, p: ?*const PathListNode, o: usize) !void {
1346 const tree = try e.getTreeA(e.gpa, t);
1347 for (tree.children[o..]) |obj| {
1348 if (obj.mode.type == .directory) {
1349 try dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);
1350 continue;
1351 }
1352 try item(w, obj.mode, obj.id.erase(), p, obj.name);
1353 }
1354 }
1355 pub fn either(e: *Repository, w: anytype, p: ?*const PathListNode, obj: Tree.Object) !void {
1356 if (obj.mode.type == .directory) {
1357 return dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);
1358 }
1359 return item(w, obj.mode, obj.id.erase(), p, obj.name);
1360 }
1361 };
1362 const M = struct {
1363 fn dir(e: *Repository, w: anytype, b_t: Id, a_t: Id, p: ?*const PathListNode) !void {
1364 var before_i: usize = 0;
1365 const before_tree = try e.getTreeA(e.gpa, b_t);
1366 const before_children = before_tree.children;
1367
1368 var after_i: usize = 0;
1369 const after_tree = try e.getTreeA(e.gpa, a_t);
1370 const after_children = after_tree.children;
1371
1372 while (true) {
1373 if (after_i == after_tree.children.len) {
1374 try D.dir(e, w, b_t, p, before_i);
1375 break;
1376 }
1377 if (before_i == before_tree.children.len) {
1378 try A.dir(e, w, a_t, p, after_i);
1379 break;
1380 }
1381
1382 const before = before_children[before_i];
1383 const after = after_children[after_i];
1384
1385 switch (before.order(after)) {
1386 .eq => {
1387 if (std.mem.eql(u8, before.id.erase(), after.id.erase())) {
1388 if (!std.mem.eql(u8, &before.mode.intbytes(), &after.mode.intbytes())) {
1389 try S.item(
1390 w,
1391 before.mode,
1392 after.mode,
1393 before.id.erase(),
1394 after.id.erase(),
1395 .M,
1396 p,
1397 after.name,
1398 );
1399 }
1400 before_i += 1;
1401 after_i += 1;
1402 continue;
1403 }
1404 if (before.mode.type != after.mode.type) {
1405 try S.item(
1406 w,
1407 before.mode,
1408 after.mode,
1409 before.id.erase(),
1410 after.id.erase(),
1411 .T,
1412 p,
1413 after.name,
1414 );
1415 before_i += 1;
1416 after_i += 1;
1417 continue;
1418 }
1419 if (before.mode.type == .directory) {
1420 try dir(
1421 e,
1422 w,
1423 before.id.tree.id,
1424 after.id.tree.id,
1425 &.{ .prev = p, .data = after.name },
1426 );
1427 before_i += 1;
1428 after_i += 1;
1429 continue;
1430 }
1431 try S.item(
1432 w,
1433 before.mode,
1434 after.mode,
1435 before.id.erase(),
1436 after.id.erase(),
1437 .M,
1438 p,
1439 after.name,
1440 );
1441 before_i += 1;
1442 after_i += 1;
1443 continue;
1444 },
1445 .lt => {
1446 const after_item = after_tree.get(before.name, before.mode.type) orelse {
1447 try D.either(e, w, p, before);
1448 before_i += 1;
1449 continue;
1450 };
1451 const before_item = before_tree.get(after.name, after.mode.type) orelse {
1452 try A.either(e, w, p, after);
1453 after_i += 1;
1454 continue;
1455 };
1456 if (std.mem.eql(u8, &after_item.id_bytes, &before.id_bytes)) {
1457 before_i += 1;
1458 continue;
1459 }
1460 if (std.mem.eql(u8, &before_item.id_bytes, &after.id_bytes)) {
1461 after_i += 1;
1462 continue;
1463 }
1464 {
1465 try D.either(e, w, p, before);
1466 before_i += 1;
1467 try A.either(e, w, p, after);
1468 after_i += 1;
1469 continue;
1470 }
1471 comptime unreachable;
1472 },
1473 .gt => {
1474 const before_item = before_tree.get(after.name, after.mode.type) orelse {
1475 try A.either(e, w, p, after);
1476 after_i += 1;
1477 continue;
1478 };
1479 const after_item = after_tree.get(before.name, before.mode.type) orelse {
1480 try D.either(e, w, p, before);
1481 before_i += 1;
1482 continue;
1483 };
1484 if (std.mem.eql(u8, &before_item.id_bytes, &after.id_bytes)) {
1485 after_i += 1;
1486 continue;
1487 }
1488 if (std.mem.eql(u8, &after_item.id_bytes, &before.id_bytes)) {
1489 before_i += 1;
1490 continue;
1491 }
1492 {
1493 try A.either(e, w, p, after);
1494 before_i += 1;
1495 try D.either(e, w, p, before);
1496 after_i += 1;
1497 continue;
1498 }
1499 comptime unreachable;
1500 },
1501 }
1502 comptime unreachable;
1503 }
1504 }
1505 };
1506 if (parentid == null) {
1507 const commitidx = try r.getCommitA(r.gpa, commitid.id);
1508 const commit = commitidx.reify(r);
1509 try A.dir(r, writable, commit.tree.id, null, 0);
1510 return;
1511 }
1512 const before_commitidx = try r.getCommitA(r.gpa, parentid.?.id);
1513 const before_commit = before_commitidx.reify(r);
1514 const after_commitidx = try r.getCommitA(r.gpa, commitid.id);
1515 const after_commit = after_commitidx.reify(r);
1516 try M.dir(r, writable, before_commit.tree.id, after_commit.tree.id, null);
1517 }
13241518};
13251519
13261520const z = @cImport({
......@@ -1468,12 +1662,34 @@ pub const Tree = struct {
14681662 };
14691663 }
14701664
1665 pub fn order(lhs: Object, rhs: Object) std.math.Order {
1666 if (lhs.name.ptr != rhs.name.ptr) {
1667 const n = @min(lhs.name.len, rhs.name.len);
1668 for (lhs.name[0..n], rhs.name[0..n]) |lhs_elem, rhs_elem| {
1669 switch (std.math.order(lhs_elem, rhs_elem)) {
1670 .eq => continue,
1671 .lt => return .lt,
1672 .gt => return .gt,
1673 }
1674 }
1675 }
1676 const l_is_dir = lhs.mode.type == .directory;
1677 const r_is_dir = rhs.mode.type == .directory;
1678 return switch (std.math.order(lhs.name.len, rhs.name.len)) {
1679 .lt => if (l_is_dir) std.math.order('/', rhs.name[lhs.name.len]) else .lt,
1680 .gt => if (r_is_dir) std.math.order(lhs.name[rhs.name.len], '/') else .gt,
1681 .eq => if (l_is_dir and r_is_dir) .eq else if (l_is_dir) .gt else if (r_is_dir) .lt else .eq,
1682 };
1683 }
1684
14711685 pub const Mode = struct {
14721686 type: Type,
14731687 perm_user: Perm,
14741688 perm_group: Perm,
14751689 perm_other: Perm,
14761690
1691 pub const none = std.mem.zeroes(Mode);
1692
14771693 pub fn format(self: Mode, comptime fmt: string, options: std.fmt.FormatOptions, writer: anytype) !void {
14781694 _ = fmt;
14791695 _ = options;
......@@ -1694,3 +1910,17 @@ pub fn findFirstUnset(set: std.bit_set.DynamicBitSetUnmanaged, after: usize) ?us
16941910 if (candidate >= set.bit_length) return null;
16951911 return candidate;
16961912}
1913
1914const PathListNode = struct {
1915 prev: ?*const @This(),
1916 data: []const u8,
1917
1918 pub fn nprint(node: *const PathListNode, writable: anytype) !void {
1919 if (node.prev == null) {
1920 try writable.writeAll(node.data);
1921 return;
1922 }
1923 try nprint(node.prev.?, writable);
1924 try writable.writevAll(&.{ "/", node.data });
1925 }
1926};