From c3f239267de1824c1913fd1f58f4b56e40dd4da3 Mon Sep 17 00:00:00 2001 From: Chris Antos Date: Sat, 6 Dec 2025 16:19:20 -0800 Subject: [PATCH 1/3] Update the git prompt to support repos with git reftables. Also move the " -> remote" logic from clink-completions/git_prompt.lua directly into the Cmder prompt implementation (otherwise it can't work properly with reftables). --- vendor/clink.lua | 96 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/vendor/clink.lua b/vendor/clink.lua index 81b4d1eef..ae5d54545 100644 --- a/vendor/clink.lua +++ b/vendor/clink.lua @@ -324,7 +324,7 @@ end -- Find out current branch -- @return {nil|git branch name} --- -local function get_git_branch(git_dir) +local function get_git_branch(git_dir, fast) git_dir = git_dir or get_git_dir() -- If git directory not found then we're probably outside of repo @@ -341,8 +341,70 @@ local function get_git_branch(git_dir) -- 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/(.+)') + if os.getenv("CLINK_DEBUG_GIT_REFTABLE") then + branch_name = '.invalid' + end + + -- If the branch name is ".invalid" and the fast method wasn't requested, + -- then invoke git.exe to get accurate current branch info (slow method). + if branch_name == ".invalid" and not fast then + local file = io_popenyield("git --no-optional-locks branch 2>nul") + if file then + for line in file:lines() do -- luacheck: ignore 512 + local b = line:match("^%*%s+(.*)") + if b then + b = b:match("^%((HEAD detached at .*)%)") or b + branch_name = b + break + end + end + file:close() + end + else + branch_name = branch_name or 'HEAD detached at '..HEAD:sub(1, 7) + end + + return branch_name +end + +local function get_git_remote(git_dir, branch) + if not git_dir then return nil end + if not branch then return nil end + + local file = io.open(git_dir.."/config", 'r') + if not file then return nil end + + local git_config = {}; + local section; + for line in file:lines() do + if (line:sub(1,1) == "[" and line:sub(-1) == "]") then + if (line:sub(2,5) == "lfs ") then + section = nil -- skip LFS entries as there can be many and we never use them + else + section = line:sub(2,-2) + git_config[section] = git_config[section] or {} + end + elseif section then + local param, value = line:match('^%s-([%w|_]+)%s-=%s+(.+)$') + if (param and value ~= nil) then + git_config[section][param] = value + end + end + end + file:close(); + + local function get_git_config_value(section, param) + return git_config[section] and git_config[section][param] or nil + end - return branch_name or 'HEAD detached at '..HEAD:sub(1, 7) + local remote_to_push = get_git_config_value('branch "'..branch..'"', 'remote') or '' + local remote_ref = get_git_config_value('remote "'..remote_to_push..'"', 'push') or + get_git_config_value('push', 'default') + + local text = remote_to_push + if remote_ref then text = text..'/'..remote_ref end + + return text ~= '' and text or nil end --- @@ -394,7 +456,7 @@ end -- Get the status and conflict status of working dir -- @return {bool , bool } --- -local function get_git_status() +local function get_git_status(git_dir) local file = io_popenyield("git --no-optional-locks status --porcelain 2>nul") if not file then return {} @@ -416,7 +478,10 @@ local function get_git_status() end file:close() - return { status = is_status, conflict = conflict_found } + local branch = get_git_branch(git_dir, false--[[fast]]) + local remote = get_git_remote(git_dir, branch) + + return { status = is_status, branch = branch, remote = remote, conflict = conflict_found } end --- @@ -515,11 +580,11 @@ end -- Use a prompt coroutine to get git status in the background. -- Cache the info so we can reuse it next time to reduce flicker. --- -local function get_git_info_table() +local function get_git_info_table(git_dir) local info = clink_promptcoroutine(function () -- Use git status if allowed. local cmderGitStatusOptIn = get_git_status_setting() - return cmderGitStatusOptIn and get_git_status() or {} + return cmderGitStatusOptIn and get_git_status(git_dir) or {} end) if not info then info = cached_info.git_info or {} @@ -539,10 +604,11 @@ local function git_prompt_filter() local git_dir = get_git_dir() local color if git_dir then - local branch = get_git_branch(git_dir) + local branch = get_git_branch(git_dir, true--[[fast]]) if branch then -- If in a different repo or branch than last time, discard cached info. - if cached_info.git_dir ~= git_dir or cached_info.git_branch ~= branch then + if cached_info.git_dir ~= git_dir or + (branch ~= ".invalid" and cached_info.git_branch ~= branch) then cached_info.git_info = nil cached_info.git_dir = git_dir cached_info.git_branch = branch @@ -550,10 +616,22 @@ local function git_prompt_filter() -- If we're inside of git repo then try to detect current branch -- Has branch => therefore it is a git folder, now figure out status - local gitInfo = get_git_info_table() + local gitInfo = get_git_info_table(git_dir) local gitStatus = gitInfo.status local gitConflict = gitInfo.conflict + -- Compensate for git reftables. + branch = gitInfo.branch or branch + if branch == ".invalid" then + branch = "Loading..." + elseif gitInfo.remote then + branch = branch.." -> "..gitInfo.remote + end + + -- Prevent an older clink-completions git_prompt.lua scripts from + -- modifying the prompt. + branch = "\x1b[10m"..branch + if gitStatus == nil then color = get_unknown_color() elseif gitStatus then From 13dd021d6afbfc4cab626c16ef4486c41c55adfa Mon Sep 17 00:00:00 2001 From: Chris Antos Date: Sun, 7 Dec 2025 19:52:40 -0800 Subject: [PATCH 2/3] Fix branch name in a newly inited repo. "git branch" does not work in a newly inited repo. So in a newly inited using reftables, the branch name was not able to be retrieved, and the "Loading..." placeholder remained indefinitely. This change updates it to try again with "git branch --show-current" when "git branch" prints empty output. --- vendor/clink.lua | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/vendor/clink.lua b/vendor/clink.lua index ae5d54545..70c669389 100644 --- a/vendor/clink.lua +++ b/vendor/clink.lua @@ -348,17 +348,36 @@ local function get_git_branch(git_dir, fast) -- If the branch name is ".invalid" and the fast method wasn't requested, -- then invoke git.exe to get accurate current branch info (slow method). if branch_name == ".invalid" and not fast then - local file = io_popenyield("git --no-optional-locks branch 2>nul") - if file then - for line in file:lines() do -- luacheck: ignore 512 - local b = line:match("^%*%s+(.*)") - if b then - b = b:match("^%((HEAD detached at .*)%)") or b - branch_name = b + local file + branch_name = nil + + -- Handle the most common case first. + if not branch_name then + file = io_popenyield("git --no-optional-locks branch 2>nul") + if file then + for line in file:lines() do + local b = line:match("^%*%s+(.*)") + if b then + b = b:match("^%((HEAD detached at .*)%)") or b + branch_name = b + break + end + end + file:close() + end + end + + -- Handle the cases where "git branch" output is empty, but "git + -- branch --show-current" shows the branch name (e.g. a new repo). + if not branch_name then + file = io_popenyield("git --no-optional-locks branch --show-current 2>nul") + if file then + for line in file:lines() do -- luacheck: ignore 512 + branch_name = line break end + file:close() end - file:close() end else branch_name = branch_name or 'HEAD detached at '..HEAD:sub(1, 7) From 6ba699a3c4d122b2df095af7a7f9970e3be09e9e Mon Sep 17 00:00:00 2001 From: Chris Antos Date: Sun, 7 Dec 2025 19:58:48 -0800 Subject: [PATCH 3/3] Clean luacheck warning. --- vendor/clink.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vendor/clink.lua b/vendor/clink.lua index 70c669389..804f9c649 100644 --- a/vendor/clink.lua +++ b/vendor/clink.lua @@ -393,8 +393,13 @@ local function get_git_remote(git_dir, branch) local file = io.open(git_dir.."/config", 'r') if not file then return nil end - local git_config = {}; - local section; + local git_config = {} + + local function get_git_config_value(section, param) + return git_config[section] and git_config[section][param] or nil + end + + local section for line in file:lines() do if (line:sub(1,1) == "[" and line:sub(-1) == "]") then if (line:sub(2,5) == "lfs ") then @@ -410,11 +415,7 @@ local function get_git_remote(git_dir, branch) end end end - file:close(); - - local function get_git_config_value(section, param) - return git_config[section] and git_config[section][param] or nil - end + file:close() local remote_to_push = get_git_config_value('branch "'..branch..'"', 'remote') or '' local remote_ref = get_git_config_value('remote "'..remote_to_push..'"', 'push') or