From a33aa5f6be267615deeb5329fb354d06c50f51f4 Mon Sep 17 00:00:00 2001 From: fatalerrors Date: Thu, 28 May 2026 11:31:20 +0200 Subject: [PATCH] add timeout to git operations --- README.md | 4 ++- doc/profile.conf.example | 4 +++ profile.d/prompt.sh | 55 +++++++++++++++++++++++++++++++--------- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index dd91170..71040d1 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,7 @@ The twelve available `PROMPT_COLOR_*` keys are: [prompt] PROMPT_SHOW_GIT = 1 PROMPT_SHOW_GIT_STATUS = 1 +PROMPT_GIT_TIMEOUT = 2 PROMPT_SHOW_CONDA = 1 PROMPT_SHOW_VENV = 1 PROMPT_SHOW_SESSION = 1 @@ -366,10 +367,11 @@ When enabled, the top prompt bar appends: - `git:` when inside a Git repository - `git:*` when local changes are present - `git: +N/-M` when ahead/behind upstream +- `git:?` when `git diff` or `git rev-list` exceeded `PROMPT_GIT_TIMEOUT` - `conda:` when a Conda environment is active - `venv:` when a Python virtualenv is active (and Conda is not) - `ssh`, `tmux`, `screen` markers when the session runs in those contexts -- both, separated by `|`, when both are available +- all, separated by `|`, when several are available **Writing a custom theme file:** diff --git a/doc/profile.conf.example b/doc/profile.conf.example index 4e97158..2610cb7 100755 --- a/doc/profile.conf.example +++ b/doc/profile.conf.example @@ -201,6 +201,10 @@ TERM=xterm-256color # Include Git dirty marker and upstream drift (+ahead/-behind) in the context. #PROMPT_SHOW_GIT_STATUS=1 # +# Timeout in seconds for git diff and git rev-list operations. +# If exceeded, the prompt displays git:? instead of full status. +#PROMPT_GIT_TIMEOUT=2 +# # Show Conda environment name at the end of the top bar when active. #PROMPT_SHOW_CONDA=1 # diff --git a/profile.d/prompt.sh b/profile.d/prompt.sh index f913079..4f119c2 100644 --- a/profile.d/prompt.sh +++ b/profile.d/prompt.sh @@ -121,7 +121,7 @@ load_theme() _lth_line="${_lth_line%$'\r'}" # strip CR _lth_line="${_lth_line#"${_lth_line%%[![:space:]]*}"}" # ltrim _lth_line="${_lth_line%"${_lth_line##*[![:space:]]}"}" # rtrim - [[ -z "$_lth_line" || "$_lth_line" == '#'* ]] && continue # blank/comment + [[ -z "$_lth_line" || "$_lth_line" == '#'* ]] && continue # blank/comment [[ "$_lth_line" == 'export '* ]] && _lth_line="${_lth_line#export }" # strip prefix if [[ "$_lth_line" != *=* ]]; then @@ -519,25 +519,56 @@ set_prompt() branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || \ branch=$(git rev-parse --short HEAD 2>/dev/null) || return 0 - local dirty="" sync="" + local dirty="" sync="" timed_out=0 + local _git_timeout="${PROMPT_GIT_TIMEOUT:-2}" + + # Build a timeout wrapper if the 'timeout' command is available; + # fall back to direct execution on systems that lack it. + local -a _tw=() + command -v timeout >/dev/null 2>&1 && _tw=(timeout "$_git_timeout") + if [[ "${PROMPT_SHOW_GIT_STATUS:-1}" != "0" ]]; then - if ! git diff --no-ext-diff --quiet --ignore-submodules -- 2>/dev/null || \ - ! git diff --cached --no-ext-diff --quiet --ignore-submodules -- 2>/dev/null; then + local _ec + + # Dirty check — working tree + "${_tw[@]}" git diff --no-ext-diff --quiet --ignore-submodules -- 2>/dev/null + _ec=$? + if [[ $_ec -eq 124 ]]; then + timed_out=1 + elif [[ $_ec -ne 0 ]]; then dirty="*" + else + # Dirty check — index + "${_tw[@]}" git diff --cached --no-ext-diff --quiet --ignore-submodules -- 2>/dev/null + _ec=$? + if [[ $_ec -eq 124 ]]; then + timed_out=1 + elif [[ $_ec -ne 0 ]]; then + dirty="*" + fi fi - local counts ahead behind - counts=$(git rev-list --left-right --count "@{upstream}...HEAD" 2>/dev/null) || counts="" - if [[ "$counts" =~ ^([0-9]+)[[:space:]]+([0-9]+)$ ]]; then - behind="${BASH_REMATCH[1]}" - ahead="${BASH_REMATCH[2]}" - if [[ "$ahead" -gt 0 || "$behind" -gt 0 ]]; then - sync=" +${ahead}/-${behind}" + if [[ $timed_out -eq 0 ]]; then + local counts ahead behind + counts=$("${_tw[@]}" git rev-list --left-right --count "@{upstream}...HEAD" 2>/dev/null) + _ec=$? + if [[ $_ec -eq 124 ]]; then + timed_out=1 + elif [[ "$counts" =~ ^([0-9]+)[[:space:]]+([0-9]+)$ ]]; then + behind="${BASH_REMATCH[1]}" + ahead="${BASH_REMATCH[2]}" + if [[ "$ahead" -gt 0 || "$behind" -gt 0 ]]; then + sync=" +${ahead}/-${behind}" + fi fi fi fi - printf "%s" "git:${branch}${dirty}${sync}" + if [[ $timed_out -eq 1 ]]; then + printf "%s" "git:${branch}?" + else + printf "%s" "git:${branch}${dirty}${sync}" + fi } local _prompt_conda_env