name : gitutil.lua
local path = require('path')

local exports = {}

---
 -- Resolves closest .git directory location.
 -- Navigates subsequently up one level and tries to find .git directory
 -- @param  {string} path Path to directory will be checked. If not provided
 --                       current directory will be used
 -- @return {string} Path to .git directory or nil if such dir not found
exports.get_git_dir = function (start_dir)

    -- Checks if provided directory contains '.git' directory
    -- and returns path to that directory
    local function has_git_dir(dir)
        return #clink.find_dirs(dir..'/.git') > 0 and dir..'/.git'
    end

    -- checks if directory contains '.git' _file_ and if it does
    -- parses it and returns a path to git directory from that file
    local function has_git_file(dir)
        local gitfile = io.open(dir..'/.git')
        if not gitfile then return false end

        local git_dir = gitfile:read():match('gitdir: (.*)')
        gitfile:close()

        if not git_dir then return false end
        -- If found path is absolute don't prepend initial
        -- directory - return absolute path value
        return path.is_absolute(git_dir) and git_dir
            or dir..'/'..git_dir
    end

    -- Set default path to current directory
    if not start_dir or start_dir == '.' then start_dir = clink.get_cwd() end

    -- Calculate parent path now otherwise we won't be
    -- able to do that inside of logical operator
    local parent_path = path.pathname(start_dir)

    return has_git_dir(start_dir)
        or has_git_file(start_dir)
        -- Otherwise go up one level and make a recursive call
        or (parent_path ~= start_dir and exports.get_git_dir(parent_path) or nil)
end

exports.get_git_common_dir = function (start_dir)
    local git_dir = exports.get_git_dir(start_dir)
    if not git_dir then return git_dir end
    local commondirfile = io.open(git_dir..'/commondir')
    if commondirfile then
        -- If there's a commondir file, we're in a git worktree
        local commondir = commondirfile:read()
        commondirfile.close()
        return path.is_absolute(commondir) and commondir
            or git_dir..'/'..commondir
    end
    return git_dir
end

---
 -- Find out current branch
 -- @return {nil|git branch name}
---
exports.get_git_branch = function (dir)
    local git_dir = dir or exports.get_git_dir()

    -- If git directory not found then we're probably outside of repo
    -- or something went wrong. The same is when head_file is nil
    local head_file = git_dir and io.open(git_dir..'/HEAD')
    if not head_file then return end

    local HEAD = head_file:read()
    head_file:close()

    -- if HEAD matches branch expression, then we're on named branch
    -- otherwise it is a detached commit
    local branch_name = HEAD:match('ref: refs/heads/(.+)')
    return branch_name or 'HEAD detached at '..HEAD:sub(1, 7)
end

return exports

© 2026 UnknownSec