324324-- Find out current branch
325325-- @return {nil|git branch name}
326326---
327- local function get_git_branch (git_dir )
327+ local function get_git_branch (git_dir , fast )
328328 git_dir = git_dir or get_git_dir ()
329329
330330 -- If git directory not found then we're probably outside of repo
@@ -341,8 +341,90 @@ local function get_git_branch(git_dir)
341341 -- if HEAD matches branch expression, then we're on named branch
342342 -- otherwise it is a detached commit
343343 local branch_name = HEAD :match (' ref: refs/heads/(.+)' )
344+ if os.getenv (" CLINK_DEBUG_GIT_REFTABLE" ) then
345+ branch_name = ' .invalid'
346+ end
347+
348+ -- If the branch name is ".invalid" and the fast method wasn't requested,
349+ -- then invoke git.exe to get accurate current branch info (slow method).
350+ if branch_name == " .invalid" and not fast then
351+ local file
352+ branch_name = nil
353+
354+ -- Handle the most common case first.
355+ if not branch_name then
356+ file = io_popenyield (" git --no-optional-locks branch 2>nul" )
357+ if file then
358+ for line in file :lines () do
359+ local b = line :match (" ^%*%s+(.*)" )
360+ if b then
361+ b = b :match (" ^%((HEAD detached at .*)%)" ) or b
362+ branch_name = b
363+ break
364+ end
365+ end
366+ file :close ()
367+ end
368+ end
369+
370+ -- Handle the cases where "git branch" output is empty, but "git
371+ -- branch --show-current" shows the branch name (e.g. a new repo).
372+ if not branch_name then
373+ file = io_popenyield (" git --no-optional-locks branch --show-current 2>nul" )
374+ if file then
375+ for line in file :lines () do -- luacheck: ignore 512
376+ branch_name = line
377+ break
378+ end
379+ file :close ()
380+ end
381+ end
382+ else
383+ branch_name = branch_name or ' HEAD detached at ' .. HEAD :sub (1 , 7 )
384+ end
385+
386+ return branch_name
387+ end
388+
389+ local function get_git_remote (git_dir , branch )
390+ if not git_dir then return nil end
391+ if not branch then return nil end
392+
393+ local file = io.open (git_dir .. " /config" , ' r' )
394+ if not file then return nil end
395+
396+ local git_config = {}
397+
398+ local function get_git_config_value (section , param )
399+ return git_config [section ] and git_config [section ][param ] or nil
400+ end
344401
345- return branch_name or ' HEAD detached at ' .. HEAD :sub (1 , 7 )
402+ local section
403+ for line in file :lines () do
404+ if (line :sub (1 ,1 ) == " [" and line :sub (- 1 ) == " ]" ) then
405+ if (line :sub (2 ,5 ) == " lfs " ) then
406+ section = nil -- skip LFS entries as there can be many and we never use them
407+ else
408+ section = line :sub (2 ,- 2 )
409+ git_config [section ] = git_config [section ] or {}
410+ end
411+ elseif section then
412+ local param , value = line :match (' ^%s-([%w|_]+)%s-=%s+(.+)$' )
413+ if (param and value ~= nil ) then
414+ git_config [section ][param ] = value
415+ end
416+ end
417+ end
418+ file :close ()
419+
420+ local remote_to_push = get_git_config_value (' branch "' .. branch .. ' "' , ' remote' ) or ' '
421+ local remote_ref = get_git_config_value (' remote "' .. remote_to_push .. ' "' , ' push' ) or
422+ get_git_config_value (' push' , ' default' )
423+
424+ local text = remote_to_push
425+ if remote_ref then text = text .. ' /' .. remote_ref end
426+
427+ return text ~= ' ' and text or nil
346428end
347429
348430---
394476-- Get the status and conflict status of working dir
395477-- @return {bool <status>, bool <is_conflict>}
396478---
397- local function get_git_status ()
479+ local function get_git_status (git_dir )
398480 local file = io_popenyield (" git --no-optional-locks status --porcelain 2>nul" )
399481 if not file then
400482 return {}
@@ -416,7 +498,10 @@ local function get_git_status()
416498 end
417499 file :close ()
418500
419- return { status = is_status , conflict = conflict_found }
501+ local branch = get_git_branch (git_dir , false --[[ fast]] )
502+ local remote = get_git_remote (git_dir , branch )
503+
504+ return { status = is_status , branch = branch , remote = remote , conflict = conflict_found }
420505end
421506
422507---
@@ -515,11 +600,11 @@ end
515600-- Use a prompt coroutine to get git status in the background.
516601-- Cache the info so we can reuse it next time to reduce flicker.
517602---
518- local function get_git_info_table ()
603+ local function get_git_info_table (git_dir )
519604 local info = clink_promptcoroutine (function ()
520605 -- Use git status if allowed.
521606 local cmderGitStatusOptIn = get_git_status_setting ()
522- return cmderGitStatusOptIn and get_git_status () or {}
607+ return cmderGitStatusOptIn and get_git_status (git_dir ) or {}
523608 end )
524609 if not info then
525610 info = cached_info .git_info or {}
@@ -539,21 +624,34 @@ local function git_prompt_filter()
539624 local git_dir = get_git_dir ()
540625 local color
541626 if git_dir then
542- local branch = get_git_branch (git_dir )
627+ local branch = get_git_branch (git_dir , true --[[ fast ]] )
543628 if branch then
544629 -- If in a different repo or branch than last time, discard cached info.
545- if cached_info .git_dir ~= git_dir or cached_info .git_branch ~= branch then
630+ if cached_info .git_dir ~= git_dir or
631+ (branch ~= " .invalid" and cached_info .git_branch ~= branch ) then
546632 cached_info .git_info = nil
547633 cached_info .git_dir = git_dir
548634 cached_info .git_branch = branch
549635 end
550636
551637 -- If we're inside of git repo then try to detect current branch
552638 -- Has branch => therefore it is a git folder, now figure out status
553- local gitInfo = get_git_info_table ()
639+ local gitInfo = get_git_info_table (git_dir )
554640 local gitStatus = gitInfo .status
555641 local gitConflict = gitInfo .conflict
556642
643+ -- Compensate for git reftables.
644+ branch = gitInfo .branch or branch
645+ if branch == " .invalid" then
646+ branch = " Loading..."
647+ elseif gitInfo .remote then
648+ branch = branch .. " -> " .. gitInfo .remote
649+ end
650+
651+ -- Prevent an older clink-completions git_prompt.lua scripts from
652+ -- modifying the prompt.
653+ branch = " \x1b [10m" .. branch
654+
557655 if gitStatus == nil then
558656 color = get_unknown_color ()
559657 elseif gitStatus then
0 commit comments