Skip to content

Instantly share code, notes, and snippets.

@poscat0x04
Last active November 14, 2025 14:16
Show Gist options
  • Select an option

  • Save poscat0x04/152faf5087e261314c0961dd3c3367ec to your computer and use it in GitHub Desktop.

Select an option

Save poscat0x04/152faf5087e261314c0961dd3c3367ec to your computer and use it in GitHub Desktop.

How to use this segment

  1. save the file somewhere
  2. source the file in zshrc before sourcing p10k
  3. replace vcs_info with gitstatus in p10k.zsh (usually located at ~/.p10k.zsh)

How does it work

See the excellently documented async.zsh from zsh-autosuggestions for low-level implementation. TLDR: Use process substitution to spawn a zsh instance that executes the main checking logic in the prompt's rendering function and immediately returns. Since the output of the process is connected to a fd of the parent process, we can then use zle's monitor feature to attach a callback function to said fd. After the gawk script finishes and starts to print to the fd the callback function is called, which, updates the parameters and calls p10k to update the prompt.

zmodload zsh/system
typeset -g _gitstatus_fd _gitstatus_child_pid
_gitstatus_check() {
emulate -L zsh
read -d '' script <<- 'EOF'
BEGIN {
conflicted = 0
deleted = 0
renamed = 0
modified = 0
staged = 0
typechanged = 0
untracked = 0
}
END {
print "conflicted", conflicted
print "deleted", deleted
print "renamed", renamed
print "modified", modified
print "staged", staged
print "typechanged", typechanged
print "untracked", untracked
print "ahead", ahead
print "behind", behind
print "branch", branch
}
function handle_file() {
fst = substr($2, 1, 1)
snd = substr($2, 2, 1)
if (snd == "D") {
deleted += 1
}
if (snd == "M" || snd == "A") {
modified += 1
}
if (fst == "M" || fst == "A" || fst == "T" || fst == "D") {
staged += 1
}
if (snd == "T") {
typechanged += 1
}
}
{
if ($1 == "#") {
# header
if ($2 == "branch.ab") {
# ahead-behind
ahead = strtonum(substr($3,2))
behind = strtonum(substr($4,2))
} else if ($2 == "branch.oid") {
commit = substr($3,1,7)
} else if ($2 == "branch.head") {
if ($3 == "(detached)") {
branch = commit
print "head_state", "detached"
} else {
branch = $3
print "head_state", "branch"
}
} else if ($2 == "branch.upstream") {
print "remote_branch", $3
} else if ($2 == "stash") {
print "stashed", $3
}
} else if ($1 == "?") {
# untracked file
untracked += 1
} else if ($1 == "!") {
# ignored file
} else if ($1 == "1") {
# normal file
handle_file()
} else if ($1 == "2") {
# rename/copy
renamed += 1
handle_file()
} else if ($1 == "u") {
# conflicting
conflicted += 1
}
}
EOF
git status --porcelain=v2 --branch --show-stash --ahead-behind | gawk -v "OFS==" -v "ORS=;" $script -
}
_gitstatus_callback() {
emulate -L zsh
local conflicted deleted renamed modified staged typechanged untracked
local ahead behind branch remote_branch stashed head_state
if [[ -z "$2" || "$2" == "hup" ]]; then
IFS='' read -rd '' -u $1 result
eval $result
unset _gitstatus_conflicted _gitstatus_deleted _gitstatus_renamed
unset _gitstatus_modified _gitstatus_staged _gitstatus_typechanged
unset _gitstatus_untracked _gitstatus_ahead _gitstatus_behind
unset _gitstatus_stashed _gitstatus_head_state
if ((conflicted != 0)); then
_gitstatus_conflicted=$conflicted
fi
if ((deleted != 0)); then
_gitstatus_deleted=$deleted
fi
if ((renamed != 0)); then
_gitstatus_renamed=$renamed
fi
if ((modified != 0)); then
_gitstatus_modified=$modified
fi
if ((staged != 0)); then
_gitstatus_staged=$staged
fi
if ((typechanged != 0)); then
_gitstatus_typechanged=$typechanged
fi
if ((untracked != 0)); then
_gitstatus_untracked=$untracked
fi
if ((ahead != 0)); then
_gitstatus_ahead=$ahead
fi
if ((behind != 0)); then
_gitstatus_behind=$behind
fi
_gitstatus_branch=$branch
_gitstatus_head_state=$head_state
_gitstatus_remote_branch=$remote_branch
_gitstatus_stashed=$stashed
p10k display -r
builtin exec {1}<&-
else
echo "command failed with error $2" 1>&2
fi
zle -F "$1"
_gitstatus_fd=
_gitstatus_child_pid=
}
prompt_gitstatus() {
emulate -L zsh
(($+commands[git])) || return
local gitDir="$(git rev-parse --git-dir 2> /dev/null)"
[[ $? != 0 || -z $gitDir ]] && return
# If POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN (e.g., "~" or a glob) matches $PWD, return
if [[ -n ${POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN-} ]]; then
# Expand a leading ~ to $HOME, then treat the result as a glob pattern
if [[ ${${gitDir:A}:h} == ${~${POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN/#\~/$HOME}} ]]; then
return
fi
fi
if [[ -n "$_gitstatus_fd" ]] && { true <&$_gitstatus_fd } 2>/dev/null; then
exec {_gitstatus_fd}>&-
zle -F $_gitstatus_fd
if [[ -n "$_gitstatus_child_pid" ]]; then
if [[ -o monitor ]]; then
kill -TERM -$_gitstatus_child_pid 2>/dev/null
else
kill -TERM $_gitstatus_child_pid 2>/dev/null
fi
fi
fi
exec {_gitstatus_fd}< <(
echo $sysparams[pid]
_gitstatus_check
)
command true
read _gitstatus_child_pid <& $_gitstatus_fd
zle -F "$_gitstatus_fd" _gitstatus_callback
p10k segment -s BRANCH -c '${(M)_gitstatus_head_state:#branch}' -f green -i "%fon" -et '$_gitstatus_branch'
p10k segment -s DETACHED -c '${(M)_gitstatus_head_state:#detached}' -et '%f@%F{green}$_gitstatus_branch'
p10k segment -c '$_gitstatus_remote_branch' -f green -et '⇄ $_gitstatus_remote_branch'
if [[ -f $gitDir/rebase-apply/applying ]]; then
p10k segment -f red -t "am"
elif [[ -f $gitDir/rebase-apply/rebasing ]]; then
p10k segment -f red -t "rebase"
elif [[ -d $gitDir/rebase-apply ]]; then
p10k segment -f red -t "am/rebase"
elif [[ -f $gitDir/rebase-merge/interactive ]]; then
p10k segment -f red -t "rebase-i"
elif [[ -d $gitDir/rebase-merge ]]; then
p10k segment -f red -t "rebase"
elif [[ -f $gitDir/CHERRY_PICK_HEAD ]]; then
p10k segment -f red -t "cherry-pick"
elif [[ -f $gitDir/MERGE_HEAD ]]; then
p10k segment -f red -t "merge"
elif [[ -f $gitDir/BISECT_LOG ]]; then
p10k segment -f red -t "bisect"
elif [[ -f $gitDir/REVERT_HEAD ]]; then
p10k segment -f red -t "revert"
fi
p10k segment -c '$_gitstatus_ahead' -f green -et '⇡$_gitstatus_ahead'
p10k segment -c '$_gitstatus_behind' -f green -et '⇣$_gitstatus_behind'
p10k segment -c '$_gitstatus_conflicted' -f red -et '~$_gitstatus_conflicted'
p10k segment -c '$_gitstatus_stashed' -f green -et '*$_gitstatus_stashed'
p10k segment -c '$_gitstatus_staged' -f yellow -et '+$_gitstatus_staged'
p10k segment -c '$_gitstatus_renamed' -f cyan -et '↠$_gitstatus_renamed'
p10k segment -c '$_gitstatus_deleted' -f red -et '×$_gitstatus_deleted'
p10k segment -c '$_gitstatus_modified' -f yellow -et '!$_gitstatus_modified'
p10k segment -c '$_gitstatus_untracked' -f blue -et '?$_gitstatus_untracked'
}
@fpytloun
Copy link

fpytloun commented Oct 9, 2025

Nice, it speeds-up things.
One addition is to use POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN which will make starting prompt in git-managed home directory much faster.

diff --git a/.zsh/p10k-gitstatus.zsh b/.zsh/p10k-gitstatus.zsh
index 2800797..f0abec2 100644
--- a/.zsh/p10k-gitstatus.zsh
+++ b/.zsh/p10k-gitstatus.zsh
@@ -157,6 +157,13 @@ prompt_gitstatus() {
   local gitDir="$(git rev-parse --git-dir 2> /dev/null)"
   [[ $? != 0 || -z $gitDir ]] && return

+  # If POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN (e.g., "~" or a glob) matches $PWD, return
+  if [[ -n ${POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN-} ]]; then
+    # Expand a leading ~ to $HOME, then treat the result as a glob pattern
+      if [[ ${${gitDir:A}:h} == ${~${POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN/#\~/$HOME}} ]]; then
+      return
+    fi
+  fi

   if [[ -n "$_gitstatus_fd" ]] && { true <&$_gitstatus_fd } 2>/dev/null; then
     exec {_gitstatus_fd}>&-

@poscat0x04
Copy link
Author

thanks, updated

@fpytloun
Copy link

fpytloun commented Oct 13, 2025

One more enhancement is to use icon and color from POWERLEVEL9K_VCS_* options for more seamless migration:

  p10k segment -s BRANCH   -c '${(M)_gitstatus_head_state:#branch}'   -f ${POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR} -et '${POWERLEVEL9K_VCS_BRANCH_ICON}$_gitstatus_branch'
  p10k segment -s DETACHED -c '${(M)_gitstatus_head_state:#detached}' -et '%f@%F{${POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR}}$_gitstatus_branch'

Then in .p10k.zsh it can be set to some nice nerdfont icon:

  typeset -g POWERLEVEL9K_VCS_BRANCH_ICON=' '

There is also POWERLEVEL9K_VCS_UNTRACKED_ICON which might be used in this script (and defaults to ?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment