| ... | @@ -261,23 +261,6 @@ pub fn getTreeDiff(alloc: std.mem.Allocator, dir: nfs.Dir, commitid: CommitId, p | ... | @@ -261,23 +261,6 @@ pub fn getTreeDiff(alloc: std.mem.Allocator, dir: nfs.Dir, commitid: CommitId, p |
| 261 | return std.mem.trim(u8, result.stdout, "\n"); | 261 | return std.mem.trim(u8, result.stdout, "\n"); |
| 262 | } | 262 | } |
| 263 | | 263 | |
| 264 | // TODO make this inspect .git manually | | |
| 265 | pub 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 | | | |
| 281 | // TODO make this inspect .git manually | 264 | // TODO make this inspect .git manually |
| 282 | pub fn getTreeDiffOnlyStat(alloc: std.mem.Allocator, dir: nfs.Dir, commitid: CommitId, parentid: ?CommitId) !string { | 265 | pub fn getTreeDiffOnlyStat(alloc: std.mem.Allocator, dir: nfs.Dir, commitid: CommitId, parentid: ?CommitId) !string { |
| 283 | const t = tracer.trace(@src(), "", .{}); | 266 | const t = tracer.trace(@src(), "", .{}); |
| ... | @@ -1321,6 +1304,217 @@ pub const Repository = struct { | ... | @@ -1321,6 +1304,217 @@ pub const Repository = struct { |
| 1321 | | 1304 | |
| 1322 | return try arena.dupe(CommitId, result.values()); | 1305 | return try arena.dupe(CommitId, result.values()); |
| 1323 | } | 1306 | } |
| | 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 | } |
| 1324 | }; | 1518 | }; |
| 1325 | | 1519 | |
| 1326 | const z = @cImport({ | 1520 | const z = @cImport({ |
| ... | @@ -1468,12 +1662,34 @@ pub const Tree = struct { | ... | @@ -1468,12 +1662,34 @@ pub const Tree = struct { |
| 1468 | }; | 1662 | }; |
| 1469 | } | 1663 | } |
| 1470 | | 1664 | |
| | 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 | |
| 1471 | pub const Mode = struct { | 1685 | pub const Mode = struct { |
| 1472 | type: Type, | 1686 | type: Type, |
| 1473 | perm_user: Perm, | 1687 | perm_user: Perm, |
| 1474 | perm_group: Perm, | 1688 | perm_group: Perm, |
| 1475 | perm_other: Perm, | 1689 | perm_other: Perm, |
| 1476 | | 1690 | |
| | 1691 | pub const none = std.mem.zeroes(Mode); |
| | 1692 | |
| 1477 | pub fn format(self: Mode, comptime fmt: string, options: std.fmt.FormatOptions, writer: anytype) !void { | 1693 | pub fn format(self: Mode, comptime fmt: string, options: std.fmt.FormatOptions, writer: anytype) !void { |
| 1478 | _ = fmt; | 1694 | _ = fmt; |
| 1479 | _ = options; | 1695 | _ = options; |
| ... | @@ -1694,3 +1910,17 @@ pub fn findFirstUnset(set: std.bit_set.DynamicBitSetUnmanaged, after: usize) ?us | ... | @@ -1694,3 +1910,17 @@ pub fn findFirstUnset(set: std.bit_set.DynamicBitSetUnmanaged, after: usize) ?us |
| 1694 | if (candidate >= set.bit_length) return null; | 1910 | if (candidate >= set.bit_length) return null; |
| 1695 | return candidate; | 1911 | return candidate; |
| 1696 | } | 1912 | } |
| | 1913 | |
| | 1914 | const 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 | }; |