Something went wrong. Try again.
A Zig library that reads git repositories without running git, with log, show and diff output byte-identical to git's.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! `zig-git log` and `zig-git show` with diffs compared with git: every//! format, the separators between message and diff in each pretty//! format, merges, root commits, paths and `--full-diff`.
const std = @import("std");const testing = std.testing;
const tool = @import("zig_git.zig");const Repo = tool.Repo;const diff_tree_tests = @import("diff_tree_tests.zig");
fn same(repo: *Repo, args: []const []const u8) !void { try tool.expectSameAsGitHow(repo, args, .{});}
test "log and show: diffs in every format and pretty format" { var repo = try Repo.init(testing.allocator, testing.io); defer repo.deinit(); try diff_tree_tests.makeHistory(&repo); try repo.git(&.{ "commit", "-q", "--allow-empty", "-m", "empty" }); const gpa = testing.allocator; const diffs = [_][]const []const u8{ &.{"-p"}, &.{"--stat"}, &.{ "-p", "--stat" }, &.{"--raw"}, &.{"--name-only"}, &.{"--name-status"}, &.{"--numstat"}, &.{"--shortstat"}, &.{"--summary"}, &.{ "-p", "--raw" }, &.{"--no-renames"}, &.{ "-p", "-U1" }, &.{"--patch-with-stat"}, &.{ "--stat", "--summary" }, &.{ "-s", "-p" }, }; const pretties = [_][]const []const u8{ &.{}, &.{"--oneline"}, &.{"--format=%s"}, &.{"--format=%s%n"}, &.{"--pretty=short"}, &.{"--format=format:%h"}, &.{"--pretty=raw"}, }; for (diffs) |d| for (pretties) |p| { var args: std.ArrayList([]const u8) = .empty; defer args.deinit(gpa); try args.append(gpa, "log"); try args.appendSlice(gpa, d); try args.appendSlice(gpa, p); try args.append(gpa, "--no-renames"); try same(&repo, args.items); args.items[0] = "show"; try args.append(gpa, "HEAD~1"); try same(&repo, args.items); }; try same(&repo, &.{ "log", "--first-parent", "-p", "--no-renames" }); try same(&repo, &.{ "log", "-p", "--no-renames", "--", "dir" }); try same(&repo, &.{ "log", "--stat", "--full-diff", "--no-renames", "--", "dir" }); try same(&repo, &.{ "show", "--first-parent", "HEAD~1" }); try same(&repo, &.{ "show", "HEAD~1", "HEAD~2" }); try same(&repo, &.{ "-c", "log.showRoot=false", "log", "-p", "--no-renames" }); try same(&repo, &.{ "-c", "diff.context=1", "log", "-p", "--no-renames", "-1", "HEAD~2" });}
test "log and show: an empty format string writes nothing for the commit" { var repo = try Repo.init(testing.allocator, testing.io); defer repo.deinit(); try diff_tree_tests.makeHistory(&repo); try repo.git(&.{ "commit", "-q", "--allow-empty", "-m", "empty" }); const gpa = testing.allocator; const diffs = [_][]const []const u8{ &.{}, &.{"-p"}, &.{"--stat"}, &.{ "-p", "--stat" }, &.{"--raw"}, &.{"--name-status"}, &.{"--numstat"}, &.{"--summary"}, &.{ "-p", "--raw" }, &.{"-s"}, }; // Empty, as git tells it from the string and not from what it // writes: `%(trailers)` of a commit with none still ends each // commit, and takes the separator. const pretties = [_][]const []const u8{ &.{"--format="}, &.{"--pretty=format:"}, &.{"--pretty=tformat:"}, &.{"--pretty="}, &.{"--format=%(trailers)"}, }; for (diffs) |d| for (pretties) |p| { var args: std.ArrayList([]const u8) = .empty; defer args.deinit(gpa); try args.append(gpa, "log"); try args.appendSlice(gpa, d); try args.appendSlice(gpa, p); try args.append(gpa, "--no-renames"); try same(&repo, args.items); args.items[0] = "show"; try args.append(gpa, "HEAD~1"); try same(&repo, args.items); }; try same(&repo, &.{ "-c", "format.pretty=", "log", "-p", "--no-renames" }); try same(&repo, &.{ "-c", "format.pretty=format:", "log", "--raw", "--no-renames" });}