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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! `zig-git check-attr` compared with `git check-attr`: attributes files at//! every level, macros, bad lines and their warnings, the index and trees//! as sources, and the command's forms and errors.
const std = @import("std");const testing = std.testing;
const tool = @import("zig_git.zig");const Repo = tool.Repo;
fn same(repo: *Repo, args: []const []const u8) !void { try tool.expectSameAsGitHow(repo, args, .{});}
fn sameIn(repo: *Repo, cwd: []const u8, args: []const []const u8, input: []const u8) !void { try tool.expectSameAsGitHow(repo, args, .{ .cwd = cwd, .input = input });}
fn write(repo: *Repo, path: []const u8, contents: []const u8) !void { if (std.fs.path.dirname(path)) |d| try repo.tmp.dir.createDirPath(repo.io, d); try repo.tmp.dir.writeFile(repo.io, .{ .sub_path = path, .data = contents });}
const top_file = \\# a comment \\*.c diff=cpp -text \\x diff binary \\y binary diff \\[attr]mac m1 -m2 !m3 v=1 \\[attr]m1 m4 \\uses-mac mac m3 \\bad ok b@d \\!neg n \\"sp ace"q qq \\"tab\there" tb \\\!bang bb \\d/ dir \\sub/** below \\/anchored an \\ lead ld \\crlf cr \\;
fn makeRepo(repo: *Repo) !void { try write(repo, ".gitattributes", top_file ++ "crlf2 c2\r\n"); try write(repo, "sub/.gitattributes", "[attr]sm s\nz sm\n*.c -diff\nanchored an2\n"); try write(repo, "sub/deep/.gitattributes", "*.c diff\nx !binary\n"); try write(repo, ".git/info/attributes", "a* foo !bar -baz\nx -diff\n"); try write(repo, "glob", "* global\n*.c g=1\n");}
test "check-attr: files at every level, macros and bad lines" { var repo = try Repo.init(testing.allocator, testing.io); defer repo.deinit(); try makeRepo(&repo); const paths = [_][]const u8{ "x", "y", "uses-mac", "a.c", "sub/a.c", "sub/deep/a.c", "sub/deep/x", "sp ace", "tab\there", "!bang", "d", "d/", "d/e", "sub/z", "anchored", "sub/anchored", "lead", "crlf", "crlf2", "abc", "sub/deep/z", "nothere", "sub/deep/", }; var args: std.ArrayList([]const u8) = .empty; defer args.deinit(testing.allocator); try args.appendSlice(testing.allocator, &.{ "check-attr", "-a", "--" }); try args.appendSlice(testing.allocator, &paths); try same(&repo, args.items); args.clearRetainingCapacity(); try args.appendSlice(testing.allocator, &.{ "check-attr", "diff", "text", "binary", "mac", "m1", "m2", "m3", "m4", "v", "foo", "bar", "baz", "--" }); try args.appendSlice(testing.allocator, &paths); try same(&repo, args.items); // The global file, by configuration and by its XDG default. try same(&repo, &.{ "-c", "core.attributesFile=glob", "check-attr", "-a", "a.c", "sub/z" }); try write(&repo, ".config/git/attributes", "* xdg\n"); try same(&repo, &.{ "check-attr", "-a", "a.c" }); try same(&repo, &.{ "-c", "core.ignoreCase=true", "check-attr", "-a", "A.C", "X", "SUB/z" }); // Warnings are said again each time a directory is entered again. try write(&repo, "other/.gitattributes", "* o b@d\n"); try same(&repo, &.{ "check-attr", "-a", "sub/z", "other/x", "sub/a.c", "other/y", "x" });}
test "check-attr: the index, trees and bare repositories" { var repo = try Repo.init(testing.allocator, testing.io); defer repo.deinit(); try makeRepo(&repo); try repo.git(&.{ "add", ".gitattributes", "sub" }); try repo.commit("attributes"); try write(&repo, "sub/.gitattributes", "z wt\n"); try write(&repo, ".gitattributes", "* top2\n"); try same(&repo, &.{ "check-attr", "-a", "sub/z", "x" }); try same(&repo, &.{ "check-attr", "--cached", "-a", "sub/z", "x" }); try same(&repo, &.{ "check-attr", "--source", "HEAD", "-a", "sub/z", "x" }); try same(&repo, &.{ "check-attr", "--source=HEAD:sub", "-a", "z" }); try same(&repo, &.{ "check-attr", "--source", "nonexistent", "-a", "x" }); try same(&repo, &.{ "check-attr", "--source", "HEAD:sub/.gitattributes", "-a", "x" }); try same(&repo, &.{ "-c", "attr.tree=HEAD", "check-attr", "-a", "sub/z" }); try same(&repo, &.{ "-c", "attr.tree=nonexistent", "check-attr", "-a", "sub/z" }); // A missing file falls back to the index; so does a symbolic link, // with a warning. try repo.tmp.dir.deleteFile(repo.io, "sub/.gitattributes"); try same(&repo, &.{ "check-attr", "-a", "sub/z" }); try repo.tmp.dir.symLink(repo.io, "../glob", "sub/.gitattributes", .{}); try same(&repo, &.{ "check-attr", "-a", "sub/z" }); try repo.git(&.{ "clone", "-q", "--bare", ".", "bare.git" }); try sameIn(&repo, "bare.git", &.{ "check-attr", "-a", "sub/z" }, ""); try sameIn(&repo, "bare.git", &.{ "-c", "attr.tree=HEAD", "check-attr", "-a", "sub/z" }, "");}
test "check-attr: forms, paths, builtin_objectmode and errors" { var repo = try Repo.init(testing.allocator, testing.io); defer repo.deinit(); try makeRepo(&repo); try write(&repo, "sub/f", "f\n"); try repo.tmp.dir.symLink(repo.io, "f", "sub/l", .{}); try repo.git(&.{ "add", "sub/f" }); const input = "x\n\"tab\\there\"\ncaf\xc3\xa9\n\n sp \nsub/a.c"; try sameIn(&repo, ".", &.{ "check-attr", "--stdin", "-a" }, input); try sameIn(&repo, ".", &.{ "check-attr", "--stdin", "diff", "text" }, input); try sameIn(&repo, ".", &.{ "check-attr", "--stdin", "-z", "diff" }, "x\x00tab\there\x00"); try same(&repo, &.{ "check-attr", "-z", "diff", "--", "x", "caf\xc3\xa9" }); try same(&repo, &.{ "-c", "core.quotePath=false", "check-attr", "diff", "--", "caf\xc3\xa9", "tab\there" }); try sameIn(&repo, "sub", &.{ "check-attr", "-a", "z", "../x", "./deep/a.c", "../../outside" }, ""); try same(&repo, &.{ "check-attr", "builtin_objectmode", "--", "sub/f", "sub/l", "sub", "x" }); try same(&repo, &.{ "check-attr", "--cached", "builtin_objectmode", "--", "sub/f", "sub/l" }); for ([_][]const []const u8{ &.{"check-attr"}, &.{ "check-attr", "diff" }, &.{ "check-attr", "-a" }, &.{ "check-attr", "-a", "diff", "--", "x" }, &.{ "check-attr", "--stdin", "diff", "--", "x" }, &.{ "check-attr", "--bogus" }, &.{ "check-attr", "-x" }, &.{ "check-attr", "--source" }, &.{ "check-attr", "b@d", "--", "x" }, &.{ "check-attr", "builtin_foo", "x" }, }) |args| try same(&repo, args);}