╭ changed ───╮ │src/push.zig│ ╰────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │70 try xit.patch.writePatches(repo_opts, state, io, allocator, &iter, &progress);│ │71 } │ │72 │ │73 -fn writeUndo( │ │73 +// the action a received push records │ │74 +pub const undo_action = "haxy/push"; │ │75 + │ │76 +// the fixture records a stand-in push with this too, so both write one shape │ │77 +pub fn writeUndo( │ │78 comptime repo_opts: rp.RepoOpts(.xit), │ │79 state: rp.Repo(.xit, repo_opts).State(.read_write), │ │80 io: std.Io, │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │88 defer author_json.deinit(allocator); │ │89 var payload: std.json.ObjectMap = try .init(allocator, &.{"author"}, &.{if (author != null) .{ .object = author_json } else .null}); │ │90 defer payload.deinit(allocator); │ │87 - try xit.undo.write(repo_opts, state, std.Io.Timestamp.now(io, .real).toSeconds(), .{ .custom = .{ .action = "push", .payload = payload } }); │ │91 + try xit.undo.write(repo_opts, state, std.Io.Timestamp.now(io, .real).toSeconds(), .{ .custom = .{ .action = undo_action, .payload = payload } });│ │92 } │ │93 │ │94 // serve a receive-pack and consume any events it pushed to the events branch, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │16 const cms = hx.search_commit; │ │17 const fork = hx.fork; │ │18 const pch = hx.pch; │ │19 +const push = hx.push; │ │20 │ │21 // cook the terminal before a panic/segfault trace is printed, so the trace│ │22 // isn't mangled by raw mode and the alternate buffer │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1339 .source_branch = "feature", │ │1340 .target_branch = "master", │ │1341 }, null, patch_author); │ │1342 + │ │1343 + // a push onto the branch that patch tracks, so the undo tab shows the │ │1344 + // action and the patch refresh it sets off │ │1345 + try seedPush(io, allocator, target_repo, patch_author); │ │1346 + try pch.refreshBranches(.server, .xit, .{}, io, allocator, target_repo, null, null); │ │1347 } │ │1348 │ │1349 +// stand in for a client push, which the fixture has no server to receive: one │ │1350 +// transaction moves the branch and records the action, the way receive-pack │ │1351 +// does, and the caller refreshes the affected patches after it │ │1352 +fn seedPush(io: std.Io, allocator: std.mem.Allocator, repo: *rp.Repo(.xit, .{}), author: evt.CommitAuthor) !void { │ │1353 + const Repo = rp.Repo(.xit, .{}); │ │1354 + const DB = Repo.DB; │ │1355 + const Ctx = struct { │ │1356 + core: *Repo.Core, │ │1357 + io: std.Io, │ │1358 + allocator: std.mem.Allocator, │ │1359 + author: evt.CommitAuthor, │ │1360 + │ │1361 + pub fn run(ctx: @This(), cursor: *DB.Cursor(.read_write)) !void { │ │1362 + const opts: rp.RepoOpts(.xit) = .{}; │ │1363 + var moment = try DB.HashMap(.read_write).init(cursor.*); │ │1364 + const state = Repo.State(.read_write){ .core = ctx.core, .extra = .{ .moment = &moment } }; │ │1365 + │ │1366 + // a null tree keeps the parent's content, so the branch advances │ │1367 + // without the fixture having to build one │ │1368 + const author_line = try std.fmt.allocPrint(ctx.allocator, "{s} <{s}>", .{ ctx.author.name, ctx.author.email });│ │1369 + defer ctx.allocator.free(author_line); │ │1370 + _ = try obj.writeCommit(.xit, opts, state, ctx.io, ctx.allocator, .{ │ │1371 + .message = "Revise the feature branch", │ │1372 + .author = author_line, │ │1373 + .timestamp = 820, │ │1374 + }, null, .{ .kind = .head, .name = "feature" }); │ │1375 + │ │1376 + try push.writeUndo(opts, state, ctx.io, ctx.allocator, ctx.author); │ │1377 + } │ │1378 + }; │ │1379 + │ │1380 + try transaction(io, repo, Ctx{ .core = &repo.core, .io = io, .allocator = allocator, .author = author }); │ │1381 +} │ │1382 + │ │1383 // commit `events` onto `ref`, rooted at `parent`, as one transaction. │ │1384 // consume always roots a new branch at nothing, so the divergent fixtures │ │1385 // write their side branch here instead. nothing is consumed, so it records │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │1413 }); │ │1414 } │ │1415 }; │ │1376 - │ │1377 - try repo.core.db_file.lock(io, .exclusive); │ │1378 - defer repo.core.db_file.unlock(io); │ │1416 │ │1380 - const history = try DB.ArrayList(.read_write).init(repo.core.db.rootCursor());│ │1381 - try history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ │ │1417 + try transaction(io, repo, Ctx{ │ │1418 .core = &repo.core, │ │1419 .io = io, │ │1420 .allocator = allocator, │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │1422 .events = events, │ │1423 .parent = parent, │ │1424 }); │ │1425 +} │ │1426 + │ │1427 +// append `ctx` to the repo's history as one transaction. the fixtures write a │ │1428 +// few states the normal paths have no way to produce │ │1429 +fn transaction(io: std.Io, repo: *rp.Repo(.xit, .{}), ctx: anytype) !void { │ │1430 + const DB = rp.Repo(.xit, .{}).DB; │ │1431 + try repo.core.db_file.lock(io, .exclusive); │ │1432 + defer repo.core.db_file.unlock(io); │ │1433 + │ │1434 + const history = try DB.ArrayList(.read_write).init(repo.core.db.rootCursor());│ │1435 + try history.appendContext(.{ .slot = try history.getSlot(-1) }, ctx); │ │1436 } │ │1437 │ │1438 // recursively copy the contents of src_dir into dest_dir │ ╰────────────────────────────────────────────────────────────────────────────────────────╯