From 59a8a163abecac6aca1f4aa96e73ee4124ab37f6 Mon Sep 17 00:00:00 2001 From: Jim Fitzpatrick Date: Sat, 9 May 2026 14:37:52 +0100 Subject: [PATCH 1/2] FIX: Memory leak and panic Due to the upgrade to zig 0.16.0 there was memory leak introduced when cloning to temporary directories, and a panic when the GARB_PATH was set. Signed-off-by: Jim Fitzpatrick --- src/main.zig | 2 +- src/root.zig | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main.zig b/src/main.zig index 9f81238..165ea25 100644 --- a/src/main.zig +++ b/src/main.zig @@ -115,7 +115,7 @@ pub fn main(init: std.process.Init) !void { std.log.err("unable to get GRAB_PATH, please set or use --temp or --path", .{}); std.process.exit(1); } - config.path = .{ .allocated = path }; + config.path = .{ .provided = path }; std.log.debug("try to get path from env", .{}); } if (res.args.remote != 0) { diff --git a/src/root.zig b/src/root.zig index 2cabc0a..49ce6fc 100644 --- a/src/root.zig +++ b/src/root.zig @@ -67,8 +67,12 @@ pub const Configuration = struct { } pub fn deinit(self: *Configuration, allocator: std.mem.Allocator) void { - _ = self; - _ = allocator; + if (self.path) |path| { + switch (path) { + .allocated => |p| allocator.free(p), + .provided, .none => {}, + } + } } pub fn getPath(self: *const Configuration) ?[]const u8 { From 1ce813f7c5c7faaf0e46207318aac90fa47f7dca Mon Sep 17 00:00:00 2001 From: Jim Fitzpatrick Date: Sat, 9 May 2026 14:43:44 +0100 Subject: [PATCH 2/2] ADD: Shallow Clone New flag added to allow the shallow cloning of repos. Signed-off-by: Jim Fitzpatrick --- changelog.d/68.feature.md | 1 + src/main.zig | 19 +++++++++++++------ src/root.zig | 22 +++++++++++++++++----- 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 changelog.d/68.feature.md diff --git a/changelog.d/68.feature.md b/changelog.d/68.feature.md new file mode 100644 index 0000000..c59488d --- /dev/null +++ b/changelog.d/68.feature.md @@ -0,0 +1 @@ +Allow the shallow clone of repos, forces a depth of one, by setting the `-S/--shallow` flag. diff --git a/src/main.zig b/src/main.zig index 165ea25..0c4beda 100644 --- a/src/main.zig +++ b/src/main.zig @@ -38,6 +38,7 @@ pub fn main(init: std.process.Init) !void { \\-p, --path Overrides the path set in the GRAB_PATH environment variable. \\-t, --temp Download repositories to a temporary directory. This will be the OS default temporary directory. \\-s, --standard Standard clone, normal clone is done using worktrees. + \\-S, --shallow Create a shallow clone with a history of depth one \\-r, --remote Add remote to existing repo. \\--log-level Set the log level. All logs are saved to file. Possible values are (debug, info, warn, error). Defualt level is info. \\--version Show program's version number and exit @@ -126,6 +127,10 @@ pub fn main(init: std.process.Init) !void { config.action = .standard; } + if (res.args.shallow != 0) { + config.shallow = true; + } + try grab.setLocation(init.io, config); for (res.positionals[0]) |repo| { @@ -141,20 +146,22 @@ pub fn main(init: std.process.Init) !void { std.log.debug("Project Data:\n\tSite: {s}\n\tOwner: {s}\n\tName: {s}\n\tClone: {s}", .{ project.site, project.owner, project.name, project.clone }); switch (config.action) { - .standard => try clone(init.io, allocator, project), - .worktree => try worktree(allocator, init.io, project), + .standard => try clone(init.io, allocator, project, .{ .shallow = config.shallow }), + .worktree => try worktree(allocator, init.io, project, .{ .shallow = config.shallow }), .remote => try addRemote(allocator, init.io, project), } } std.log.info("Finished", .{}); } -fn clone(io: std.Io, allocator: std.mem.Allocator, project: grab.Project) !void { +const gitOpts = struct { shallow: bool = false }; + +fn clone(io: std.Io, allocator: std.mem.Allocator, project: grab.Project, opts: gitOpts) !void { var project_ = project; const cwd = std.Io.Dir.cwd(); const path = try grab.createPath(io, cwd, &[_][]const u8{ project_.site, project_.owner }); project_.root = path; - grab.clone(allocator, io, project_, .{ .bare = false }) catch |err| switch (err) { + grab.clone(allocator, io, project_, .{ .bare = false, .shallow = opts.shallow }) catch |err| switch (err) { error.exists => { std.log.err("Unable to clone: {s}, path not empty", .{project_.name}); std.process.exit(1); @@ -188,13 +195,13 @@ fn addRemote(allocator: std.mem.Allocator, io: std.Io, project: grab.Project) !v std.log.info("successfully added remotes", .{}); } -fn worktree(allocator: std.mem.Allocator, io: std.Io, project: grab.Project) !void { +fn worktree(allocator: std.mem.Allocator, io: std.Io, project: grab.Project, opts: gitOpts) !void { std.log.debug("Config worktree deployment", .{}); var project_ = project; const cwd = std.Io.Dir.cwd(); const path = try grab.createPath(io, cwd, &[_][]const u8{ project_.site, project_.owner, project_.name }); project_.root = path; - grab.clone(allocator, io, project_, .{ .bare = true }) catch |err| switch (err) { + grab.clone(allocator, io, project_, .{ .bare = true, .shallow = opts.shallow }) catch |err| switch (err) { error.exists => { std.log.err("Unable to clone: {s}, path not empty", .{project_.name}); std.process.exit(1); diff --git a/src/root.zig b/src/root.zig index 49ce6fc..05f032b 100644 --- a/src/root.zig +++ b/src/root.zig @@ -50,6 +50,7 @@ pub const Action = enum { pub const CloneOptions = struct { bare: bool, + shallow: bool = false, }; pub const PathSource = union(enum) { @@ -61,6 +62,7 @@ pub const PathSource = union(enum) { pub const Configuration = struct { path: ?PathSource = .none, action: Action = .worktree, + shallow: bool = false, pub fn init() Configuration { return Configuration{}; @@ -90,16 +92,26 @@ pub fn clone(allocator: std.mem.Allocator, io: std.Io, project: Project, opts: C const path = if (project.root) |root| try root.realPathFileAlloc(io, ".", allocator) else return error.noroot; defer allocator.free(path); - const cmd = if (opts.bare) - &[_][]const u8{ "git", "-C", path, "clone", "--bare", project.clone, ".bare" } - else - &[_][]const u8{ "git", "-C", path, "clone", project.clone }; + + var cmd: std.ArrayList([]const u8) = .empty; + defer cmd.deinit(allocator); + try cmd.appendSlice(allocator, &[_][]const u8{ "git", "-C", path, "clone" }); + + if (opts.shallow) try cmd.append(allocator, "--depth=1"); + + if (opts.bare) { + try cmd.appendSlice(allocator, &[_][]const u8{ "--bare", project.clone, ".bare" }); + } else { + try cmd.append(allocator, project.clone); + } + const result = std.process.run(allocator, io, .{ - .argv = cmd, + .argv = cmd.items, }) catch |err| { std.log.err("Failed to run git clone: {}", .{err}); return err; }; + defer allocator.free(result.stdout); defer allocator.free(result.stderr); if (std.mem.startsWith(u8, result.stderr, "fatal")) {