tiny.smg.command.scan.changed
Defined in command.scan.
API (1)
Actions
Public operations.
Source
Source: tools/smg/src/command/scan/changed.zig
zig
const std = @import("std");const smg = @import("../../root.zig");const git = smg.git;const text = smg.text;const sys = @import("sys");pub fn files(allocator: std.mem.Allocator, root: []const u8, ref: []const u8, invalid_ref: *bool) ![]const []const u8 { invalid_ref.* = false; var io_state = std.Io.Threaded.init(allocator, .{}); defer io_state.deinit(); if (!std.mem.eql(u8, ref, "HEAD")) { const commit_ref = try std.fmt.allocPrint(allocator, "{s}^{{commit}}", .{ref}); const verify = std.process.run(allocator, io_state.io(), .{ .argv = &.{ "git", "rev-parse", "--verify", "--quiet", commit_ref }, .cwd = .{ .path = root }, .expand_arg0 = .expand, .stdout_limit = .limited(4 * 1024 * 1024), .stderr_limit = .limited(1024 * 1024), }) catch return &.{}; if (!processExitedZero(verify)) { invalid_ref.* = true; return &.{}; } } var out: std.ArrayList([]const u8) = .empty; const diff_result = std.process.run(allocator, io_state.io(), .{ .argv = &.{ "git", "diff", "--no-renames", "--name-only", ref }, .cwd = .{ .path = root }, .expand_arg0 = .expand, .stdout_limit = .limited(4 * 1024 * 1024), .stderr_limit = .limited(1024 * 1024), }) catch return &.{}; if (processExitedZero(diff_result)) try append(allocator, root, &out, diff_result.stdout); const untracked_result = std.process.run(allocator, io_state.io(), .{ .argv = &.{ "git", "ls-files", "--others", "--exclude-standard" }, .cwd = .{ .path = root }, .expand_arg0 = .expand, .stdout_limit = .limited(4 * 1024 * 1024), .stderr_limit = .limited(1024 * 1024), }) catch return &.{}; if (processExitedZero(untracked_result)) try append(allocator, root, &out, untracked_result.stdout); std.mem.sort([]const u8, out.items, {}, stringLess); return try out.toOwnedSlice(allocator);}fn processExitedZero(result: anytype) bool { return switch (result.term) { .exited => |code| code == 0, else => false, };}fn append(allocator: std.mem.Allocator, root: []const u8, out: *std.ArrayList([]const u8), stdout: []const u8) !void { var lines = std.mem.splitScalar(u8, stdout, '\n'); while (lines.next()) |line_raw| { const line = text.trim(line_raw); if (line.len == 0 or !supportedPath(line)) continue; const path = try std.fs.path.join(allocator, &.{ root, line }); if (!text.list.containsString(out.items, path)) try out.append(allocator, path); }}fn supportedPath(path: []const u8) bool { return std.mem.endsWith(u8, path, ".py") or std.mem.endsWith(u8, path, ".ts") or std.mem.endsWith(u8, path, ".tsx") or std.mem.endsWith(u8, path, ".js") or std.mem.endsWith(u8, path, ".jsx") or std.mem.endsWith(u8, path, ".mjs") or std.mem.endsWith(u8, path, ".cjs") or std.mem.endsWith(u8, path, ".zig") or std.mem.endsWith(u8, path, ".c") or std.mem.endsWith(u8, path, ".h") or std.mem.endsWith(u8, path, ".cpp") or std.mem.endsWith(u8, path, ".hpp") or std.mem.endsWith(u8, path, ".cc") or std.mem.endsWith(u8, path, ".cxx") or std.mem.endsWith(u8, path, ".hh") or std.mem.endsWith(u8, path, ".hxx") or std.mem.endsWith(u8, path, ".cu") or std.mem.endsWith(u8, path, ".cuh") or std.mem.endsWith(u8, path, ".metal") or std.mem.endsWith(u8, path, ".chic");}fn stringLess(_: void, a: []const u8, b: []const u8) bool { return std.mem.lessThan(u8, a, b);}fn replaceStoredGraph( allocator: std.mem.Allocator, value: smg.graph.Graph, root: []const u8,) !void { var opened = try smg.storage.store.open(allocator, root, smg.default_limits.storage); defer opened.close(); _ = try smg.storage.graph.replace(allocator, &opened.database, value);}test "changed-file filter matches supported source extensions" { try std.testing.expect(supportedPath("src/app.py")); try std.testing.expect(supportedPath("src/app.tsx")); try std.testing.expect(supportedPath("src/kernel.cuh")); try std.testing.expect(supportedPath("src/library.chic")); try std.testing.expect(!supportedPath("README.md")); try std.testing.expect(!supportedPath("src/app.pyc"));}test "changed files preserve a deleted source tombstone" { const fs_io = std.Options.debug_io; var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const root = try tmp.dir.realPathFileAlloc(fs_io, ".", allocator); try tmp.dir.createDirPath(fs_io, "src"); try tmp.dir.writeFile(fs_io, .{ .sub_path = "src/drop.py", .data = "def drop():\n return 1\n" }); try std.testing.expect((try git.run(allocator, root, &.{ "git", "init" }, 1024 * 1024)).ok); try std.testing.expect((try git.run(allocator, root, &.{ "git", "add", "src/drop.py" }, 1024 * 1024)).ok); try std.testing.expect((try git.run(allocator, root, &.{ "git", "-c", "user.name=smg", "-c", "user.email=smg@example.invalid", "commit", "-m", "base" }, 1024 * 1024)).ok); try tmp.dir.deleteFile(fs_io, "src/drop.py"); try std.testing.expect((try git.run(allocator, root, &.{ "git", "add", "-u" }, 1024 * 1024)).ok); try std.testing.expect((try git.run(allocator, root, &.{ "git", "-c", "user.name=smg", "-c", "user.email=smg@example.invalid", "commit", "-m", "delete" }, 1024 * 1024)).ok); var invalid = false; const changed = try files(allocator, root, "HEAD~1", &invalid); try std.testing.expect(!invalid); try std.testing.expectEqual(@as(usize, 1), changed.len); try std.testing.expectEqualStrings(try std.fs.path.join(allocator, &.{ root, "src/drop.py" }), changed[0]); try std.testing.expect(!sys.fs.exists(changed[0]));}test "changed files include both sides of a source rename" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const root = try std.fmt.allocPrint(allocator, "/tmp/smg-changed-rename-test-{x}", .{@intFromPtr(&arena)}); defer sys.fs.deleteTree(root) catch {}; try sys.fs.createDirPath(try std.fs.path.join(allocator, &.{ root, "src" })); const before = try std.fs.path.join(allocator, &.{ root, "src/before.py" }); const after = try std.fs.path.join(allocator, &.{ root, "src/after.py" }); try text.writeFile(before, "def value():\n return 1\n"); try std.testing.expect((try git.run(allocator, root, &.{ "git", "init" }, 1024 * 1024)).ok); try std.testing.expect((try git.run(allocator, root, &.{ "git", "add", "src/before.py" }, 1024 * 1024)).ok); try std.testing.expect((try git.run(allocator, root, &.{ "git", "-c", "user.name=smg", "-c", "user.email=smg@example.invalid", "commit", "-m", "base" }, 1024 * 1024)).ok); var initial = smg.graph.init(allocator); _ = try smg.scan.scanPaths(allocator, &initial, root, &.{}, false, smg.default_limits); try replaceStoredGraph(allocator, initial, root); try std.testing.expect((try git.run(allocator, root, &.{ "git", "mv", "src/before.py", "src/after.py" }, 1024 * 1024)).ok); try std.testing.expect((try git.run(allocator, root, &.{ "git", "-c", "user.name=smg", "-c", "user.email=smg@example.invalid", "commit", "-m", "rename" }, 1024 * 1024)).ok); var invalid = false; const changed = try files(allocator, root, "HEAD~1", &invalid); var loaded = try smg.storage.graph.load(allocator, allocator, root, smg.default_limits.storage); _ = try smg.scan.scanPaths(allocator, &loaded, root, changed, true, smg.default_limits); try replaceStoredGraph(allocator, loaded, root); const persisted = try smg.storage.graph.load(allocator, allocator, root, smg.default_limits.storage); try std.testing.expect(!invalid); try std.testing.expectEqual(@as(usize, 2), changed.len); try std.testing.expect(text.list.containsString(changed, before)); try std.testing.expect(text.list.containsString(changed, after)); try std.testing.expect(!sys.fs.exists(before)); try std.testing.expect(sys.fs.exists(after)); try std.testing.expect(smg.graph.getNode(&persisted, "src.before") == null); try std.testing.expect(smg.graph.getNode(&persisted, "src.before.value") == null); try std.testing.expect(smg.graph.getNode(&persisted, "src.after.value") != null);}Source: tools/smg/src/command/scan/root.zig:1
zig
pub const changed = @import("changed.zig");Audit
| Definitions | 2 |
|---|---|
| Public names | 3 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |