205 lines
5.5 KiB
Bash
205 lines
5.5 KiB
Bash
|
#!/usr/bin/env bash
|
||
|
# -*- coding: utf-8 -*-
|
||
|
# gitstatus.sh -- produce the current git repo status on STDOUT
|
||
|
# Functionally equivalent to 'gitstatus.py', but written in bash (not python).
|
||
|
#
|
||
|
# Alan K. Stebbens <aks@stebbens.org> [http://github.com/aks]
|
||
|
|
||
|
set -u
|
||
|
|
||
|
if [[ -z "${__GIT_PROMPT_DIR:+x}" ]]; then
|
||
|
SOURCE="${BASH_SOURCE[0]}"
|
||
|
while [[ -h "${SOURCE}" ]]; do
|
||
|
DIR="$( cd -P "$( dirname "${SOURCE}" )" && pwd )"
|
||
|
SOURCE="$(readlink "${SOURCE}")"
|
||
|
[[ "${SOURCE}" != /* ]] && SOURCE="${DIR}/${SOURCE}"
|
||
|
done
|
||
|
__GIT_PROMPT_DIR="$( cd -P "$( dirname "${SOURCE}" )" && pwd )"
|
||
|
fi
|
||
|
|
||
|
if [[ "${__GIT_PROMPT_IGNORE_SUBMODULES:-0}" == "1" ]]; then
|
||
|
_ignore_submodules="--ignore-submodules"
|
||
|
else
|
||
|
_ignore_submodules=""
|
||
|
fi
|
||
|
|
||
|
if [[ "${__GIT_PROMPT_WITH_USERNAME_AND_REPO:-0}" == "1" ]]; then
|
||
|
# returns "user/repo" from remote.origin.url git variable
|
||
|
#
|
||
|
# supports urls:
|
||
|
# https://user@bitbucket.org/user/repo.git
|
||
|
# https://github.com/user/repo.git
|
||
|
# git@github.com:user/repo.git
|
||
|
#
|
||
|
remote_url=$(git config --get remote.origin.url | sed 's|^.*//||; s/.*@//; s/[^:/]\+[:/]//; s/.git$//')
|
||
|
else
|
||
|
remote_url='.'
|
||
|
fi
|
||
|
|
||
|
# shellcheck disable=SC2086
|
||
|
gitstatus=$( LC_ALL=C git --no-optional-locks status ${_ignore_submodules} --untracked-files="${__GIT_PROMPT_SHOW_UNTRACKED_FILES:-normal}" --porcelain --branch )
|
||
|
|
||
|
# if the status is fatal, exit now
|
||
|
[[ ! "${?}" ]] && exit 0
|
||
|
|
||
|
git_dir="$(git rev-parse --git-dir 2>/dev/null)"
|
||
|
[[ -z "${git_dir:+x}" ]] && exit 0
|
||
|
|
||
|
__git_prompt_read ()
|
||
|
{
|
||
|
local f="${1}"
|
||
|
shift
|
||
|
[[ -r "${f}" ]] && read -r "${@}" <"${f}"
|
||
|
}
|
||
|
|
||
|
state=""
|
||
|
step=""
|
||
|
total=""
|
||
|
if [[ -d "${git_dir}/rebase-merge" ]]; then
|
||
|
__git_prompt_read "${git_dir}/rebase-merge/msgnum" step
|
||
|
__git_prompt_read "${git_dir}/rebase-merge/end" total
|
||
|
if [[ -f "${git_dir}/rebase-merge/interactive" ]]; then
|
||
|
state="|REBASE-i"
|
||
|
else
|
||
|
state="|REBASE-m"
|
||
|
fi
|
||
|
else
|
||
|
if [[ -d "${git_dir}/rebase-apply" ]]; then
|
||
|
__git_prompt_read "${git_dir}/rebase-apply/next" step
|
||
|
__git_prompt_read "${git_dir}/rebase-apply/last" total
|
||
|
if [[ -f "${git_dir}/rebase-apply/rebasing" ]]; then
|
||
|
state="|REBASE"
|
||
|
elif [[ -f "${git_dir}/rebase-apply/applying" ]]; then
|
||
|
state="|AM"
|
||
|
else
|
||
|
state="|AM/REBASE"
|
||
|
fi
|
||
|
elif [[ -f "${git_dir}/MERGE_HEAD" ]]; then
|
||
|
state="|MERGING"
|
||
|
elif [[ -f "${git_dir}/CHERRY_PICK_HEAD" ]]; then
|
||
|
state="|CHERRY-PICKING"
|
||
|
elif [[ -f "${git_dir}/REVERT_HEAD" ]]; then
|
||
|
state="|REVERTING"
|
||
|
elif [[ -f "${git_dir}/BISECT_LOG" ]]; then
|
||
|
state="|BISECTING"
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
if [[ -n "${step}" ]] && [[ -n "${total}" ]]; then
|
||
|
state="${state} ${step}/${total}"
|
||
|
fi
|
||
|
|
||
|
num_staged=0
|
||
|
num_changed=0
|
||
|
num_conflicts=0
|
||
|
num_untracked=0
|
||
|
while IFS='' read -r line || [[ -n "${line}" ]]; do
|
||
|
status="${line:0:2}"
|
||
|
while [[ -n ${status} ]]; do
|
||
|
case "${status}" in
|
||
|
#two fixed character matches, loop finished
|
||
|
\#\#) branch_line="${line/\.\.\./^}"; break ;;
|
||
|
\?\?) ((num_untracked++)); break ;;
|
||
|
U?) ((num_conflicts++)); break;;
|
||
|
?U) ((num_conflicts++)); break;;
|
||
|
DD) ((num_conflicts++)); break;;
|
||
|
AA) ((num_conflicts++)); break;;
|
||
|
#two character matches, first loop
|
||
|
?M) ((num_changed++)) ;;
|
||
|
?\ ) ;;
|
||
|
#single character matches, second loop
|
||
|
U) ((num_conflicts++)) ;;
|
||
|
\ ) ;;
|
||
|
*) ((num_staged++)) ;;
|
||
|
esac
|
||
|
status="${status:0:(${#status}-1)}"
|
||
|
done
|
||
|
done <<< "${gitstatus}"
|
||
|
|
||
|
num_stashed=0
|
||
|
if [[ "${__GIT_PROMPT_IGNORE_STASH:-0}" != "1" ]]; then
|
||
|
stash_file="${git_dir}/logs/refs/stash"
|
||
|
if [[ -e "${stash_file}" ]]; then
|
||
|
while IFS='' read -r wcline || [[ -n "${wcline}" ]]; do
|
||
|
((num_stashed++))
|
||
|
done < "${stash_file}"
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
clean=0
|
||
|
if (( num_changed == 0 && num_staged == 0 && num_untracked == 0 && num_stashed == 0 && num_conflicts == 0)) ; then
|
||
|
clean=1
|
||
|
fi
|
||
|
|
||
|
IFS="^" read -ra branch_fields <<< "${branch_line/\#\# }"
|
||
|
branch="${branch_fields[@]:0:1}"
|
||
|
remote=""
|
||
|
upstream=""
|
||
|
|
||
|
detached_head=0
|
||
|
|
||
|
if [[ "${branch}" == *"Initial commit on"* ]]; then
|
||
|
IFS=" " read -ra fields <<< "${branch}"
|
||
|
branch="${fields[@]:3:1}"
|
||
|
remote="_NO_REMOTE_TRACKING_"
|
||
|
remote_url='.'
|
||
|
elif [[ "${branch}" == *"No commits yet on"* ]]; then
|
||
|
IFS=" " read -ra fields <<< "${branch}"
|
||
|
branch="${fields[@]:4:1}"
|
||
|
remote="_NO_REMOTE_TRACKING_"
|
||
|
remote_url='.'
|
||
|
elif [[ "${branch}" == *"no branch"* ]]; then
|
||
|
tag=$( git describe --tags --exact-match )
|
||
|
if [[ -n "${tag}" ]]; then
|
||
|
branch="_PRETAG_${tag}"
|
||
|
detached_head=1
|
||
|
else
|
||
|
branch="_PREHASH_$( git rev-parse --short HEAD )"
|
||
|
detached_head=1
|
||
|
fi
|
||
|
else
|
||
|
if [[ "${#branch_fields[@]}" -eq 1 ]]; then
|
||
|
remote="_NO_REMOTE_TRACKING_"
|
||
|
remote_url='.'
|
||
|
else
|
||
|
IFS="[,]" read -ra remote_fields <<< "${branch_fields[1]}"
|
||
|
upstream="${remote_fields[@]:0:1}"
|
||
|
for remote_field in "${remote_fields[@]}"; do
|
||
|
if [[ "${remote_field}" == "ahead "* ]]; then
|
||
|
num_ahead="${remote_field:6}"
|
||
|
ahead="_AHEAD_${num_ahead}"
|
||
|
fi
|
||
|
if [[ "${remote_field}" == "behind "* ]] || [[ "${remote_field}" == " behind "* ]]; then
|
||
|
num_behind="${remote_field:7}"
|
||
|
behind="_BEHIND_${num_behind# }"
|
||
|
fi
|
||
|
done
|
||
|
remote="${behind-}${ahead-}"
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
if [[ -z "${remote:+x}" ]] ; then
|
||
|
remote='.'
|
||
|
fi
|
||
|
|
||
|
if [[ -z "${upstream:+x}" ]] ; then
|
||
|
upstream='^'
|
||
|
fi
|
||
|
|
||
|
UPSTREAM_TRIMMED=$(echo $upstream | xargs)
|
||
|
|
||
|
printf "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n" \
|
||
|
"${branch}${state}" \
|
||
|
"${remote}" \
|
||
|
"${remote_url}" \
|
||
|
"${UPSTREAM_TRIMMED}" \
|
||
|
"${num_staged}" \
|
||
|
"${num_conflicts}" \
|
||
|
"${num_changed}" \
|
||
|
"${num_untracked}" \
|
||
|
"${num_stashed}" \
|
||
|
"${clean}" \
|
||
|
"${detached_head}"
|
||
|
|
||
|
exit
|