Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

Repository

git.Repository
pub const Repository = struct

File

Code

pub const Repository = struct {
    odb: Odb,

    pub fn init(
        repo: *Repository,
        allocator: Allocator,
        format: Oid.Format,
        pack_file: *Io.File.Reader,
        index_file: *Io.File.Reader,
    ) !void {
        repo.* = .{ .odb = undefined };
        try repo.odb.init(allocator, format, pack_file, index_file);
    }

    pub fn deinit(repository: *Repository) void {
        repository.odb.deinit();
        repository.* = undefined;
    }

    /// Checks out the repository at `commit_oid` to `worktree`.
    pub fn checkout(
        repository: *Repository,
        io: Io,
        worktree: Io.Dir,
        commit_oid: Oid,
        diagnostics: *Diagnostics,
    ) !void {
        try repository.odb.seekOid(commit_oid);
        const tree_oid = tree_oid: {
            const commit_object = try repository.odb.readObject();
            if (commit_object.type != .commit) return error.NotACommit;
            break :tree_oid try getCommitTree(repository.odb.format, commit_object.data);
        };
        try repository.checkoutTree(io, worktree, tree_oid, "", diagnostics);
    }

    /// Checks out the tree at `tree_oid` to `worktree`.
    fn checkoutTree(
        repository: *Repository,
        io: Io,
        dir: Io.Dir,
        tree_oid: Oid,
        current_path: []const u8,
        diagnostics: *Diagnostics,
    ) !void {
        try repository.odb.seekOid(tree_oid);
        const tree_object = try repository.odb.readObject();
        if (tree_object.type != .tree) return error.NotATree;
        // The tree object may be evicted from the object cache while we're
        // iterating over it, so we can make a defensive copy here to make sure
        // it remains valid until we're done with it
        const tree_data = try repository.odb.allocator.dupe(u8, tree_object.data);
        defer repository.odb.allocator.free(tree_data);

        var tree_iter: TreeIterator = .{
            .format = repository.odb.format,
            .data = tree_data,
            .pos = 0,
        };
        while (try tree_iter.next()) |entry| {
            switch (entry.type) {
                .directory => {
                    try dir.createDir(io, entry.name, .default_dir);
                    var subdir = try dir.openDir(io, entry.name, .{});
                    defer subdir.close(io);
                    const sub_path = try std.fs.path.join(repository.odb.allocator, &.{ current_path, entry.name });
                    defer repository.odb.allocator.free(sub_path);
                    try repository.checkoutTree(io, subdir, entry.oid, sub_path, diagnostics);
                },
                .file => {
                    try repository.odb.seekOid(entry.oid);
                    const file_object = try repository.odb.readObject();
                    if (file_object.type != .blob) return error.InvalidFile;
                    var file = dir.createFile(io, entry.name, .{ .exclusive = true }) catch |e| {
                        const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name });
                        errdefer diagnostics.allocator.free(file_name);
                        try diagnostics.errors.append(diagnostics.allocator, .{ .unable_to_create_file = .{
                            .code = e,
                            .file_name = file_name,
                        } });
                        continue;
                    };
                    defer file.close(io);
                    try file.writePositionalAll(io, file_object.data, 0);
                },
                .symlink => {
                    try repository.odb.seekOid(entry.oid);
                    const symlink_object = try repository.odb.readObject();
                    if (symlink_object.type != .blob) return error.InvalidFile;
                    const link_name = symlink_object.data;
                    dir.symLink(io, link_name, entry.name, .{}) catch |e| {
                        const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name });
                        errdefer diagnostics.allocator.free(file_name);
                        const link_name_dup = try diagnostics.allocator.dupe(u8, link_name);
                        errdefer diagnostics.allocator.free(link_name_dup);
                        try diagnostics.errors.append(diagnostics.allocator, .{ .unable_to_create_sym_link = .{
                            .code = e,
                            .file_name = file_name,
                            .link_name = link_name_dup,
                        } });
                    };
                },
                .gitlink => {
                    // Consistent with git archive behavior, create the directory but
                    // do nothing else
                    try dir.createDir(io, entry.name, .default_dir);
                },
            }
        }
    }

    /// Returns the ID of the tree associated with the given commit (provided as
    /// raw object data).
    fn getCommitTree(format: Oid.Format, commit_data: []const u8) !Oid {
        if (!mem.startsWith(u8, commit_data, "tree ") or
            commit_data.len < "tree ".len + format.formattedLength() + "\n".len or
            commit_data["tree ".len + format.formattedLength()] != '\n')
        {
            return error.InvalidCommit;
        }
        return try .parse(format, commit_data["tree ".len..][0..format.formattedLength()]);
    }

    const TreeIterator = struct {
        format: Oid.Format,
        data: []const u8,
        pos: usize,

        const Entry = struct {
            type: Type,
            executable: bool,
            name: [:0]const u8,
            oid: Oid,

            const Type = enum(u4) {
                directory = 0o4,
                file = 0o10,
                symlink = 0o12,
                gitlink = 0o16,
            };
        };

        fn next(iterator: *TreeIterator) !?Entry {
            if (iterator.pos == iterator.data.len) return null;

            const mode_end = mem.indexOfScalarPos(u8, iterator.data, iterator.pos, ' ') orelse return error.InvalidTree;
            const mode: packed struct {
                permission: u9,
                unused: u3,
                type: u4,
            } = @bitCast(std.fmt.parseUnsigned(u16, iterator.data[iterator.pos..mode_end], 8) catch return error.InvalidTree);
            const @"type" = std.enums.fromInt(Entry.Type, mode.type) orelse return error.InvalidTree;
            const executable = switch (mode.permission) {
                0 => if (@"type" == .file) return error.InvalidTree else false,
                0o644 => if (@"type" != .file) return error.InvalidTree else false,
                0o755 => if (@"type" != .file) return error.InvalidTree else true,
                else => return error.InvalidTree,
            };
            iterator.pos = mode_end + 1;

            const name_end = mem.indexOfScalarPos(u8, iterator.data, iterator.pos, 0) orelse return error.InvalidTree;
            const name = iterator.data[iterator.pos..name_end :0];
            iterator.pos = name_end + 1;

            const oid_length = iterator.format.byteLength();
            if (iterator.pos + oid_length > iterator.data.len) return error.InvalidTree;
            const oid: Oid = .fromBytes(iterator.format, iterator.data[iterator.pos..][0..oid_length]);
            iterator.pos += oid_length;

            return .{ .type = @"type", .executable = executable, .name = name, .oid = oid };
        }
    };
}