ohmyzsh
This commit is contained in:
268
zsh/.oh-my-zsh/custom/themes/agnosterg3.zsh-theme
Normal file
268
zsh/.oh-my-zsh/custom/themes/agnosterg3.zsh-theme
Normal file
@ -0,0 +1,268 @@
|
||||
# vim:ft=zsh ts=2 sw=2 sts=2
|
||||
#
|
||||
# agnoster's Theme - https://gist.github.com/3712874
|
||||
# A Powerline-inspired theme for ZSH
|
||||
#
|
||||
# # README
|
||||
#
|
||||
# In order for this theme to render correctly, you will need a
|
||||
# [Powerline-patched font](https://github.com/Lokaltog/powerline-fonts).
|
||||
# Make sure you have a recent version: the code points that Powerline
|
||||
# uses changed in 2012, and older versions will display incorrectly,
|
||||
# in confusing ways.
|
||||
#
|
||||
# In addition, I recommend the
|
||||
# [Solarized theme](https://github.com/altercation/solarized/) and, if you're
|
||||
# using it on Mac OS X, [iTerm 2](https://iterm2.com/) over Terminal.app -
|
||||
# it has significantly better color fidelity.
|
||||
#
|
||||
# If using with "light" variant of the Solarized color schema, set
|
||||
# SOLARIZED_THEME variable to "light". If you don't specify, we'll assume
|
||||
# you're using the "dark" variant.
|
||||
#
|
||||
# # Goals
|
||||
#
|
||||
# The aim of this theme is to only show you *relevant* information. Like most
|
||||
# prompts, it will only show git information when in a git working directory.
|
||||
# However, it goes a step further: everything from the current user and
|
||||
# hostname to whether the last call exited with an error to whether background
|
||||
# jobs are running in this shell will all be displayed automatically when
|
||||
# appropriate.
|
||||
|
||||
### Segment drawing
|
||||
# A few utility functions to make it easy and re-usable to draw segmented prompts
|
||||
|
||||
CURRENT_BG='NONE'
|
||||
|
||||
case ${SOLARIZED_THEME:-dark} in
|
||||
light) CURRENT_FG='white';;
|
||||
*) CURRENT_FG='black';;
|
||||
esac
|
||||
|
||||
# Special Powerline characters
|
||||
|
||||
() {
|
||||
local LC_ALL="" LC_CTYPE="en_US.UTF-8"
|
||||
# NOTE: This segment separator character is correct. In 2012, Powerline changed
|
||||
# the code points they use for their special characters. This is the new code point.
|
||||
# If this is not working for you, you probably have an old version of the
|
||||
# Powerline-patched fonts installed. Download and install the new version.
|
||||
# Do not submit PRs to change this unless you have reviewed the Powerline code point
|
||||
# history and have new information.
|
||||
# This is defined using a Unicode escape sequence so it is unambiguously readable, regardless of
|
||||
# what font the user is viewing this source code in. Do not replace the
|
||||
# escape sequence with a single literal character.
|
||||
# Do not change this! Do not make it '\u2b80'; that is the old, wrong code point.
|
||||
SEGMENT_SEPARATOR=$'\ue0b0'
|
||||
}
|
||||
|
||||
# Begin a segment
|
||||
# Takes two arguments, background and foreground. Both can be omitted,
|
||||
# rendering default background/foreground.
|
||||
prompt_segment() {
|
||||
local bg fg
|
||||
[[ -n $1 ]] && bg="%K{$1}" || bg="%k"
|
||||
[[ -n $2 ]] && fg="%F{$2}" || fg="%f"
|
||||
if [[ $CURRENT_BG != 'NONE' && $1 != $CURRENT_BG ]]; then
|
||||
echo -n " %{$bg%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR%{$fg%} "
|
||||
else
|
||||
echo -n "%{$bg%}%{$fg%} "
|
||||
fi
|
||||
CURRENT_BG=$1
|
||||
[[ -n $3 ]] && echo -n $3
|
||||
}
|
||||
|
||||
# End the prompt, closing any open segments
|
||||
prompt_end() {
|
||||
if [[ -n $CURRENT_BG ]]; then
|
||||
echo -n " %{%k%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR"
|
||||
else
|
||||
echo -n "%{%k%}"
|
||||
fi
|
||||
echo -n "%{%f%}"
|
||||
CURRENT_BG=''
|
||||
}
|
||||
|
||||
### Prompt components
|
||||
# Each component will draw itself, and hide itself if no information needs to be shown
|
||||
|
||||
# Context: user@hostname (who am I and where am I)
|
||||
prompt_context() {
|
||||
if [[ "$USERNAME" != "$DEFAULT_USER" || -n "$SSH_CLIENT" ]]; then
|
||||
prompt_segment black default "%(!.%{%F{yellow}%}.)%n@%m"
|
||||
fi
|
||||
}
|
||||
|
||||
# Git: branch/detached head, dirty status
|
||||
prompt_git() {
|
||||
(( $+commands[git] )) || return
|
||||
if [[ "$(git config --get oh-my-zsh.hide-status 2>/dev/null)" = 1 ]]; then
|
||||
return
|
||||
fi
|
||||
local PL_BRANCH_CHAR
|
||||
() {
|
||||
local LC_ALL="" LC_CTYPE="en_US.UTF-8"
|
||||
PL_BRANCH_CHAR=$'\ue0a0' #
|
||||
}
|
||||
local ref dirty mode repo_path
|
||||
|
||||
if [[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]]; then
|
||||
repo_path=$(git rev-parse --git-dir 2>/dev/null)
|
||||
dirty=$(parse_git_dirty)
|
||||
ref=$(git symbolic-ref HEAD 2> /dev/null) || ref="➦ $(git rev-parse --short HEAD 2> /dev/null)"
|
||||
if [[ -n $dirty ]]; then
|
||||
prompt_segment yellow black
|
||||
else
|
||||
prompt_segment green $CURRENT_FG
|
||||
fi
|
||||
|
||||
if [[ -e "${repo_path}/BISECT_LOG" ]]; then
|
||||
mode=" <B>"
|
||||
elif [[ -e "${repo_path}/MERGE_HEAD" ]]; then
|
||||
mode=" >M<"
|
||||
elif [[ -e "${repo_path}/rebase" || -e "${repo_path}/rebase-apply" || -e "${repo_path}/rebase-merge" || -e "${repo_path}/../.dotest" ]]; then
|
||||
mode=" >R>"
|
||||
fi
|
||||
|
||||
setopt promptsubst
|
||||
autoload -Uz vcs_info
|
||||
|
||||
zstyle ':vcs_info:*' enable git
|
||||
zstyle ':vcs_info:*' get-revision true
|
||||
zstyle ':vcs_info:*' check-for-changes true
|
||||
zstyle ':vcs_info:*' stagedstr '✚'
|
||||
zstyle ':vcs_info:*' unstagedstr '±'
|
||||
zstyle ':vcs_info:*' formats ' %u%c'
|
||||
zstyle ':vcs_info:*' actionformats ' %u%c'
|
||||
vcs_info
|
||||
echo -n "${ref/refs\/heads\//$PL_BRANCH_CHAR }${vcs_info_msg_0_%% }${mode}"
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_bzr() {
|
||||
(( $+commands[bzr] )) || return
|
||||
|
||||
# Test if bzr repository in directory hierarchy
|
||||
local dir="$PWD"
|
||||
while [[ ! -d "$dir/.bzr" ]]; do
|
||||
[[ "$dir" = "/" ]] && return
|
||||
dir="${dir:h}"
|
||||
done
|
||||
|
||||
local bzr_status status_mod status_all revision
|
||||
if bzr_status=$(bzr status 2>&1); then
|
||||
status_mod=$(echo -n "$bzr_status" | head -n1 | grep "modified" | wc -m)
|
||||
status_all=$(echo -n "$bzr_status" | head -n1 | wc -m)
|
||||
revision=$(bzr log -r-1 --log-format line | cut -d: -f1)
|
||||
if [[ $status_mod -gt 0 ]] ; then
|
||||
prompt_segment yellow black "bzr@$revision ✚"
|
||||
else
|
||||
if [[ $status_all -gt 0 ]] ; then
|
||||
prompt_segment yellow black "bzr@$revision"
|
||||
else
|
||||
prompt_segment green black "bzr@$revision"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_hg() {
|
||||
(( $+commands[hg] )) || return
|
||||
local rev st branch
|
||||
if $(hg id >/dev/null 2>&1); then
|
||||
if $(hg prompt >/dev/null 2>&1); then
|
||||
if [[ $(hg prompt "{status|unknown}") = "?" ]]; then
|
||||
# if files are not added
|
||||
prompt_segment red white
|
||||
st='±'
|
||||
elif [[ -n $(hg prompt "{status|modified}") ]]; then
|
||||
# if any modification
|
||||
prompt_segment yellow black
|
||||
st='±'
|
||||
else
|
||||
# if working copy is clean
|
||||
prompt_segment green $CURRENT_FG
|
||||
fi
|
||||
echo -n $(hg prompt "☿ {rev}@{branch}") $st
|
||||
else
|
||||
st=""
|
||||
rev=$(hg id -n 2>/dev/null | sed 's/[^-0-9]//g')
|
||||
branch=$(hg id -b 2>/dev/null)
|
||||
if `hg st | grep -q "^\?"`; then
|
||||
prompt_segment red black
|
||||
st='±'
|
||||
elif `hg st | grep -q "^[MA]"`; then
|
||||
prompt_segment yellow black
|
||||
st='±'
|
||||
else
|
||||
prompt_segment green $CURRENT_FG
|
||||
fi
|
||||
echo -n "☿ $rev@$branch" $st
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Dir:
|
||||
# - current working directory
|
||||
# - CitC client name and //-abbreviated directory when in google3
|
||||
prompt_dir() {
|
||||
if [[ $PWD =~ '/google/src/cloud/[^/]+/(.+)/google3(.*)' ]]; then
|
||||
prompt_segment cyan black "${match[1]}"
|
||||
prompt_segment blue $CURRENT_FG "//${match[2]#/}"
|
||||
# Use CitC client names as window titles in screen/tmux
|
||||
print -n "\e]2;${match[1]}\a" >/dev/tty
|
||||
else
|
||||
prompt_segment blue $CURRENT_FG '%~'
|
||||
fi
|
||||
}
|
||||
|
||||
# Virtualenv: current working virtualenv
|
||||
prompt_virtualenv() {
|
||||
local virtualenv_path="$VIRTUAL_ENV"
|
||||
if [[ -n $virtualenv_path && -n $VIRTUAL_ENV_DISABLE_PROMPT ]]; then
|
||||
prompt_segment blue black "(`basename $virtualenv_path`)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Status:
|
||||
# - was there an error
|
||||
# - am I root
|
||||
# - are there background jobs?
|
||||
prompt_status() {
|
||||
local -a symbols
|
||||
|
||||
[[ $RETVAL -ne 0 ]] && symbols+="%{%F{red}%}✘"
|
||||
[[ $UID -eq 0 ]] && symbols+="%{%F{yellow}%}⚡"
|
||||
[[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{cyan}%}⚙"
|
||||
|
||||
[[ -n "$symbols" ]] && prompt_segment black default "$symbols"
|
||||
}
|
||||
|
||||
#AWS Profile:
|
||||
# - display current AWS_PROFILE name
|
||||
# - displays yellow on red if profile name contains 'production' or
|
||||
# ends in '-prod'
|
||||
# - displays black on green otherwise
|
||||
prompt_aws() {
|
||||
[[ -z "$AWS_PROFILE" || "$SHOW_AWS_PROMPT" = false ]] && return
|
||||
case "$AWS_PROFILE" in
|
||||
*-prod|*production*) prompt_segment red yellow "AWS: $AWS_PROFILE" ;;
|
||||
*) prompt_segment green black "AWS: $AWS_PROFILE" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
## Main prompt
|
||||
build_prompt() {
|
||||
RETVAL=$?
|
||||
prompt_status
|
||||
prompt_virtualenv
|
||||
prompt_aws
|
||||
prompt_context
|
||||
prompt_dir
|
||||
prompt_git
|
||||
prompt_bzr
|
||||
prompt_hg
|
||||
prompt_end
|
||||
}
|
||||
|
||||
PROMPT='%{%f%b%k%}$(build_prompt) '
|
4
zsh/.oh-my-zsh/custom/themes/example.zsh-theme
Normal file
4
zsh/.oh-my-zsh/custom/themes/example.zsh-theme
Normal file
@ -0,0 +1,4 @@
|
||||
# Put your custom themes in this folder.
|
||||
# Example:
|
||||
|
||||
PROMPT="%{$fg[red]%}%n%{$reset_color%}@%{$fg[blue]%}%m %{$fg[yellow]%}%~ %{$reset_color%}%% "
|
5
zsh/.oh-my-zsh/custom/themes/powerlevel10k/.gitattributes
vendored
Normal file
5
zsh/.oh-my-zsh/custom/themes/powerlevel10k/.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
* text=auto
|
||||
*.zsh text eol=lf
|
||||
*.zsh-theme text eol=lf
|
||||
/prompt_powerlevel9k_setup text eol=lf
|
||||
/prompt_powerlevel10k_setup text eol=lf
|
1
zsh/.oh-my-zsh/custom/themes/powerlevel10k/.gitignore
vendored
Normal file
1
zsh/.oh-my-zsh/custom/themes/powerlevel10k/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.zwc
|
22
zsh/.oh-my-zsh/custom/themes/powerlevel10k/LICENSE
Normal file
22
zsh/.oh-my-zsh/custom/themes/powerlevel10k/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2009-2014 Robby Russell and contributors (see https://github.com/robbyrussell/oh-my-zsh/contributors)
|
||||
Copyright (c) 2014-2017 Ben Hilburn <bhilburn@gmail.com>
|
||||
Copyright (c) 2019 Roman Perepelitsa <roman.perepelitsa@gmail.com> and contributors (see https://github.com/romkatv/powerlevel10k/contributors)
|
||||
|
||||
MIT LICENSE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
14
zsh/.oh-my-zsh/custom/themes/powerlevel10k/Makefile
Normal file
14
zsh/.oh-my-zsh/custom/themes/powerlevel10k/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
ZSH := $(shell command -v zsh 2> /dev/null)
|
||||
|
||||
all:
|
||||
|
||||
zwc:
|
||||
$(MAKE) -C gitstatus zwc
|
||||
$(or $(ZSH),:) -fc 'for f in *.zsh-theme internal/*.zsh; do zcompile -R -- $$f.zwc $$f || exit; done'
|
||||
|
||||
minify:
|
||||
$(MAKE) -C gitstatus minify
|
||||
rm -rf -- .git .gitattributes .gitignore LICENSE Makefile README.md font.md powerlevel10k.png
|
||||
|
||||
pkg: zwc
|
||||
$(MAKE) -C gitstatus pkg
|
1877
zsh/.oh-my-zsh/custom/themes/powerlevel10k/README.md
Normal file
1877
zsh/.oh-my-zsh/custom/themes/powerlevel10k/README.md
Normal file
File diff suppressed because it is too large
Load Diff
1634
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-classic.zsh
Normal file
1634
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-classic.zsh
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1611
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-lean.zsh
Normal file
1611
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-lean.zsh
Normal file
File diff suppressed because it is too large
Load Diff
193
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-pure.zsh
Normal file
193
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-pure.zsh
Normal file
@ -0,0 +1,193 @@
|
||||
# Config file for Powerlevel10k with the style of Pure (https://github.com/sindresorhus/pure).
|
||||
#
|
||||
# Differences from Pure:
|
||||
#
|
||||
# - Git:
|
||||
# - `@c4d3ec2c` instead of something like `v1.4.0~11` when in detached HEAD state.
|
||||
# - No automatic `git fetch` (the same as in Pure with `PURE_GIT_PULL=0`).
|
||||
#
|
||||
# Apart from the differences listed above, the replication of Pure prompt is exact. This includes
|
||||
# even the questionable parts. For example, just like in Pure, there is no indication of Git status
|
||||
# being stale; prompt symbol is the same in command, visual and overwrite vi modes; when prompt
|
||||
# doesn't fit on one line, it wraps around with no attempt to shorten it.
|
||||
#
|
||||
# If you like the general style of Pure but not particularly attached to all its quirks, type
|
||||
# `p10k configure` and pick "Lean" style. This will give you slick minimalist prompt while taking
|
||||
# advantage of Powerlevel10k features that aren't present in Pure.
|
||||
|
||||
# Temporarily change options.
|
||||
'builtin' 'local' '-a' 'p10k_config_opts'
|
||||
[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases')
|
||||
[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob')
|
||||
[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
|
||||
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
|
||||
|
||||
() {
|
||||
emulate -L zsh -o extended_glob
|
||||
|
||||
# Unset all configuration options.
|
||||
unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR'
|
||||
|
||||
# Zsh >= 5.1 is required.
|
||||
autoload -Uz is-at-least && is-at-least 5.1 || return
|
||||
|
||||
# Prompt colors.
|
||||
local grey=242
|
||||
local red=1
|
||||
local yellow=3
|
||||
local blue=4
|
||||
local magenta=5
|
||||
local cyan=6
|
||||
local white=7
|
||||
|
||||
# Left prompt segments.
|
||||
typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
|
||||
# =========================[ Line #1 ]=========================
|
||||
context # user@host
|
||||
dir # current directory
|
||||
vcs # git status
|
||||
command_execution_time # previous command duration
|
||||
# =========================[ Line #2 ]=========================
|
||||
newline # \n
|
||||
virtualenv # python virtual environment
|
||||
prompt_char # prompt symbol
|
||||
)
|
||||
|
||||
# Right prompt segments.
|
||||
typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
|
||||
# =========================[ Line #1 ]=========================
|
||||
# command_execution_time # previous command duration
|
||||
# virtualenv # python virtual environment
|
||||
# context # user@host
|
||||
# time # current time
|
||||
# =========================[ Line #2 ]=========================
|
||||
newline # \n
|
||||
)
|
||||
|
||||
# Basic style options that define the overall prompt look.
|
||||
typeset -g POWERLEVEL9K_BACKGROUND= # transparent background
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol
|
||||
typeset -g POWERLEVEL9K_VISUAL_IDENTIFIER_EXPANSION= # no segment icons
|
||||
|
||||
# Add an empty line before each prompt except the first. This doesn't emulate the bug
|
||||
# in Pure that makes prompt drift down whenever you use the Alt-C binding from fzf or similar.
|
||||
typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=true
|
||||
|
||||
# Magenta prompt symbol if the last command succeeded.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS}_FOREGROUND=$magenta
|
||||
# Red prompt symbol if the last command failed.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS}_FOREGROUND=$red
|
||||
# Default prompt symbol.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯'
|
||||
# Prompt symbol in command vi mode.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮'
|
||||
# Prompt symbol in visual vi mode is the same as in command mode.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='❮'
|
||||
# Prompt symbol in overwrite vi mode is the same as in command mode.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=false
|
||||
|
||||
# Grey Python Virtual Environment.
|
||||
typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=$grey
|
||||
# Don't show Python version.
|
||||
typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
|
||||
typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
|
||||
|
||||
# Blue current directory.
|
||||
typeset -g POWERLEVEL9K_DIR_FOREGROUND=$blue
|
||||
|
||||
# Context format when root: user@host. The first part white, the rest grey.
|
||||
typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE="%F{$white}%n%f%F{$grey}@%m%f"
|
||||
# Context format when not root: user@host. The whole thing grey.
|
||||
typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE="%F{$grey}%n@%m%f"
|
||||
# Don't show context unless root or in SSH.
|
||||
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION=
|
||||
|
||||
# Show previous command duration only if it's >= 5s.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=5
|
||||
# Don't show fractional seconds. Thus, 7s rather than 7.3s.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0
|
||||
# Duration format: 1d 2h 3m 4s.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s'
|
||||
# Yellow previous command duration.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=$yellow
|
||||
|
||||
# Grey Git prompt. This makes stale prompts indistinguishable from up-to-date ones.
|
||||
typeset -g POWERLEVEL9K_VCS_FOREGROUND=$grey
|
||||
|
||||
# Disable async loading indicator to make directories that aren't Git repositories
|
||||
# indistinguishable from large Git repositories without known state.
|
||||
typeset -g POWERLEVEL9K_VCS_LOADING_TEXT=
|
||||
|
||||
# Don't wait for Git status even for a millisecond, so that prompt always updates
|
||||
# asynchronously when Git state changes.
|
||||
typeset -g POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS=0
|
||||
|
||||
# Cyan ahead/behind arrows.
|
||||
typeset -g POWERLEVEL9K_VCS_{INCOMING,OUTGOING}_CHANGESFORMAT_FOREGROUND=$cyan
|
||||
# Don't show remote branch, current tag or stashes.
|
||||
typeset -g POWERLEVEL9K_VCS_GIT_HOOKS=(vcs-detect-changes git-untracked git-aheadbehind)
|
||||
# Don't show the branch icon.
|
||||
typeset -g POWERLEVEL9K_VCS_BRANCH_ICON=
|
||||
# When in detached HEAD state, show @commit where branch normally goes.
|
||||
typeset -g POWERLEVEL9K_VCS_COMMIT_ICON='@'
|
||||
# Don't show staged, unstaged, untracked indicators.
|
||||
typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED}_ICON=
|
||||
# Show '*' when there are staged, unstaged or untracked files.
|
||||
typeset -g POWERLEVEL9K_VCS_DIRTY_ICON='*'
|
||||
# Show '⇣' if local branch is behind remote.
|
||||
typeset -g POWERLEVEL9K_VCS_INCOMING_CHANGES_ICON=':⇣'
|
||||
# Show '⇡' if local branch is ahead of remote.
|
||||
typeset -g POWERLEVEL9K_VCS_OUTGOING_CHANGES_ICON=':⇡'
|
||||
# Don't show the number of commits next to the ahead/behind arrows.
|
||||
typeset -g POWERLEVEL9K_VCS_{COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=1
|
||||
# Remove space between '⇣' and '⇡' and all trailing spaces.
|
||||
typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${${${P9K_CONTENT/⇣* :⇡/⇣⇡}// }//:/ }'
|
||||
|
||||
# Grey current time.
|
||||
typeset -g POWERLEVEL9K_TIME_FOREGROUND=$grey
|
||||
# Format for the current time: 09:51:02. See `man 3 strftime`.
|
||||
typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
|
||||
# If set to true, time will update when you hit enter. This way prompts for the past
|
||||
# commands will contain the start times of their commands rather than the end times of
|
||||
# their preceding commands.
|
||||
typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
|
||||
|
||||
# Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt
|
||||
# when accepting a command line. Supported values:
|
||||
#
|
||||
# - off: Don't change prompt when accepting a command line.
|
||||
# - always: Trim down prompt when accepting a command line.
|
||||
# - same-dir: Trim down prompt when accepting a command line unless this is the first command
|
||||
# typed after changing current working directory.
|
||||
typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=off
|
||||
|
||||
# Instant prompt mode.
|
||||
#
|
||||
# - off: Disable instant prompt. Choose this if you've tried instant prompt and found
|
||||
# it incompatible with your zsh configuration files.
|
||||
# - quiet: Enable instant prompt and don't print warnings when detecting console output
|
||||
# during zsh initialization. Choose this if you've read and understood
|
||||
# https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt.
|
||||
# - verbose: Enable instant prompt and print a warning when detecting console output during
|
||||
# zsh initialization. Choose this if you've never tried instant prompt, haven't
|
||||
# seen the warning, or if you are unsure what this all means.
|
||||
typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose
|
||||
|
||||
# Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized.
|
||||
# For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload
|
||||
# can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you
|
||||
# really need it.
|
||||
typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true
|
||||
|
||||
# If p10k is already loaded, reload configuration.
|
||||
# This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true.
|
||||
(( ! $+functions[p10k] )) || p10k reload
|
||||
}
|
||||
|
||||
# Tell `p10k configure` which file it should overwrite.
|
||||
typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a}
|
||||
|
||||
(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
|
||||
'builtin' 'unset' 'p10k_config_opts'
|
1722
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-rainbow.zsh
Normal file
1722
zsh/.oh-my-zsh/custom/themes/powerlevel10k/config/p10k-rainbow.zsh
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,111 @@
|
||||
# Config file for Powerlevel10k with the style of robbyrussell theme from Oh My Zsh.
|
||||
#
|
||||
# Original: https://github.com/ohmyzsh/ohmyzsh/wiki/Themes#robbyrussell.
|
||||
#
|
||||
# Replication of robbyrussell theme is exact. The only observable difference is in
|
||||
# performance. Powerlevel10k prompt is very fast everywhere, even in large Git repositories.
|
||||
#
|
||||
# Usage: Source this file either before or after loading Powerlevel10k.
|
||||
#
|
||||
# source ~/powerlevel10k/config/p10k-robbyrussell.zsh
|
||||
# source ~/powerlevel10k/powerlevel10k.zsh-theme
|
||||
|
||||
# Temporarily change options.
|
||||
'builtin' 'local' '-a' 'p10k_config_opts'
|
||||
[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases')
|
||||
[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob')
|
||||
[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
|
||||
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
|
||||
|
||||
() {
|
||||
emulate -L zsh -o extended_glob
|
||||
|
||||
# Unset all configuration options.
|
||||
unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR'
|
||||
|
||||
# Zsh >= 5.1 is required.
|
||||
autoload -Uz is-at-least && is-at-least 5.1 || return
|
||||
|
||||
# Left prompt segments.
|
||||
typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(prompt_char dir vcs)
|
||||
# Right prompt segments.
|
||||
typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=()
|
||||
|
||||
# Basic style options that define the overall prompt look.
|
||||
typeset -g POWERLEVEL9K_BACKGROUND= # transparent background
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol
|
||||
typeset -g POWERLEVEL9K_VISUAL_IDENTIFIER_EXPANSION= # no segment icons
|
||||
|
||||
# Green prompt symbol if the last command succeeded.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS}_FOREGROUND=green
|
||||
# Red prompt symbol if the last command failed.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS}_FOREGROUND=red
|
||||
# Prompt symbol: bold arrow.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_CONTENT_EXPANSION='%B➜ '
|
||||
|
||||
# Cyan current directory.
|
||||
typeset -g POWERLEVEL9K_DIR_FOREGROUND=cyan
|
||||
# Show only the last segment of the current directory.
|
||||
typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_last
|
||||
# Bold directory.
|
||||
typeset -g POWERLEVEL9K_DIR_CONTENT_EXPANSION='%B$P9K_CONTENT'
|
||||
|
||||
# Git status formatter.
|
||||
function my_git_formatter() {
|
||||
emulate -L zsh
|
||||
if [[ -n $P9K_CONTENT ]]; then
|
||||
# If P9K_CONTENT is not empty, it's either "loading" or from vcs_info (not from
|
||||
# gitstatus plugin). VCS_STATUS_* parameters are not available in this case.
|
||||
typeset -g my_git_format=$P9K_CONTENT
|
||||
else
|
||||
# Use VCS_STATUS_* parameters to assemble Git status. See reference:
|
||||
# https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh.
|
||||
typeset -g my_git_format="${1+%B%4F}git:(${1+%1F}"
|
||||
my_git_format+=${${VCS_STATUS_LOCAL_BRANCH:-${VCS_STATUS_COMMIT[1,8]}}//\%/%%}
|
||||
my_git_format+="${1+%4F})"
|
||||
if (( VCS_STATUS_NUM_CONFLICTED || VCS_STATUS_NUM_STAGED ||
|
||||
VCS_STATUS_NUM_UNSTAGED || VCS_STATUS_NUM_UNTRACKED )); then
|
||||
my_git_format+=" ${1+%3F}✗"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
functions -M my_git_formatter 2>/dev/null
|
||||
|
||||
# Disable the default Git status formatting.
|
||||
typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true
|
||||
# Install our own Git status formatter.
|
||||
typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}'
|
||||
typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter()))+${my_git_format}}'
|
||||
# Grey Git status when loading.
|
||||
typeset -g POWERLEVEL9K_VCS_LOADING_FOREGROUND=246
|
||||
|
||||
# Instant prompt mode.
|
||||
#
|
||||
# - off: Disable instant prompt. Choose this if you've tried instant prompt and found
|
||||
# it incompatible with your zsh configuration files.
|
||||
# - quiet: Enable instant prompt and don't print warnings when detecting console output
|
||||
# during zsh initialization. Choose this if you've read and understood
|
||||
# https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt.
|
||||
# - verbose: Enable instant prompt and print a warning when detecting console output during
|
||||
# zsh initialization. Choose this if you've never tried instant prompt, haven't
|
||||
# seen the warning, or if you are unsure what this all means.
|
||||
typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose
|
||||
|
||||
# Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized.
|
||||
# For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload
|
||||
# can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you
|
||||
# really need it.
|
||||
typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true
|
||||
|
||||
# If p10k is already loaded, reload configuration.
|
||||
# This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true.
|
||||
(( ! $+functions[p10k] )) || p10k reload
|
||||
}
|
||||
|
||||
# Tell `p10k configure` which file it should overwrite.
|
||||
typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a}
|
||||
|
||||
(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
|
||||
'builtin' 'unset' 'p10k_config_opts'
|
116
zsh/.oh-my-zsh/custom/themes/powerlevel10k/font.md
Normal file
116
zsh/.oh-my-zsh/custom/themes/powerlevel10k/font.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Recommended font: Meslo Nerd Font patched for Powerlevel10k
|
||||
|
||||
Gorgeous monospace font designed by Jim Lyles for Bitstream, customized by the same for Apple,
|
||||
further customized by André Berg, and finally patched by yours truly with customized scripts
|
||||
originally developed by Ryan L McIntyre of Nerd Fonts. Contains all glyphs and symbols that
|
||||
Powerlevel10k may need. Battle-tested in dozens of different terminals on all major operating
|
||||
systems.
|
||||
|
||||
*FAQ*: [How was the recommended font created?](README.md#how-was-the-recommended-font-created)
|
||||
|
||||
## Automatic font installation
|
||||
|
||||
If you are using iTerm2 or Termux, `p10k configure` can install the recommended font for you.
|
||||
Simply answer `Yes` when asked whether to install *Meslo Nerd Font*.
|
||||
|
||||
If you are using a different terminal, proceed with manual font installation. 👇
|
||||
|
||||
## Manual font installation
|
||||
|
||||
1. Download these four ttf files:
|
||||
- [MesloLGS NF Regular.ttf](
|
||||
https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Regular.ttf)
|
||||
- [MesloLGS NF Bold.ttf](
|
||||
https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold.ttf)
|
||||
- [MesloLGS NF Italic.ttf](
|
||||
https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Italic.ttf)
|
||||
- [MesloLGS NF Bold Italic.ttf](
|
||||
https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold%20Italic.ttf)
|
||||
1. Double-click on each file and click "Install". This will make `MesloLGS NF` font available to all
|
||||
applications on your system.
|
||||
1. Configure your terminal to use this font:
|
||||
- **iTerm2**: Type `p10k configure` and answer `Yes` when asked whether to install
|
||||
*Meslo Nerd Font*. Alternatively, open *iTerm2 → Preferences → Profiles → Text* and set *Font* to
|
||||
`MesloLGS NF`.
|
||||
- **Apple Terminal**: Open *Terminal → Preferences → Profiles → Text*, click *Change* under *Font*
|
||||
and select `MesloLGS NF` family.
|
||||
- **Hyper**: Open *Hyper → Edit → Preferences* and change the value of `fontFamily` under
|
||||
`module.exports.config` to `MesloLGS NF`.
|
||||
- **Visual Studio Code**: Open *File → Preferences → Settings* (PC) or
|
||||
*Code → Preferences → Settings* (Mac), enter `terminal.integrated.fontFamily` in the search box at
|
||||
the top of *Settings* tab and set the value below to `MesloLGS NF`.
|
||||
Consult [this screenshot](
|
||||
https://raw.githubusercontent.com/romkatv/powerlevel10k-media/389133fb8c9a2347929a23702ce3039aacc46c3d/visual-studio-code-font-settings.jpg)
|
||||
to see how it should look like or see [this issue](
|
||||
https://github.com/romkatv/powerlevel10k/issues/671) for extra information.
|
||||
- **GNOME Terminal** (the default Ubuntu terminal): Open *Terminal → Preferences* and click on the
|
||||
selected profile under *Profiles*. Check *Custom font* under *Text Appearance* and select
|
||||
`MesloLGS NF Regular`.
|
||||
- **Konsole**: Open *Settings → Edit Current Profile → Appearance*, click *Select Font* and select
|
||||
`MesloLGS NF Regular`.
|
||||
- **Tilix**: Open *Tilix → Preferences* and click on the selected profile under *Profiles*. Check
|
||||
*Custom font* under *Text Appearance* and select `MesloLGS NF Regular`.
|
||||
- **Windows Console Host** (the old thing): Click the icon in the top left corner, then
|
||||
*Properties → Font* and set *Font* to `MesloLGS NF`.
|
||||
- **Windows Terminal** by Microsoft (the new thing): Open `settings.json` (<kbd>Ctrl+Shift+,</kbd>),
|
||||
search for `fontFace` and set the value to `MesloLGS NF` for every profile. If you don't find
|
||||
`fontFace`, add it under *profiles → defaults*. See [this settings file](
|
||||
https://raw.githubusercontent.com/romkatv/dotfiles-public/aba0e6c4657d705ed6c344d700d659977385f25c/dotfiles/microsoft-terminal-settings.json)
|
||||
for example.
|
||||
- **IntelliJ** (and other IDEs by Jet Brains): Open *IDE → Edit → Preferences → Editor →
|
||||
Color Scheme → Console Font*. Select *Use console font instead of the default* and set the font
|
||||
name to `MesloLGS NF`.
|
||||
- **Termux**: Type `p10k configure` and answer `Yes` when asked whether to install
|
||||
*Meslo Nerd Font*.
|
||||
- **Blink**: Type `config`, go to *Appearance*, tap *Add a new font*, tap *Open Gallery*, select
|
||||
*MesloLGS NF.css*, tap *import* and type `exit` in the home view to reload the font.
|
||||
- **Terminus**: Open *Settings → Appearance* and set *Font* to `MesloLGS NF`.
|
||||
- **Terminator**: Open *Preferences* using the context menu. Under *Profiles* select the *General*
|
||||
tab (should be selected already), uncheck *Use the system fixed width font* (if not already)
|
||||
and select `MesloLGS NF Regular`. Exit the Preferences dialog by clicking *Close*.
|
||||
- **Guake**: Right Click on an open terminal and open *Preferences*. Under *Appearance*
|
||||
tab, uncheck *Use the system fixed width font* (if not already) and select `MesloLGS NF Regular`.
|
||||
Exit the Preferences dialog by clicking *Close*.
|
||||
- **MobaXterm**: Open *Settings* → *Configuration* → *Terminal* → (under *Terminal look and feel*)
|
||||
and change *Font* to `MesloLGS NF`.
|
||||
- **Asbrú Connection Manager**: Open *Preferences → Local Shell Options → Look and Feel*, enable
|
||||
*Use these personal options* and change *Font:* under *Terminal UI* to `MesloLGS NF Regular`.
|
||||
To change the font for the remote host connections, go to *Preferences → Terminal Options →
|
||||
Look and Feel* and change *Font:* under *Terminal UI* to `MesloLGS NF Regular`.
|
||||
- **WSLtty**: Right click on an open terminal and then on *Options*. In the *Text* section, under
|
||||
*Font*, click *"Select..."* and set Font to `MesloLGS NF Regular`.
|
||||
- **Yakuake**: Click *≡* → *Manage Profiles* → *New* → *Appearance*. Click *Choose* next to the
|
||||
*Font* dropdown, select `MesloLGS NF` and click *OK*. Click *OK* to save the profile. Select the
|
||||
new profile and click *Set as Default*.
|
||||
- **Alacritty**: Create or open `~/.config/alacritty/alacritty.yml` and add the following section
|
||||
to it:
|
||||
```yaml
|
||||
font:
|
||||
normal:
|
||||
family: "MesloLGS NF"
|
||||
```
|
||||
- **Kitty**: Create or open `~/.config/kitty/kitty.conf` and add the following line to it:
|
||||
```text
|
||||
font_family MesloLGS NF
|
||||
```
|
||||
Restart Kitty by closing all sessions and opening a new session.
|
||||
- **WezTerm**: Create or open `$HOME/.config/wezterm/wezterm.lua` and add the following:
|
||||
```lua
|
||||
local wezterm = require 'wezterm';
|
||||
return {
|
||||
font = wezterm.font("MesloLGS NF"),
|
||||
}
|
||||
```
|
||||
If the file already exists, only add the line with the font to the existing return.
|
||||
Also add the first line if it is not already present.
|
||||
- **urxvt**: Create or open `~/.Xresources` and add the following line to it:
|
||||
```text
|
||||
URxvt.font: xft:MesloLGS NF:size=11
|
||||
```
|
||||
You can adjust the font size to your preference. After changing the configuration use `xrdb ~/.Xresources` to reload the config.
|
||||
The new config is applied for all new terminals.
|
||||
1. Run `p10k configure` to generate a new `~/.p10k.zsh`. The old config may work
|
||||
incorrectly with the new font.
|
||||
|
||||
_Using a different terminal and know how to set the font for it? Share your knowledge by sending a
|
||||
PR to expand the list!_
|
@ -0,0 +1,4 @@
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 100
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
16
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.gitattributes
vendored
Normal file
16
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.gitattributes
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
* text=auto
|
||||
|
||||
*.cc text eol=lf
|
||||
*.h text eol=lf
|
||||
*.info text eol=lf
|
||||
*.json text eol=lf
|
||||
*.md text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.zsh text eol=lf
|
||||
|
||||
/.clang-format text eol=lf
|
||||
/LICENSE text eol=lf
|
||||
/Makefile text eol=lf
|
||||
/build text eol=lf
|
||||
/install text eol=lf
|
||||
/mbuild text eol=lf
|
8
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.gitignore
vendored
Normal file
8
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
*.zwc
|
||||
/core
|
||||
/deps/libgit2-*.tar.gz
|
||||
/locks
|
||||
/logs
|
||||
/obj
|
||||
/usrbin/gitstatusd*
|
||||
/.vscode/ipch
|
17
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.vscode/c_cpp_properties.json
vendored
Normal file
17
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/src"
|
||||
],
|
||||
"defines": [
|
||||
],
|
||||
"compilerPath": "/usr/bin/g++",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
72
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.vscode/settings.json
vendored
Normal file
72
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"*.zwc": true,
|
||||
"core": true,
|
||||
"locks/": true,
|
||||
"logs/": true,
|
||||
"obj/": true,
|
||||
"usrbin/": true,
|
||||
},
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"complex": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"fstream": "cpp",
|
||||
"functional": "cpp",
|
||||
"future": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ratio": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"thread": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"tuple": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"utility": "cpp",
|
||||
"variant": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"charconv": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"iterator": "cpp",
|
||||
"map": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"random": "cpp",
|
||||
"string": "cpp",
|
||||
"bit": "cpp",
|
||||
"netfwd": "cpp"
|
||||
}
|
||||
}
|
674
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/LICENSE
Normal file
674
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
@ -0,0 +1,46 @@
|
||||
APPNAME ?= gitstatusd
|
||||
OBJDIR ?= obj
|
||||
|
||||
CXX ?= g++
|
||||
ZSH := $(shell command -v zsh 2> /dev/null)
|
||||
|
||||
VERSION ?= $(shell . ./build.info && printf "%s" "$$gitstatus_version")
|
||||
|
||||
# Note: -fsized-deallocation is not used to avoid binary compatibility issues on macOS.
|
||||
#
|
||||
# Sized delete is implemented as __ZdlPvm in /usr/lib/libc++.1.dylib but this symbol is
|
||||
# missing in macOS prior to 10.13.
|
||||
CXXFLAGS += -std=c++14 -funsigned-char -O3 -DNDEBUG -DGITSTATUS_VERSION=$(VERSION) -Wall -Werror # -g -fsanitize=thread
|
||||
LDFLAGS += -pthread # -fsanitize=thread
|
||||
LDLIBS += -lgit2 # -lprofiler -lunwind
|
||||
|
||||
SRCS := $(shell find src -name "*.cc")
|
||||
OBJS := $(patsubst src/%.cc, $(OBJDIR)/%.o, $(SRCS))
|
||||
|
||||
all: $(APPNAME)
|
||||
|
||||
$(APPNAME): usrbin/$(APPNAME)
|
||||
|
||||
usrbin/$(APPNAME): $(OBJS)
|
||||
$(CXX) $(OBJS) $(LDFLAGS) $(LDLIBS) -o $@
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p -- $(OBJDIR)
|
||||
|
||||
$(OBJDIR)/%.o: src/%.cc Makefile build.info | $(OBJDIR)
|
||||
$(CXX) $(CXXFLAGS) -MM -MT $@ src/$*.cc >$(OBJDIR)/$*.dep
|
||||
$(CXX) $(CXXFLAGS) -Wall -c -o $@ src/$*.cc
|
||||
|
||||
clean:
|
||||
rm -rf -- $(OBJDIR)
|
||||
|
||||
zwc:
|
||||
$(or $(ZSH),:) -fc 'for f in *.zsh install; do zcompile -R -- $$f.zwc $$f || exit; done'
|
||||
|
||||
minify:
|
||||
rm -rf -- .clang-format .git .gitattributes .gitignore .vscode deps docs src usrbin/.gitkeep LICENSE Makefile README.md build mbuild
|
||||
|
||||
pkg: zwc
|
||||
GITSTATUS_DAEMON= GITSTATUS_CACHE_DIR=$(shell pwd)/usrbin ./install -f
|
||||
|
||||
-include $(OBJS:.o=.dep)
|
529
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/README.md
Normal file
529
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/README.md
Normal file
@ -0,0 +1,529 @@
|
||||
# gitstatus
|
||||
|
||||
**gitstatus** is a 10x faster alternative to `git status` and `git describe`. Its primary use
|
||||
case is to enable fast git prompt in interactive shells.
|
||||
|
||||
Heavy lifting is done by **gitstatusd** -- a custom binary written in C++. It comes with Zsh and
|
||||
Bash bindings for integration with shell.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Using from Zsh](#using-from-zsh)
|
||||
1. [Using from Bash](#using-from-bash)
|
||||
2. [Using from other shells](#using-from-other-shells)
|
||||
1. [How it works](#how-it-works)
|
||||
1. [Benchmarks](#benchmarks)
|
||||
1. [Why fast](#why-fast)
|
||||
1. [Requirements](#requirements)
|
||||
1. [Compiling](#compiling)
|
||||
1. [License](#license)
|
||||
|
||||
## Using from Zsh
|
||||
|
||||
The easiest way to take advantage of gitstatus from Zsh is to use a theme that's already integrated
|
||||
with it. For example, [Powerlevel10k](https://github.com/romkatv/powerlevel10k) is a flexible and
|
||||
fast theme with first-class gitstatus integration.
|
||||
|
||||

|
||||
|
||||
For those who wish to use gitstatus without a theme, there is
|
||||
[gitstatus.prompt.zsh](gitstatus.prompt.zsh). Install it as follows:
|
||||
|
||||
```zsh
|
||||
git clone --depth=1 https://github.com/romkatv/gitstatus.git ~/gitstatus
|
||||
echo 'source ~/gitstatus/gitstatus.prompt.zsh' >>! ~/.zshrc
|
||||
```
|
||||
|
||||
Users in mainland China can use the official mirror on gitee.com for faster download.<br>
|
||||
中国大陆用户可以使用 gitee.com 上的官方镜像加速下载.
|
||||
|
||||
```zsh
|
||||
git clone --depth=1 https://gitee.com/romkatv/gitstatus.git ~/gitstatus
|
||||
echo 'source ~/gitstatus/gitstatus.prompt.zsh' >>! ~/.zshrc
|
||||
```
|
||||
|
||||
Alternatively, if you have Homebrew installed:
|
||||
|
||||
```zsh
|
||||
brew install romkatv/gitstatus/gitstatus
|
||||
echo "source $(brew --prefix)/opt/gitstatus/gitstatus.prompt.zsh" >>! ~/.zshrc
|
||||
```
|
||||
|
||||
(If you choose this option, replace `~/gitstatus` with `$(brew --prefix)/opt/gitstatus/gitstatus`
|
||||
in all code snippets below.)
|
||||
|
||||
_Make sure to disable your current theme if you have one._
|
||||
|
||||
This will give you a basic yet functional prompt with git status in it. It's
|
||||
[over 10x faster](#benchmarks) than any alternative that can give you comparable prompt. In order
|
||||
to customize it, set `PROMPT` and/or `RPROMPT` at the end of `~/.zshrc` after sourcing
|
||||
`gitstatus.prompt.zsh`. Insert `${GITSTATUS_PROMPT}` where you want git status to go. For example:
|
||||
|
||||
```zsh
|
||||
source ~/gitstatus/gitstatus.prompt.zsh
|
||||
|
||||
PROMPT='%~%# ' # left prompt: directory followed by %/# (normal/root)
|
||||
RPROMPT='$GITSTATUS_PROMPT' # right prompt: git status
|
||||
```
|
||||
|
||||
The expansion of `${GITSTATUS_PROMPT}` can contain the following bits:
|
||||
|
||||
| segment | meaning |
|
||||
|-------------|-------------------------------------------------------|
|
||||
| `master` | current branch |
|
||||
| `#v1` | HEAD is tagged with `v1`; not shown when on a branch |
|
||||
| `@5fc6fca4` | current commit; not shown when on a branch or tag |
|
||||
| `⇣1` | local branch is behind the remote by 1 commit |
|
||||
| `⇡2` | local branch is ahead of the remote by 2 commits |
|
||||
| `⇠3` | local branch is behind the push remote by 3 commits |
|
||||
| `⇢4` | local branch is ahead of the push remote by 4 commits |
|
||||
| `*5` | there are 5 stashes |
|
||||
| `merge` | merge is in progress (could be some other action) |
|
||||
| `~6` | there are 6 merge conflicts |
|
||||
| `+7` | there are 7 staged changes |
|
||||
| `!8` | there are 8 unstaged changes |
|
||||
| `?9` | there are 9 untracked files |
|
||||
|
||||
`$GITSTATUS_PROMPT_LEN` tells you how long `$GITSTATUS_PROMPT` is when printed to the console.
|
||||
[gitstatus.prompt.zsh](gitstatus.prompt.zsh) has an example of using it to truncate the current
|
||||
directory.
|
||||
|
||||
If you'd like to change the format of git status, or want to have greater control over the
|
||||
process of assembling `PROMPT`, you can copy and modify parts of
|
||||
[gitstatus.prompt.zsh](gitstatus.prompt.zsh) instead of sourcing the script. Your `~/.zshrc`
|
||||
might look something like this:
|
||||
|
||||
```zsh
|
||||
source ~/gitstatus/gitstatus.plugin.zsh
|
||||
|
||||
function my_set_prompt() {
|
||||
PROMPT='%~%# '
|
||||
RPROMPT=''
|
||||
|
||||
if gitstatus_query MY && [[ $VCS_STATUS_RESULT == ok-sync ]]; then
|
||||
RPROMPT=${${VCS_STATUS_LOCAL_BRANCH:-@${VCS_STATUS_COMMIT}}//\%/%%} # escape %
|
||||
(( VCS_STATUS_NUM_STAGED )) && RPROMPT+='+'
|
||||
(( VCS_STATUS_NUM_UNSTAGED )) && RPROMPT+='!'
|
||||
(( VCS_STATUS_NUM_UNTRACKED )) && RPROMPT+='?'
|
||||
fi
|
||||
|
||||
setopt no_prompt_{bang,subst} prompt_percent # enable/disable correct prompt expansions
|
||||
}
|
||||
|
||||
gitstatus_stop 'MY' && gitstatus_start -s -1 -u -1 -c -1 -d -1 'MY'
|
||||
autoload -Uz add-zsh-hook
|
||||
add-zsh-hook precmd my_set_prompt
|
||||
```
|
||||
|
||||
This snippet is sourcing `gitstatus.plugin.zsh` rather than `gitstatus.prompt.zsh`. The former
|
||||
defines low-level bindings that communicate with gitstatusd over pipes. The latter is a simple
|
||||
script that uses these bindings to assemble git prompt.
|
||||
|
||||
Unlike [Powerlevel10k](https://github.com/romkatv/powerlevel10k), code based on
|
||||
[gitstatus.prompt.zsh](gitstatus.prompt.zsh) is communicating with gitstatusd synchronously. This
|
||||
can make your prompt slow when working in a large git repository or on a slow machine. To avoid
|
||||
this problem, call `gitstatus_query` asynchronously as documented in
|
||||
[gitstatus.plugin.zsh](gitstatus.plugin.zsh). This can be quite challenging.
|
||||
|
||||
## Using from Bash
|
||||
|
||||
The easiest way to take advantage of gitstatus from Bash is via
|
||||
[gitstatus.prompt.sh](gitstatus.prompt.sh). Install it as follows:
|
||||
|
||||
```bash
|
||||
git clone --depth=1 https://github.com/romkatv/gitstatus.git ~/gitstatus
|
||||
echo 'source ~/gitstatus/gitstatus.prompt.sh' >> ~/.bashrc
|
||||
```
|
||||
|
||||
Users in mainland China can use the official mirror on gitee.com for faster download.<br>
|
||||
中国大陆用户可以使用 gitee.com 上的官方镜像加速下载.
|
||||
|
||||
```bash
|
||||
git clone --depth=1 https://gitee.com/romkatv/gitstatus.git ~/gitstatus
|
||||
echo 'source ~/gitstatus/gitstatus.prompt.sh' >> ~/.bashrc
|
||||
```
|
||||
|
||||
Alternatively, if you have Homebrew installed:
|
||||
|
||||
```zsh
|
||||
brew install romkatv/gitstatus/gitstatus
|
||||
echo "source $(brew --prefix)/opt/gitstatus/gitstatus.prompt.sh" >> ~/.bashrc
|
||||
```
|
||||
|
||||
(If you choose this option, replace `~/gitstatus` with `$(brew --prefix)/opt/gitstatus/gitstatus`
|
||||
in all code snippets below.)
|
||||
|
||||
This will give you a basic yet functional prompt with git status in it. It's
|
||||
[over 10x faster](#benchmarks) than any alternative that can give you comparable prompt.
|
||||
|
||||

|
||||
|
||||
In order to customize your prompt, set `PS1` at the end of `~/.bashrc` after sourcing
|
||||
`gitstatus.prompt.sh`. Insert `${GITSTATUS_PROMPT}` where you want git status to go. For example:
|
||||
|
||||
```bash
|
||||
source ~/gitstatus/gitstatus.prompt.sh
|
||||
|
||||
PS1='\w ${GITSTATUS_PROMPT}\n\$ ' # directory followed by git status and $/# (normal/root)
|
||||
```
|
||||
|
||||
The expansion of `${GITSTATUS_PROMPT}` can contain the following bits:
|
||||
|
||||
| segment | meaning |
|
||||
|-------------|-------------------------------------------------------|
|
||||
| `master` | current branch |
|
||||
| `#v1` | HEAD is tagged with `v1`; not shown when on a branch |
|
||||
| `@5fc6fca4` | current commit; not shown when on a branch or tag |
|
||||
| `⇣1` | local branch is behind the remote by 1 commit |
|
||||
| `⇡2` | local branch is ahead of the remote by 2 commits |
|
||||
| `⇠3` | local branch is behind the push remote by 3 commits |
|
||||
| `⇢4` | local branch is ahead of the push remote by 4 commits |
|
||||
| `*5` | there are 5 stashes |
|
||||
| `merge` | merge is in progress (could be some other action) |
|
||||
| `~6` | there are 6 merge conflicts |
|
||||
| `+7` | there are 7 staged changes |
|
||||
| `!8` | there are 8 unstaged changes |
|
||||
| `?9` | there are 9 untracked files |
|
||||
|
||||
If you'd like to change the format of git status, or want to have greater control over the
|
||||
process of assembling `PS1`, you can copy and modify parts of
|
||||
[gitstatus.prompt.sh](gitstatus.prompt.sh) instead of sourcing the script. Your `~/.bashrc` might
|
||||
look something like this:
|
||||
|
||||
```bash
|
||||
source ~/gitstatus/gitstatus.plugin.sh
|
||||
|
||||
function my_set_prompt() {
|
||||
PS1='\w'
|
||||
|
||||
if gitstatus_query && [[ "$VCS_STATUS_RESULT" == ok-sync ]]; then
|
||||
if [[ -n "$VCS_STATUS_LOCAL_BRANCH" ]]; then
|
||||
PS1+=" ${VCS_STATUS_LOCAL_BRANCH//\\/\\\\}" # escape backslash
|
||||
else
|
||||
PS1+=" @${VCS_STATUS_COMMIT//\\/\\\\}" # escape backslash
|
||||
fi
|
||||
(( VCS_STATUS_HAS_STAGED" )) && PS1+='+'
|
||||
(( VCS_STATUS_HAS_UNSTAGED" )) && PS1+='!'
|
||||
(( VCS_STATUS_HAS_UNTRACKED" )) && PS1+='?'
|
||||
fi
|
||||
|
||||
PS1+='\n\$ '
|
||||
|
||||
shopt -u promptvars # disable expansion of '$(...)' and the like
|
||||
}
|
||||
|
||||
gitstatus_stop && gitstatus_start
|
||||
PROMPT_COMMAND=my_set_prompt
|
||||
```
|
||||
|
||||
This snippet is sourcing `gitstatus.plugin.sh` rather than `gitstatus.prompt.sh`. The former
|
||||
defines low-level bindings that communicate with gitstatusd over pipes. The latter is a simple
|
||||
script that uses these bindings to assemble git prompt.
|
||||
|
||||
Note: Bash bindings, unlike Zsh bindings, don't support asynchronous calls.
|
||||
|
||||
## Using from other shells
|
||||
|
||||
If there are no gitstatusd bindings for your shell, you'll need to get your hands dirty.
|
||||
Use the existing bindings for inspiration; run `gitstatusd --help` or read the same thing in
|
||||
[options.cc](src/options.cc).
|
||||
|
||||
## How it works
|
||||
|
||||
gitstatusd reads requests from stdin and prints responses to stdout. Requests contain an ID and
|
||||
a directory. Responses contain the same ID and machine-readable git status for the directory.
|
||||
gitstatusd keeps some state in memory for the directories it has seen in order to serve future
|
||||
requests faster.
|
||||
|
||||
[Zsh bindings](gitstatus.plugin.zsh) and [Bash bindings](gitstatus.plugin.sh) start gitstatusd in
|
||||
the background and communicate with it via pipes. Themes such as
|
||||
[Powerlevel10k](https://github.com/romkatv/powerlevel10k) use these bindings to put git status in
|
||||
`PROMPT`.
|
||||
|
||||
Note that gitstatus cannot be used as a drop-in replacement for `git status` command as it doesn't
|
||||
produce output in the same format. It does perform the same computation though.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
The following benchmark results were obtained on Intel i9-7900X running Ubuntu 18.04 in
|
||||
a clean [chromium](https://github.com/chromium/chromium) repository synced to `9394e49a`. The
|
||||
repository was checked out to an ext4 filesystem on M.2 SSD.
|
||||
|
||||
Three functionally equivalent tools for computing git status were benchmarked:
|
||||
|
||||
* `gitstatusd`
|
||||
* `git` with untracked cache enabled
|
||||
* `lg2` -- a demo/example executable from [libgit2](https://github.com/romkatv/libgit2) that
|
||||
implements a subset of `git` functionality on top of libgit2 API; for the purposes of this
|
||||
benchmark the subset is sufficient to generate the same data as the other tools
|
||||
|
||||
Every tool was benchmark in cold and hot conditions. For `git` the first run in a repository was
|
||||
considered cold, with the following runs considered hot. `lg2` was patched to compute results twice
|
||||
in a single invocation without freeing the repository in between; the second run was considered hot.
|
||||
The same patching was not done for `git` because `git` cannot be easily modified to refresh inmemory
|
||||
index state between invocations; in fact, this limitation is one of the primary reasons developers
|
||||
use libgit2. `gitstatusd` was benchmarked similarly to `lg2` with two result computations in the
|
||||
same invocation.
|
||||
|
||||
Two commands were benchmarked: `status` and `describe`.
|
||||
|
||||
### Status
|
||||
|
||||
In this benchmark all tools were computing the equivalent of `git status`. Lower numbers are better.
|
||||
|
||||
| Tool | Cold | Hot |
|
||||
|---------------|-----------:|------------:|
|
||||
| **gitstatus** | **291 ms** | **30.9 ms** |
|
||||
| git | 876 ms | 295 ms |
|
||||
| lg2 | 1730 ms | 1310 ms |
|
||||
|
||||
gitstatusd is substantially faster than the alternatives, especially on hot runs. Note that hot runs
|
||||
are of primary importance to the main use case of gitstatus in interactive shells.
|
||||
|
||||
The performance of `git status` fluctuated wildly in this benchmarks for reasons unknown to the
|
||||
author. Moreover, performance is sticky -- once `git status` settles around a number, it stays
|
||||
there for a long time. Numbers as diverse as 295, 352, 663 and 730 had been observed on hot runs on
|
||||
the same repository. The number in the table is the lowest (fastest or best) that `git status` had
|
||||
shown.
|
||||
|
||||
### Describe
|
||||
|
||||
In this benchmark all tools were computing the equivalent of `git describe --tags --exact-match`
|
||||
to find tags that resolve to the same commit as `HEAD`. Lower numbers are better.
|
||||
|
||||
| Tool | Cold | Hot |
|
||||
|---------------|------------:|--------------:|
|
||||
| **gitstatus** | **4.04 ms** | **0.0345 ms** |
|
||||
| git | 18.0 ms | 14.5 ms |
|
||||
| lg2 | 185 ms | 45.2 ms |
|
||||
|
||||
gitstatusd is once again faster than the alternatives, more so on hot runs.
|
||||
|
||||
## Why fast
|
||||
|
||||
Since gitstatusd doesn't have to print all staged/unstaged/untracked files but only report
|
||||
whether there are any, it can terminate repository scan early. It can also remember which files
|
||||
were dirty on the previous run and check them first on the next run to avoid the scan entirely if
|
||||
the files are still dirty. However, the benchmarks above were performed in a clean repository where
|
||||
these shortcuts do not trigger. All benchmarked tools had to do the same work -- check the status
|
||||
of every file in the index to see if it has changed, check every directory for newly created files,
|
||||
etc. And yet, gitstatusd came ahead by a large margin. This section describes what it does that
|
||||
makes it so fast.
|
||||
|
||||
Most of the following comparisons are done against libgit2 rather than git because of the author's
|
||||
familiarity with the former but not the with latter. libgit2 has clean, well-documented APIs and an
|
||||
elegant implementation, which makes it so much easier to work with and to analyze performance
|
||||
bottlenecks.
|
||||
|
||||
### Summary for the impatient
|
||||
|
||||
Under the benchmark conditions described above, the equivalent of libgit2's
|
||||
`git_diff_index_to_workdir` (the most expensive part of `status` command) is 46.3 times faster in
|
||||
gitstatusd. The speedup comes from the following sources.
|
||||
|
||||
* gitstatusd uses more efficient data structures and algorithms and employs performance-conscious
|
||||
coding style throughout the codebase. This reduces CPU time in userspace by 32x compared to libgit2.
|
||||
* gitstatusd uses less expensive system calls and makes fewer of them. This reduces CPU time spent
|
||||
in kernel by 1.9x.
|
||||
* gitstatusd can utilize multiple cores to scan index and workdir in parallel with almost perfect
|
||||
scaling. This reduces total run time by 12.4x while having virtually no effect on total CPU time.
|
||||
|
||||
### Problem statement
|
||||
|
||||
The most resource-intensive part of the `status` command is finding the difference between _index_
|
||||
and _workdir_ (`git_diff_index_to_workdir` in libgit2). Index is a list of all files in the git
|
||||
repository with their last modification times. This is an obvious simplification but it suffices for
|
||||
this exposition. On disk, index is stored sorted by file path. Here's an example of git index:
|
||||
|
||||
| File | Last modification time |
|
||||
|-------------|-----------------------:|
|
||||
| Makefile | 2019-04-01T14:12:32Z |
|
||||
| src/hello.c | 2019-04-01T14:12:00Z |
|
||||
| src/hello.h | 2019-04-01T14:12:32Z |
|
||||
|
||||
This list needs to be compared to the list of files in the working directory. If any of the files
|
||||
listed in the index are missing from the workdir or have different last modification time, they are
|
||||
"unstaged" in gitstatusd parlance. If you run `git status`, they'll be shown as "changes not staged
|
||||
for commit". Thus, any implementation of `status` command has to call `stat()` or one of its
|
||||
variants on every file in the index.
|
||||
|
||||
In addition, all files in the working directory for which there is no entry in the index at all are
|
||||
"untracked". `git status` will show them as "untracked files". Finding untracked files requires some
|
||||
form of work directory traversal.
|
||||
|
||||
### Single-threaded scan
|
||||
|
||||
Let's see how `git_diff_index_to_workdir` from libgit2 accomplishes these tasks. Here's its CPU
|
||||
profile from 200 hot runs over chromium repository.
|
||||
|
||||

|
||||
|
||||
(The CPU profile was created with [gperftools](https://github.com/gperftools/gperftools) and
|
||||
rendered with [pprof](https://github.com/google/pprof)).
|
||||
|
||||
We can see `__GI__lxstat` taking a lot of time. This is the `stat()` call for every file in the
|
||||
index. We can also identify `__opendir`, `__readdir` and `__GI___close_nocancel` -- glibc wrappers
|
||||
for reading the contents of a directory. This is for finding untracked files. Out of the total 232
|
||||
seconds, 111 seconds -- or 47.7% -- was spent on these calls. The rest is computation -- comparing
|
||||
strings, sorting arrays, etc.
|
||||
|
||||
Now let's take a look at the CPU profile of gitstatusd on the same task.
|
||||
|
||||

|
||||
|
||||
The first impression is that this profile looks pruned. This isn't an artifact. The profile was
|
||||
generated with the same tools and the same flags as the profile of libgit2.
|
||||
|
||||
Since both profiles were generated from the same workload, absolute numbers can be compared. We can
|
||||
see that gitstatusd took 62 seconds in total compared to libgit2's 232 seconds. System calls at the
|
||||
core of the algorithm are cleary visible. `__GI___fxstatat` is a flavor of `stat()`, and the other
|
||||
three calls -- `__libc_openat64`, `__libc_close` and `__GI___fxstat` are responsible for opening
|
||||
directories and finding untracked files. Notice that there is almost nothing else in the profile
|
||||
apart from these calls. The rest of the code accounts for 3.77 seconds of CPU time -- 32 times less
|
||||
than in libgit2.
|
||||
|
||||
So, one reason gitstatusd is fast is that it has efficient diffing code -- very little time is spent
|
||||
outside of kernel. However, if we look closely, we can notice that system calls in gitstatusd are
|
||||
_also_ faster than in libgit2. For example, libgit2 spent 72.07 seconds in `__GI__lxstat` while
|
||||
gitstatusd spent only 48.82 seconds in `__GI___fxstatat`. There are two reasons for this difference.
|
||||
First, libgit2 makes more `stat()` calls than is strictly required. It's not necessary to stat
|
||||
directories because index only has files. There are 25k directories in chromium repository (and 300k
|
||||
files) -- that's 25k `stat()` calls that could be avoided. The second reason is that libgit2 and
|
||||
gitstatusd use different flavors of `stat()`. libgit2 uses `lstat()`, which takes a path to the file
|
||||
as input. Its performance is linear in the number of subdirectories in the path because it needs to
|
||||
perform a lookup for every one of them and to check permissions. gitstatusd uses `fstatat()`, which
|
||||
takes a file descriptor to the parent directory and a name of the file. Just a single lookup, less
|
||||
CPU time.
|
||||
|
||||
Similarly to `lstat()` vs `fstatat()`, it's faster to open files and directories with `openat()`
|
||||
from the parent directory file descriptor than with regular `open()` that accepts full file path.
|
||||
gitstatusd takes advantage of `openat()` to open directories as fast as possible. It opens about 90%
|
||||
of the directories (this depends on the actual directory structure of the repository) from the
|
||||
immediate parent -- the most efficient way -- and the remaining 10% it opens from the repository's
|
||||
root directory. The reason it's done this way is to keep the maximum number of simultaneously open
|
||||
file descriptors bounded. libgit2 can have O(repository depth) simultaneously open file descriptors,
|
||||
which may be OK for a single-threaded application but can balloon to a large number when scans are
|
||||
done by many threads simultaneously, like in gitstatusd.
|
||||
|
||||
There is no equivalent to `__opendir` or `__readdir` in the gitstatusd profile because it uses the
|
||||
equivalent of [untracked cache](https://git-scm.com/docs/git-update-index#_untracked_cache) from
|
||||
git. On the first scan of the workdir gitstatusd lists all files just like libgit2. But, unlike
|
||||
libgit2, it remembers the last modification time of every directory along with the list of
|
||||
untracked files under it. On the next scan, gitstatusd can skip listing files in directories whose
|
||||
last modification time hasn't changed.
|
||||
|
||||
To summarize, here's what gitstatusd was doing when the CPU profile was captured:
|
||||
|
||||
1. `__libc_openat64`: Open every directory for which there are files in the index.
|
||||
2. `__GI___fxstat`: Check last modification time of the directory. Since it's the same as on the
|
||||
last scan, this directory has the same list of untracked files as before, which is empty (the
|
||||
repository is clean).
|
||||
3. `__GI___fxstatat`: Check last modification time for every file in the index that belongs to this
|
||||
directory.
|
||||
4. `__libc_close`: Close the file descriptor to the directory.
|
||||
|
||||
Here's how the very first scan of a repository looks like in gitstatusd:
|
||||
|
||||

|
||||
|
||||
(Some glibc functions are mislabel on this profile. `explicit_bzero` and `__nss_passwd_lookup` are
|
||||
in reality `strcmp` and `memcmp`.)
|
||||
|
||||
This is a superset of the previous -- hot -- profile, with an extra `syscall` and string sorting for
|
||||
directory listing. gitstatusd uses `getdents64` Linux system call directly, bypassing the glibc
|
||||
wrapper that libgit2 uses. This is 23% faster. The details of this optimization can be found in a
|
||||
[separate document](docs/listdir.md).
|
||||
|
||||
### Multithreading
|
||||
|
||||
The diffing algorithm in gitstatusd was designed from the ground up with the intention of using it
|
||||
concurrently from multiple threads. With a fast SSD, `status` is CPU bound, so taking advantage of
|
||||
all available CPU cores is an obvious way to yield results faster.
|
||||
|
||||
gitstatusd exhibits almost perfect scaling from multithreading. Engaging all cores allows it to
|
||||
produce results 12.4 times faster than in single-threaded execution. This is on Intel i9-7900X with
|
||||
10 cores (20 with hyperthreading) with single-core frequency of 4.3GHz and all-core frequency of
|
||||
4.0GHz.
|
||||
|
||||
Note: `git status` also uses all available cores in some parts of its algorithm while `lg2` does
|
||||
everything in a single thread.
|
||||
|
||||
### Postprocessing
|
||||
|
||||
Once the difference between the index and the workdir is found, we have a list of _candidates_ --
|
||||
files that may be unstaged or untracked. To make the final judgement, these files need to be checked
|
||||
against `.gitignore` rules and a few other things.
|
||||
|
||||
gitstatusd uses [patched libgit2](https://github.com/romkatv/libgit2) for this step. This fork
|
||||
adds several optimizations that make libgit2 faster. The patched libgit2 performs more than twice
|
||||
as fast in the benchmark as the original even without changes in the user code (that is, in the
|
||||
code that uses the libgit2 APIs). The fork also adds several API extensions, most notable of which
|
||||
is the support for multi-threaded scans. If `lg2 status` is modified to take advantage of these
|
||||
extensions, it outperforms the original libgit2 by a factor of 18. Lastly, the fork fixes a score of
|
||||
bugs, most of which become apparent only when using libgit2 from multiple threads.
|
||||
|
||||
_WARNING: Changes to libgit2 are extensive but the testing they underwent isn't. It is
|
||||
**not recommended** to use the patched libgit2 in production._
|
||||
|
||||
## Requirements
|
||||
|
||||
* To compile: binutils, cmake, gcc, g++, git and GNU make.
|
||||
* To run: Linux, macOS, FreeBSD, Android, WSL, Cygwin or MSYS2.
|
||||
|
||||
## Compiling
|
||||
|
||||
There are prebuilt `gitstatusd` binaries in [releases](
|
||||
https://github.com/romkatv/gitstatus/releases). When using the official shell bindings
|
||||
provided by gitstatus, the right binary for your architecture gets downloaded automatically.
|
||||
|
||||
If prebuilt binaries don't work for you, you'll need to get your hands dirty.
|
||||
|
||||
### Compiling for personal use
|
||||
|
||||
```zsh
|
||||
git clone --depth=1 https://github.com/romkatv/gitstatus.git
|
||||
cd gitstatus
|
||||
./build -w -s -d docker
|
||||
```
|
||||
|
||||
Users in mainland China can use the official mirror on gitee.com for faster download.<br>
|
||||
中国大陆用户可以使用 gitee.com 上的官方镜像加速下载.
|
||||
|
||||
```zsh
|
||||
git clone --depth=1 https://gitee.com/romkatv/gitstatus.git
|
||||
cd gitstatus
|
||||
./build -w -s -d docker
|
||||
```
|
||||
|
||||
- If it says that `-d docker` is not supported on your OS, remove this flag.
|
||||
- If it says that `-s` is not supported on your OS, remove this flag.
|
||||
- If it tell you to install docker but you cannot or don't want to, remove `-d docker`.
|
||||
- If it says that some command is missing, install it.
|
||||
|
||||
If everything goes well, the newly built binary will appear in `./usrbin`. It'll be picked up
|
||||
by shell bindings automatically.
|
||||
|
||||
When you update shell bindings, they may refuse to work with the binary you've built earlier. In
|
||||
this case you'll need to rebuild.
|
||||
|
||||
If you are using gitstatus through [Powerlevel10k](https://github.com/romkatv/powerlevel10k), the
|
||||
instructions are the same except that you don't need to clone gitstatus. Instead, change your
|
||||
current directory to `/path/to/powerlevel10k/gitstatus` (`/path/to/powerlevel10k` is the directory
|
||||
where you've installed Powerlevel10k) and run `./build -w -s -d docker` from there as described
|
||||
above.
|
||||
|
||||
### Compiling for distribution
|
||||
|
||||
It's currently neither easy nor recommended to package and distribute gitstatus. There are no
|
||||
instructions you can follow that would allow you to easily update your package when new versions of
|
||||
gitstatus are released. This may change in the future but not soon.
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0. See [LICENSE](LICENSE). Contributions are covered by the same
|
||||
license.
|
623
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/build
Executable file
623
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/build
Executable file
@ -0,0 +1,623 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Type `build -h` for help and see https://github.com/romkatv/gitstatus
|
||||
# for full documentation.
|
||||
|
||||
set -ue
|
||||
|
||||
if [ -n "${ZSH_VERSION:-}" ]; then
|
||||
emulate sh -o err_exit -o no_unset
|
||||
fi
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
if [ -z "${ZSH_VERSION-}" ] && command -v zsh >/dev/null 2>&1; then
|
||||
case "${BASH_VERSION-}" in
|
||||
[0-3].*) exec zsh "$0" "$@";;
|
||||
esac
|
||||
fi
|
||||
|
||||
usage="$(command cat <<\END
|
||||
Usage: build [-m ARCH] [-c CPU] [-d CMD] [-i IMAGE] [-s] [-w]
|
||||
|
||||
Options:
|
||||
|
||||
-m ARCH `uname -m` from the target machine; defaults to `uname -m`
|
||||
from the local machine
|
||||
-c CPU generate machine instructions for CPU of this type; this
|
||||
value gets passed as `-march` (or `-mcpu` for ppc64le) to gcc;
|
||||
inferred from ARCH if not set explicitly
|
||||
-d CMD build in a Docker container and use CMD as the `docker`
|
||||
command; e.g., `-d docker` or `-d podman`
|
||||
-i IMAGE build in this Docker image; inferred from ARCH if not set
|
||||
explicitly
|
||||
-s install whatever software is necessary for build to
|
||||
succeed; on some operating systems this option is not
|
||||
supported; on others it can have partial effect
|
||||
-w automatically download tarballs for dependencies if they
|
||||
do not already exist in ./deps; dependencies are described
|
||||
in ./build.info
|
||||
END
|
||||
)"
|
||||
|
||||
build="$(command cat <<\END
|
||||
outdir="$(command pwd)"
|
||||
|
||||
if command -v mktemp >/dev/null 2>&1; then
|
||||
workdir="$(command mktemp -d "${TMPDIR:-/tmp}"/gitstatus-build.XXXXXXXXXX)"
|
||||
else
|
||||
workdir="${TMPDIR:-/tmp}/gitstatus-build.tmp.$$"
|
||||
command mkdir -- "$workdir"
|
||||
fi
|
||||
|
||||
cd -- "$workdir"
|
||||
workdir="$(command pwd)"
|
||||
|
||||
narg() { echo $#; }
|
||||
|
||||
if [ "$(narg $workdir)" != 1 -o -z "${workdir##*:*}" -o -z "${workdir##*=*}" ]; then
|
||||
>&2 echo "[error] cannot build in this directory: $workdir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
appname=gitstatusd
|
||||
libgit2_tmp="$outdir"/deps/"$appname".libgit2.tmp
|
||||
|
||||
cleanup() {
|
||||
trap - INT QUIT TERM ILL PIPE
|
||||
cd /
|
||||
if ! command rm -rf -- "$workdir" "$outdir"/usrbin/"$appname".tmp "$libgit2_tmp"; then
|
||||
command sleep 5
|
||||
command rm -rf -- "$workdir" "$outdir"/usrbin/"$appname".tmp "$libgit2_tmp"
|
||||
fi
|
||||
}
|
||||
trap cleanup INT QUIT TERM ILL PIPE
|
||||
|
||||
if [ -n "$gitstatus_install_tools" ]; then
|
||||
case "$gitstatus_kernel" in
|
||||
linux)
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
command apk update
|
||||
command apk add binutils cmake gcc g++ git make musl-dev perl-utils
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get install -y binutils cmake gcc g++ make wget
|
||||
else
|
||||
>&2 echo "[error] -s is not supported on this system"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
freebsd)
|
||||
command pkg install -y cmake gmake binutils git perl5 wget
|
||||
;;
|
||||
openbsd)
|
||||
command pkg_add install cmake gmake gcc git wget
|
||||
;;
|
||||
netbsd)
|
||||
command pkgin -y install cmake gmake binutils git
|
||||
;;
|
||||
darwin)
|
||||
if ! command -v make >/dev/null 2>&1 || ! command -v gcc >/dev/null 2>&1; then
|
||||
>&2 echo "[error] please run 'xcode-select --install' and retry"
|
||||
exit 1
|
||||
fi
|
||||
if command -v port >/dev/null 2>&1; then
|
||||
sudo port -N install libiconv cmake wget
|
||||
elif command -v brew >/dev/null 2>&1; then
|
||||
for formula in libiconv cmake git wget; do
|
||||
if command brew ls --version "$formula" &>/dev/null; then
|
||||
command brew upgrade "$formula"
|
||||
else
|
||||
command brew install "$formula"
|
||||
fi
|
||||
done
|
||||
else
|
||||
>&2 echo "[error] please install MacPorts or Homebrew and retry"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
msys*|mingw*)
|
||||
command pacman -Syu --noconfirm
|
||||
command pacman -S --needed --noconfirm binutils cmake gcc git make perl
|
||||
;;
|
||||
*)
|
||||
>&2 echo "[internal error] unhandled kernel: $gitstatus_kernel"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
cpus="$(command getconf _NPROCESSORS_ONLN 2>/dev/null)" ||
|
||||
cpus="$(command sysctl -n hw.ncpu 2>/dev/null)" ||
|
||||
cpus=8
|
||||
|
||||
case "$gitstatus_cpu" in
|
||||
powerpc64le) archflag="-mcpu";;
|
||||
*) archflag="-march";;
|
||||
esac
|
||||
|
||||
cflags="$archflag=$gitstatus_cpu -fno-plt -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fpie"
|
||||
ldflags=
|
||||
static_pie=
|
||||
|
||||
if [ -z "${CC-}" ]; then
|
||||
case "$gitstatus_kernel" in
|
||||
freebsd) export CC=clang;;
|
||||
*) export CC=cc;;
|
||||
esac
|
||||
fi
|
||||
|
||||
printf 'int main() {}\n' >"$workdir"/cc-test.c
|
||||
if 2>/dev/null "$CC" \
|
||||
-ffile-prefix-map=x=y \
|
||||
-Werror \
|
||||
-c "$workdir"/cc-test.c \
|
||||
-o "$workdir"/cc-test.o; then
|
||||
cflags="$cflags -ffile-prefix-map=$workdir/="
|
||||
fi
|
||||
|
||||
command rm -f -- "$workdir"/cc-test "$workdir"/cc-test.o
|
||||
if 2>/dev/null "$CC" \
|
||||
-fstack-clash-protection -fcf-protection \
|
||||
-Werror \
|
||||
-c "$workdir"/cc-test.c \
|
||||
-o "$workdir"/cc-test.o; then
|
||||
cflags="$cflags -fstack-clash-protection -fcf-protection"
|
||||
fi
|
||||
|
||||
command rm -f -- "$workdir"/cc-test "$workdir"/cc-test.o
|
||||
if 2>/dev/null "$CC" \
|
||||
-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now \
|
||||
-Werror \
|
||||
"$workdir"/cc-test.c \
|
||||
-o "$workdir"/cc-test; then
|
||||
ldflags="$ldflags -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"
|
||||
fi
|
||||
|
||||
command rm -f -- "$workdir"/cc-test "$workdir"/cc-test.o
|
||||
if 2>/dev/null "$CC" \
|
||||
-fpie -static-pie \
|
||||
-Werror \
|
||||
"$workdir"/cc-test.c \
|
||||
-o "$workdir"/cc-test; then
|
||||
static_pie='-static-pie'
|
||||
fi
|
||||
|
||||
if [ "$gitstatus_cpu" = x86-64 ]; then
|
||||
cflags="$cflags -mtune=generic"
|
||||
fi
|
||||
|
||||
libgit2_cmake_flags=
|
||||
libgit2_cflags="${CFLAGS-} $cflags -O3 -DNDEBUG"
|
||||
|
||||
gitstatus_cxx=g++
|
||||
gitstatus_cxxflags="${CXXFLAGS-} $cflags -I${workdir}/libgit2/include -DGITSTATUS_ZERO_NSEC -D_GNU_SOURCE -D_GLIBCXX_ASSERTIONS"
|
||||
gitstatus_ldflags="${LDFLAGS-} $ldflags -L${workdir}/libgit2/build"
|
||||
gitstatus_ldlibs=
|
||||
gitstatus_make=make
|
||||
|
||||
case "$gitstatus_kernel" in
|
||||
linux)
|
||||
gitstatus_ldflags="$gitstatus_ldflags ${static_pie:--static}"
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DENABLE_REPRODUCIBLE_BUILDS=ON"
|
||||
;;
|
||||
freebsd)
|
||||
gitstatus_cxx=clang++
|
||||
gitstatus_make=gmake
|
||||
gitstatus_ldflags="$gitstatus_ldflags ${static_pie:--static}"
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DENABLE_REPRODUCIBLE_BUILDS=ON"
|
||||
;;
|
||||
openbsd)
|
||||
gitstatus_cxx=eg++
|
||||
gitstatus_make=gmake
|
||||
gitstatus_ldflags="$gitstatus_ldflags ${static_pie:--static}"
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DENABLE_REPRODUCIBLE_BUILDS=ON"
|
||||
;;
|
||||
netbsd)
|
||||
gitstatus_make=gmake
|
||||
gitstatus_ldflags="$gitstatus_ldflags ${static_pie:--static}"
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DENABLE_REPRODUCIBLE_BUILDS=ON"
|
||||
;;
|
||||
darwin)
|
||||
command mkdir -- "$workdir"/lib
|
||||
if [ -e /opt/local/lib/libiconv.a ]; then
|
||||
command ln -s -- /opt/local/lib/libiconv.a "$workdir"/lib
|
||||
libgit2_cflags="$libgit2_cflags -I/opt/local/include"
|
||||
gitstatus_cxxflags="$gitstatus_cxxflags -I/opt/local/include"
|
||||
else
|
||||
brew_prefix="$(command brew --prefix)"
|
||||
command ln -s -- "$brew_prefix"/opt/libiconv/lib/libiconv.a "$workdir"/lib
|
||||
libgit2_cflags="$libgit2_cflags -I"$brew_prefix"/opt/libiconv/include"
|
||||
gitstatus_cxxflags="$gitstatus_cxxflags -I"$brew_prefix"/opt/libiconv/include"
|
||||
fi
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DUSE_ICONV=ON"
|
||||
gitstatus_ldlibs="$gitstatus_ldlibs -liconv"
|
||||
gitstatus_ldflags="$gitstatus_ldflags -L${workdir}/lib"
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DENABLE_REPRODUCIBLE_BUILDS=OFF"
|
||||
;;
|
||||
msys*|mingw*)
|
||||
gitstatus_ldflags="$gitstatus_ldflags ${static_pie:--static}"
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DENABLE_REPRODUCIBLE_BUILDS=ON"
|
||||
;;
|
||||
cygwin*)
|
||||
gitstatus_ldflags="$gitstatus_ldflags ${static_pie:--static}"
|
||||
libgit2_cmake_flags="$libgit2_cmake_flags -DENABLE_REPRODUCIBLE_BUILDS=ON"
|
||||
;;
|
||||
*)
|
||||
>&2 echo "[internal error] unhandled kernel: $gitstatus_kernel"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
for cmd in cat cmake git ld ln mkdir rm strip tar "$gitstatus_make"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
if [ -n "$gitstatus_install_tools" ]; then
|
||||
>&2 echo "[internal error] $cmd not found"
|
||||
exit 1
|
||||
else
|
||||
>&2 echo "[error] command not found: $cmd"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
. "$outdir"/build.info
|
||||
if [ -z "${libgit2_version:-}" ]; then
|
||||
>&2 echo "[internal error] libgit2_version not set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${libgit2_sha256:-}" ]; then
|
||||
>&2 echo "[internal error] libgit2_sha256 not set"
|
||||
exit 1
|
||||
fi
|
||||
libgit2_tarball="$outdir"/deps/libgit2-"$libgit2_version".tar.gz
|
||||
if [ ! -e "$libgit2_tarball" ]; then
|
||||
if [ -n "$gitstatus_download_deps" ]; then
|
||||
if ! command -v wget >/dev/null 2>&1; then
|
||||
if [ -n "$gitstatus_install_tools" ]; then
|
||||
>&2 echo "[internal error] wget not found"
|
||||
exit 1
|
||||
else
|
||||
>&2 echo "[error] command not found: wget"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
libgit2_url=https://github.com/romkatv/libgit2/archive/"$libgit2_version".tar.gz
|
||||
if ! >"$libgit2_tmp" command wget --no-config -qO- -- "$libgit2_url" &&
|
||||
! >"$libgit2_tmp" command wget -qO- -- "$libgit2_url"; then
|
||||
set -x
|
||||
>&2 command which wget
|
||||
>&2 command ls -lAd -- "$(command which wget)"
|
||||
>&2 command ls -lAd -- "$outdir"
|
||||
>&2 command ls -lA -- "$outdir"
|
||||
>&2 command ls -lAd -- "$outdir"/deps
|
||||
>&2 command ls -lA -- "$outdir"/deps
|
||||
set +x
|
||||
exit 1
|
||||
fi
|
||||
command mv -f -- "$libgit2_tmp" "$libgit2_tarball"
|
||||
else
|
||||
>&2 echo "[error] file not found: deps/libgit2-"$libgit2_version".tar.gz"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
libgit2_actual_sha256=
|
||||
if command -v shasum >/dev/null 2>/dev/null; then
|
||||
libgit2_actual_sha256="$(command shasum -b -a 256 -- "$libgit2_tarball")"
|
||||
libgit2_actual_sha256="${libgit2_actual_sha256%% *}"
|
||||
elif command -v sha256sum >/dev/null 2>/dev/null; then
|
||||
libgit2_actual_sha256="$(command sha256sum -b -- "$libgit2_tarball")"
|
||||
libgit2_actual_sha256="${libgit2_actual_sha256%% *}"
|
||||
elif command -v sha256 >/dev/null 2>/dev/null; then
|
||||
libgit2_actual_sha256="$(command sha256 -- "$libgit2_tarball" </dev/null)"
|
||||
# Ignore sha256 output if it's from hashalot. It's incompatible.
|
||||
if [ ${#libgit2_actual_sha256} -lt 64 ]; then
|
||||
libgit2_actual_sha256=
|
||||
else
|
||||
libgit2_actual_sha256="${libgit2_actual_sha256##* }"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$libgit2_actual_sha256" ]; then
|
||||
>&2 echo "[error] command not found: shasum or sha256sum"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$libgit2_actual_sha256" != "$libgit2_sha256" ]; then
|
||||
>&2 echo "[error] sha256 mismatch"
|
||||
>&2 echo ""
|
||||
>&2 echo " file : deps/libgit2-$libgit2_version.tar.gz"
|
||||
>&2 echo " expected: $libgit2_sha256"
|
||||
>&2 echo " actual : $libgit2_actual_sha256"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd -- "$workdir"
|
||||
command tar -xzf "$libgit2_tarball"
|
||||
command mv -- libgit2-"$libgit2_version" libgit2
|
||||
command mkdir libgit2/build
|
||||
cd libgit2/build
|
||||
|
||||
CFLAGS="$libgit2_cflags" command cmake \
|
||||
-DCMAKE_BUILD_TYPE=None \
|
||||
-DZERO_NSEC=ON \
|
||||
-DTHREADSAFE=ON \
|
||||
-DUSE_BUNDLED_ZLIB=ON \
|
||||
-DREGEX_BACKEND=builtin \
|
||||
-DUSE_HTTP_PARSER=builtin \
|
||||
-DUSE_SSH=OFF \
|
||||
-DUSE_HTTPS=OFF \
|
||||
-DBUILD_CLAR=OFF \
|
||||
-DUSE_GSSAPI=OFF \
|
||||
-DUSE_NTLMCLIENT=OFF \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
$libgit2_cmake_flags \
|
||||
..
|
||||
command make -j "$cpus" VERBOSE=1
|
||||
|
||||
APPNAME="$appname".tmp \
|
||||
OBJDIR="$workdir"/gitstatus \
|
||||
CXX="${CXX:-$gitstatus_cxx}" \
|
||||
CXXFLAGS="$gitstatus_cxxflags" \
|
||||
LDFLAGS="$gitstatus_ldflags" \
|
||||
LDLIBS="$gitstatus_ldlibs" \
|
||||
command "$gitstatus_make" -C "$outdir" -j "$cpus"
|
||||
|
||||
app="$outdir"/usrbin/"$appname"
|
||||
|
||||
command strip "$app".tmp
|
||||
|
||||
command mkdir -- "$workdir"/repo
|
||||
printf '[init]\n defaultBranch = master\n' >"$workdir"/.gitconfig
|
||||
(
|
||||
cd -- "$workdir"/repo
|
||||
GIT_CONFIG_NOSYSTEM=1 HOME="$workdir" command git init
|
||||
GIT_CONFIG_NOSYSTEM=1 HOME="$workdir" command git config user.name "Your Name"
|
||||
GIT_CONFIG_NOSYSTEM=1 HOME="$workdir" command git config user.email "you@example.com"
|
||||
GIT_CONFIG_NOSYSTEM=1 HOME="$workdir" command git commit \
|
||||
--allow-empty --allow-empty-message --no-gpg-sign -m ''
|
||||
)
|
||||
|
||||
resp="$(printf "hello\037$workdir/repo\036" | "$app".tmp)"
|
||||
case "$resp" in
|
||||
hello*1*/repo*master*);;
|
||||
*)
|
||||
>&2 echo 'error: invalid gitstatusd response for a git repo'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
resp="$(printf 'hello\037\036' | "$app".tmp)"
|
||||
case "$resp" in
|
||||
hello*0*);;
|
||||
*)
|
||||
>&2 echo 'error: invalid gitstatusd response for a non-repo'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
command mv -f -- "$app".tmp "$app"
|
||||
|
||||
cleanup
|
||||
|
||||
command cat >&2 <<-END
|
||||
-------------------------------------------------
|
||||
SUCCESS: created usrbin/$appname
|
||||
END
|
||||
END
|
||||
)"
|
||||
|
||||
docker_image=
|
||||
docker_cmd=
|
||||
|
||||
gitstatus_arch=
|
||||
gitstatus_cpu=
|
||||
gitstatus_install_tools=
|
||||
gitstatus_download_deps=
|
||||
|
||||
while getopts ':m:c:i:d:swh' opt "$@"; do
|
||||
case "$opt" in
|
||||
h)
|
||||
printf '%s\n' "$usage"
|
||||
exit
|
||||
;;
|
||||
m)
|
||||
if [ -n "$gitstatus_arch" ]; then
|
||||
>&2 echo "[error] duplicate option: -$opt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
exit 1
|
||||
fi
|
||||
gitstatus_arch="$OPTARG"
|
||||
;;
|
||||
c)
|
||||
if [ -n "$gitstatus_cpu" ]; then
|
||||
>&2 echo "[error] duplicate option: -$opt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
exit 1
|
||||
fi
|
||||
gitstatus_cpu="$OPTARG"
|
||||
;;
|
||||
i)
|
||||
if [ -n "$docker_image" ]; then
|
||||
>&2 echo "[error] duplicate option: -$opt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
exit 1
|
||||
fi
|
||||
docker_image="$OPTARG"
|
||||
;;
|
||||
d)
|
||||
if [ -n "$docker_cmd" ]; then
|
||||
>&2 echo "[error] duplicate option: -$opt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
exit 1
|
||||
fi
|
||||
docker_cmd="$OPTARG"
|
||||
;;
|
||||
s)
|
||||
if [ -n "$gitstatus_install_tools" ]; then
|
||||
>&2 echo "[error] duplicate option: -$opt"
|
||||
exit 1
|
||||
fi
|
||||
gitstatus_install_tools=1
|
||||
;;
|
||||
w)
|
||||
if [ -n "$gitstatus_download_deps" ]; then
|
||||
>&2 echo "[error] duplicate option: -$opt"
|
||||
exit 1
|
||||
fi
|
||||
gitstatus_download_deps=1
|
||||
;;
|
||||
\?) >&2 echo "[error] invalid option: -$OPTARG" ; exit 1;;
|
||||
:) >&2 echo "[error] missing required argument: -$OPTARG"; exit 1;;
|
||||
*) >&2 echo "[internal error] unhandled option: -$opt" ; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$OPTIND" -le $# ]; then
|
||||
>&2 echo "[error] unexpected positional argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$docker_image" -a -z "$docker_cmd" ]; then
|
||||
>&2 echo "[error] cannot use -i without -d"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$gitstatus_arch" ]; then
|
||||
gitstatus_arch="$(uname -m)"
|
||||
gitstatus_arch="$(printf '%s' "$gitstatus_arch" | tr '[A-Z]' '[a-z]')"
|
||||
fi
|
||||
|
||||
if [ -z "$gitstatus_cpu" ]; then
|
||||
case "$gitstatus_arch" in
|
||||
armel) gitstatus_cpu=armv5;;
|
||||
armv6l|armhf) gitstatus_cpu=armv6;;
|
||||
armv7l) gitstatus_cpu=armv7;;
|
||||
arm64|aarch64) gitstatus_cpu=armv8-a;;
|
||||
ppc64le) gitstatus_cpu=powerpc64le;;
|
||||
riscv64) gitstatus_cpu=rv64imafdc;;
|
||||
x86_64|amd64) gitstatus_cpu=x86-64;;
|
||||
x86) gitstatus_cpu=i586;;
|
||||
s390x) gitstatus_cpu=z900;;
|
||||
i386|i586|i686) gitstatus_cpu="$gitstatus_arch";;
|
||||
*)
|
||||
>&2 echo '[error] unable to infer target CPU architecture'
|
||||
>&2 echo 'Please specify explicitly with `-c CPU`.'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
gitstatus_kernel="$(uname -s)"
|
||||
gitstatus_kernel="$(printf '%s' "$gitstatus_kernel" | tr '[A-Z]' '[a-z]')"
|
||||
|
||||
case "$gitstatus_kernel" in
|
||||
linux)
|
||||
if [ -n "$docker_cmd" ]; then
|
||||
if [ -z "${docker_cmd##*/*}" ]; then
|
||||
if [ ! -x "$docker_cmd" ]; then
|
||||
>&2 echo "[error] not an executable file: $docker_cmd"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! command -v "$docker_cmd" >/dev/null 2>&1; then
|
||||
>&2 echo "[error] command not found: $docker_cmd"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [ -z "$docker_image" ]; then
|
||||
case "$gitstatus_arch" in
|
||||
x86_64) docker_image=alpine:3.11.6;;
|
||||
x86|i386|i586|i686) docker_image=i386/alpine:3.11.6;;
|
||||
armv6l|armhf) docker_image=arm32v6/alpine:3.11.6;;
|
||||
armv7l) docker_image=arm32v7/alpine:3.11.6;;
|
||||
aarch64) docker_image=arm64v8/alpine:3.11.6;;
|
||||
ppc64le) docker_image=ppc64le/alpine:3.11.6;;
|
||||
s390x) docker_image=s390x/alpine:3.11.6;;
|
||||
*)
|
||||
>&2 echo '[error] unable to infer docker image'
|
||||
>&2 echo 'Please specify explicitly with `-i IMAGE`.'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
freebsd|openbsd|netbsd|darwin)
|
||||
if [ -n "$docker_cmd" ]; then
|
||||
>&2 echo "[error] docker (-d) is not supported on $gitstatus_kernel"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
msys_nt-*|mingw32_nt-*|mingw64_nt-*|cygwin_nt-*)
|
||||
if ! printf '%s' "$gitstatus_kernel" | grep -Eqx '[^-]+-[0-9]+\.[0-9]+(-.*)?'; then
|
||||
>&2 echo '[error] unsupported kernel, sorry!'
|
||||
exit 1
|
||||
fi
|
||||
gitstatus_kernel="$(printf '%s' "$gitstatus_kernel" | sed 's/^\([^-]*-[0-9]*\.[0-9]*\).*/\1/')"
|
||||
if [ -n "$docker_cmd" ]; then
|
||||
>&2 echo '[error] docker (-d) is not supported on windows'
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$gitstatus_install_tools" -a -z "${gitstatus_kernel##cygwin_nt-*}" ]; then
|
||||
>&2 echo '[error] -s is not supported on cygwin'
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
>&2 echo '[error] unsupported kernel, sorry!'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
dir="$(dirname -- "$0")"
|
||||
cd -- "$dir"
|
||||
dir="$(pwd)"
|
||||
|
||||
>&2 echo "Building gitstatusd..."
|
||||
>&2 echo ""
|
||||
>&2 echo " kernel := $gitstatus_kernel"
|
||||
>&2 echo " arch := $gitstatus_arch"
|
||||
>&2 echo " cpu := $gitstatus_cpu"
|
||||
[ -z "$docker_cmd" ] || >&2 echo " docker command := $docker_cmd"
|
||||
[ -z "$docker_image" ] || >&2 echo " docker image := $docker_image"
|
||||
if [ -n "$gitstatus_install_tools" ]; then
|
||||
>&2 echo " install tools := yes"
|
||||
else
|
||||
>&2 echo " install tools := no"
|
||||
fi
|
||||
if [ -n "$gitstatus_download_deps" ]; then
|
||||
>&2 echo " download deps := yes"
|
||||
else
|
||||
>&2 echo " download deps := no"
|
||||
fi
|
||||
|
||||
if [ -n "$docker_cmd" ]; then
|
||||
"$docker_cmd" run \
|
||||
-e docker_cmd="$docker_cmd" \
|
||||
-e docker_image="$docker_image" \
|
||||
-e gitstatus_kernel="$gitstatus_kernel" \
|
||||
-e gitstatus_arch="$gitstatus_arch" \
|
||||
-e gitstatus_cpu="$gitstatus_cpu" \
|
||||
-e gitstatus_install_tools="$gitstatus_install_tools" \
|
||||
-e gitstatus_download_deps="$gitstatus_download_deps" \
|
||||
-v "$dir":/out \
|
||||
-w /out \
|
||||
--rm \
|
||||
-- "$docker_image" /bin/sh -uexc "$build"
|
||||
else
|
||||
eval "$build"
|
||||
fi
|
@ -0,0 +1,22 @@
|
||||
# This value gets embedded in gitstatusd at build time. It is
|
||||
# read by ./Makefile. `gitstatusd --version` reports it back.
|
||||
#
|
||||
# This value is also read by shell bindings (indirectly, through
|
||||
# ./install) when using GITSTATUS_DAEMON or usrbin/gitstatusd.
|
||||
gitstatus_version="v1.5.3"
|
||||
|
||||
# libgit2 is a build time dependency of gitstatusd. The values of
|
||||
# libgit2_version and libgit2_sha256 are read by ./build.
|
||||
#
|
||||
# If ./deps/libgit2-${libgit2_version}.tar.gz doesn't exist, build
|
||||
# downloads it from the following location:
|
||||
#
|
||||
# https://github.com/romkatv/libgit2/archive/${libgit2_version}.tar.gz
|
||||
#
|
||||
# Once downloaded, the tarball is stored at the path indicated
|
||||
# above so that repeated builds don't consume network bandwidth.
|
||||
#
|
||||
# If sha256 of ./deps/libgit2-${libgit2_version}.tar.gz doesn't match,
|
||||
# build gets aborted.
|
||||
libgit2_version="tag-5860a42d19bcd226cb6eff2dcbfcbf155d570c73"
|
||||
libgit2_sha256="2289203eda19913a2f6d2b26a15384cc43872bffd70e87a7659f9a22da79058e"
|
@ -0,0 +1,330 @@
|
||||
# Fast directory listing
|
||||
|
||||
In order to find untracked files in a git repository, [gitstatusd](../README.md) needs to list the
|
||||
contents of every directory. gitstatusd does it 27% faster than a reasonable implementation that a
|
||||
seasoned C/C++ practitioner might write. This document explains the optimizations that went into it.
|
||||
As directory listing is a common operation, many other projects can benefit from applying these
|
||||
optimizations.
|
||||
|
||||
## v1
|
||||
|
||||
Given a path to a directory, `ListDir()` must produce the list of files in that directory. Moreover,
|
||||
the list must be sorted lexicographically to enable fast comparison with Git index.
|
||||
|
||||
The following C++ implementation gets the job done. For simplicity, it returns an empty list on
|
||||
error.
|
||||
|
||||
```c++
|
||||
vector<string> ListDir(const char* dirname) {
|
||||
vector<string> entries;
|
||||
if (DIR* dir = opendir(dirname)) {
|
||||
while (struct dirent* ent = (errno = 0, readdir(dir))) {
|
||||
if (!Dots(ent->d_name)) entries.push_back(ent->d_name);
|
||||
}
|
||||
if (errno) entries.clear();
|
||||
sort(entries.begin(), entries.end());
|
||||
closedir(dir);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
```
|
||||
|
||||
Every directory has entries `"."` and `".."`, which we aren't interested in. We filter them out with
|
||||
a helper function `Dots()`.
|
||||
|
||||
```c++
|
||||
bool Dots(const char* s) { return s[0] == '.' && (!s[1] || (s[1] == '.' && !s[2])); }
|
||||
```
|
||||
|
||||
To check how fast `ListDir()` performs, we can run it many times on a typical directory. One million
|
||||
runs on a directory with 32 files with 16-character names takes 12.7 seconds.
|
||||
|
||||
## v2
|
||||
|
||||
Experienced C++ practitioners will scoff at our implementation of `ListDir()`. If it's meant to be
|
||||
efficient, returning `vector<string>` is an unaffordable convenience. To avoid heap allocations we
|
||||
can use a simple arena that will allow us to reuse memory between different `ListDir()` calls.
|
||||
|
||||
(Changed and added lines are marked with comments.)
|
||||
|
||||
```c++
|
||||
void ListDir(const char* dirname, string& arena, vector<char*>& entries) { // +
|
||||
entries.clear(); // +
|
||||
if (DIR* dir = opendir(dirname)) {
|
||||
arena.clear(); // +
|
||||
while (struct dirent* ent = (errno = 0, readdir(dir))) {
|
||||
if (!Dots(ent->d_name)) {
|
||||
entries.push_back(reinterpret_cast<char*>(arena.size())); // +
|
||||
arena.append(ent->d_name, strlen(ent->d_name) + 1); // +
|
||||
}
|
||||
}
|
||||
if (errno) entries.clear();
|
||||
for (char*& p : entries) p = &arena[reinterpret_cast<size_t>(p)]; // +
|
||||
sort(entries.begin(), entries.end(), // +
|
||||
[](const char* a, const char* b) { return strcmp(a, b) < 0; }); // +
|
||||
closedir(dir);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To make performance comparison easier, we can normalize them relative to the baseline. v1 will get
|
||||
performance score of 100. A twice-as-fast alternative will be 200.
|
||||
|
||||
| version | optimization | score |
|
||||
|---------|----------------------------|----------:|
|
||||
| v1 | baseline | 100.0 |
|
||||
| **v2** | **avoid heap allocations** | **112.7** |
|
||||
|
||||
Avoiding heap allocations makes `ListDir()` 12.7% faster. Not bad. As an added bonus, those casts
|
||||
will fend off the occasional frontend developer who accidentally wanders into the codebase.
|
||||
|
||||
## v3
|
||||
|
||||
`opendir()` is an expensive call whose performance is linear in the number of subdirectories in the
|
||||
path because it needs to perform a lookup for every one of them. We can replace it with `openat()`,
|
||||
which takes a file descriptor to the parent directory and a name of the subdirectory. Just a single
|
||||
lookup, less CPU time. This optimization assumes that callers already have a descriptor to the
|
||||
parent directory, which is indeed the case for gitstatusd, and is often the case in other
|
||||
applications that traverse filesystem.
|
||||
|
||||
```c++
|
||||
void ListDir(int parent_fd, const char* dirname, string& arena, vector<char*>& entries) { // +
|
||||
entries.clear();
|
||||
int dir_fd = openat(parent_fd, dirname, O_NOATIME | O_RDONLY | O_DIRECTORY | O_CLOEXEC); // +
|
||||
if (dir_fd < 0) return; // +
|
||||
if (DIR* dir = fdopendir(dir_fd)) {
|
||||
arena.clear();
|
||||
while (struct dirent* ent = (errno = 0, readdir(dir))) {
|
||||
if (!Dots(ent->d_name)) {
|
||||
entries.push_back(reinterpret_cast<char*>(arena.size()));
|
||||
arena.append(ent->d_name, strlen(ent->d_name) + 1);
|
||||
}
|
||||
}
|
||||
if (errno) entries.clear();
|
||||
for (char*& p : entries) p = &arena[reinterpret_cast<size_t>(p)];
|
||||
sort(entries.begin(), entries.end(),
|
||||
[](const char* a, const char* b) { return strcmp(a, b) < 0; });
|
||||
closedir(dir);
|
||||
} else { // +
|
||||
close(dir_fd); // +
|
||||
} // +
|
||||
}
|
||||
```
|
||||
|
||||
This is worth about 3.5% in speed.
|
||||
|
||||
| version | optimization | score |
|
||||
|---------|--------------------------------------|----------:|
|
||||
| v1 | baseline | 100.0 |
|
||||
| v2 | avoid heap allocations | 112.7 |
|
||||
| **v3** | **open directories with `openat()`** | **116.2** |
|
||||
|
||||
## v4
|
||||
|
||||
Copying file names to the arena isn't free but it doesn't seem like we can avoid it. Poking around
|
||||
we can see that the POSIX API we are using is implemented on Linux on top of `getdents64` system
|
||||
call. Its documentation isn't very encouraging:
|
||||
|
||||
```text
|
||||
These are not the interfaces you are interested in. Look at
|
||||
readdir(3) for the POSIX-conforming C library interface. This page
|
||||
documents the bare kernel system call interfaces.
|
||||
|
||||
Note: There are no glibc wrappers for these system calls.
|
||||
```
|
||||
|
||||
Hmm... The API looks like something we can take advantage of, so let's try it anyway.
|
||||
|
||||
First, we'll need a simple `Arena` class that can allocate 8KB blocks of memory.
|
||||
|
||||
```c++
|
||||
class Arena {
|
||||
public:
|
||||
enum { kBlockSize = 8 << 10 };
|
||||
|
||||
char* Alloc() {
|
||||
if (cur_ == blocks_.size()) blocks_.emplace_back(kBlockSize, 0);
|
||||
return blocks_[cur_++].data();
|
||||
}
|
||||
|
||||
void Clear() { cur_ = 0; }
|
||||
|
||||
private:
|
||||
size_t cur_ = 0;
|
||||
vector<string> blocks_;
|
||||
};
|
||||
```
|
||||
|
||||
Next, we need to define `struct dirent64_t` ourselves because there is no wrapper for the system
|
||||
call we are about to use.
|
||||
|
||||
```c++
|
||||
struct dirent64_t {
|
||||
ino64_t d_ino;
|
||||
off64_t d_off;
|
||||
unsigned short d_reclen;
|
||||
unsigned char d_type;
|
||||
char d_name[];
|
||||
};
|
||||
```
|
||||
|
||||
Finally we can get to the implementation of `ListDir()`.
|
||||
|
||||
```c++
|
||||
void ListDir(int parent_fd, Arena& arena, vector<char*>& entries) { // +
|
||||
entries.clear();
|
||||
int dir_fd = openat(parent_fd, dirname, O_NOATIME | O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
if (dir_fd < 0) return;
|
||||
arena.Clear(); // +
|
||||
while (true) { // +
|
||||
char* buf = arena.Alloc(); // +
|
||||
int n = syscall(SYS_getdents64, dir_fd, buf, Arena::kBlockSize); // +
|
||||
if (n <= 0) { // +
|
||||
if (n) entries.clear(); // +
|
||||
break; // +
|
||||
} // +
|
||||
for (int pos = 0; pos < n;) { // +
|
||||
auto* ent = reinterpret_cast<dirent64_t*>(buf + pos); // +
|
||||
if (!Dots(ent->d_name)) entries.push_back(ent->d_name); // +
|
||||
pos += ent->d_reclen; // +
|
||||
} // +
|
||||
} // +
|
||||
sort(entries.begin(), entries.end(),
|
||||
[](const char* a, const char* b) { return strcmp(a, b) < 0; });
|
||||
close(dir_fd);
|
||||
}
|
||||
```
|
||||
|
||||
How are we doing with this one?
|
||||
|
||||
| version | optimization | score |
|
||||
|---------|----------------------------------|----------:|
|
||||
| v1 | baseline | 100.0 |
|
||||
| v2 | avoid heap allocations | 112.7 |
|
||||
| v3 | open directories with `openat()` | 116.2 |
|
||||
| **v4** | **call `getdents64()` directly** | **137.8** |
|
||||
|
||||
Solid 20% speedup. Worth the trouble. Unfortunately, we now have just one `reinterpret_cast` instead
|
||||
of two, and it's not nearly as scary-looking. Hopefully with the next iteration we can get back some
|
||||
of that evil vibe of low-level code.
|
||||
|
||||
As a bonus, every element in `entries` has `d_type` at offset -1. This can be useful to the callers
|
||||
that need to distinguish between regular files and directories (gitstatusd, in fact, needs this).
|
||||
Note how `ListDir()` implements this feature at zero cost, as a lucky accident of `dirent64_t`
|
||||
memory layout.
|
||||
|
||||
## v5
|
||||
|
||||
The CPU profile of `ListDir()` reveals that almost all userspace CPU time is spent in `strcmp()`.
|
||||
Digging into the source code of `std::sort()` we can see that it uses Insertion Sort for short
|
||||
collections. Our 32-element vector falls under the threshold. Insertion Sort makes `O(N^2)`
|
||||
comparisons, hence a lot of CPU time in `strcmp()`. Switching to `qsort()` or
|
||||
[Timsort](https://en.wikipedia.org/wiki/Timsort) is of no use as all good sorting algorithms fall
|
||||
back to Insertion Sort.
|
||||
|
||||
If we cannot make fewer comparisons, perhaps we can make each of them faster? `strcmp()` compares
|
||||
characters one at a time. It cannot read ahead as it can be illegal to touch memory past the first
|
||||
null byte. But _we_ know that it's safe to read a few extra bytes past the end of `d_name` for every
|
||||
entry except the last in the buffer. And since we own the buffer, we can overallocate it so that
|
||||
reading past the end of the last entry is also safe.
|
||||
|
||||
Combining these ideas with the fact that file names on Linux are at most 255 bytes long, we can
|
||||
invoke `getdents64()` like this:
|
||||
|
||||
```c++
|
||||
int n = syscall(SYS_getdents64, dir_fd, buf, Arena::kBlockSize - 256);
|
||||
```
|
||||
|
||||
And then compare entries like this:
|
||||
|
||||
```c++
|
||||
[](const char* a, const char* b) { return memcmp(a, b, 255) < 0; }
|
||||
```
|
||||
|
||||
This version doesn't give any speedup compared to the previous but it opens an avenue for another
|
||||
optimization. The pointers we pass to `memcmp()` aren't aligned. To be more specific, their
|
||||
numerical values are `N * 8 + 3` for some `N`. When given such a pointer, `memcmp()` will check the
|
||||
first 5 bytes one by one, and only then switch to comparing 8 bytes at a time. If we can handle the
|
||||
first 5 bytes ourselves, we can pass aligned memory to `memcmp()` and take full advantage of its
|
||||
vectorized loop.
|
||||
|
||||
Here's the implementation:
|
||||
|
||||
```c++
|
||||
uint64_t Read64(const void* p) { // +
|
||||
uint64_t x; // +
|
||||
memcpy(&x, p, sizeof(x)); // +
|
||||
return x; // +
|
||||
} // +
|
||||
|
||||
void ByteSwap64(void* p) { // +
|
||||
uint64_t x = __builtin_bswap64(Read64(p)); // +
|
||||
memcpy(p, &x, sizeof(x)); // +
|
||||
} // +
|
||||
|
||||
void ListDir(int parent_fd, Arena& arena, vector<char*>& entries) {
|
||||
entries.clear();
|
||||
int dir_fd = openat(parent_fd, dirname, O_NOATIME | O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
if (dir_fd < 0) return;
|
||||
arena.Clear();
|
||||
while (true) {
|
||||
char* buf = arena.Alloc();
|
||||
int n = syscall(SYS_getdents64, dir_fd, buf, Arena::kBlockSize - 256); // +
|
||||
if (n <= 0) {
|
||||
if (n) entries.clear();
|
||||
break;
|
||||
}
|
||||
for (int pos = 0; pos < n;) {
|
||||
auto* ent = reinterpret_cast<dirent64_t*>(buf + pos);
|
||||
if (!Dots(ent->d_name)) {
|
||||
ByteSwap64(ent->d_name); // +
|
||||
entries.push_back(ent->d_name);
|
||||
}
|
||||
pos += ent->d_reclen;
|
||||
}
|
||||
}
|
||||
sort(entries.begin(), entries.end(), [](const char* a, const char* b) {
|
||||
uint64_t x = Read64(a); // +
|
||||
uint64_t y = Read64(b); // +
|
||||
return x < y || (x == y && a != b && memcmp(a + 5, b + 5, 256) < 0); // +
|
||||
});
|
||||
for (char* p : entries) ByteSwap64(p); // +
|
||||
close(dir_fd);
|
||||
}
|
||||
```
|
||||
|
||||
This is for Little Endian architecture. Big Endian doesn't need `ByteSwap64()`, so it'll be a bit
|
||||
faster.
|
||||
|
||||
| version | optimization | score |
|
||||
|---------|----------------------------------|----------:|
|
||||
| v1 | baseline | 100.0 |
|
||||
| v2 | avoid heap allocations | 112.7 |
|
||||
| v3 | open directories with `openat()` | 116.2 |
|
||||
| v4 | call `getdents64()` directly | 137.8 |
|
||||
| **v5** | **hand-optimize `strcmp()`** | **143.3** |
|
||||
|
||||
Fast and respectably arcane.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Through a series of incremental improvements we've sped up directory listing by 43.3% compared to a
|
||||
naive implementation (v1) and 27.2% compared to a reasonable implementation that a seasoned C/C++
|
||||
practitioner might write (v2).
|
||||
|
||||
However, these numbers are based on an artificial benchmark while the real judge is always the real
|
||||
code. Our goal was to speed up gitstatusd. Benchmark was just a tool. Thankfully, the different
|
||||
versions of `ListDir()` have the same comparative performance within gitstatusd as in the benchmark.
|
||||
In truth, the directory chosen for the benchmark wasn't arbitrary. It was picked by sampling
|
||||
gitstatusd when it runs on [chromium](https://github.com/chromium/chromium) git repository.
|
||||
|
||||
The final version of `ListDir()` spends 97% of its CPU time in the kernel. If we assume that it
|
||||
makes the minimum possible number of system calls and these calls are optimal (true to the best
|
||||
of my knowledge), it puts the upper bound on possible future performance improvements at just 3%.
|
||||
There is almost nothing left in `ListDir()` to optimize.
|
||||
|
||||

|
||||
|
||||
(The CPU profile was created with [gperftools](https://github.com/gperftools/gperftools) and
|
||||
rendered with [pprof](https://github.com/google/pprof)).
|
@ -0,0 +1,470 @@
|
||||
# Bash bindings for gitstatus.
|
||||
|
||||
[[ $- == *i* ]] || return # non-interactive shell
|
||||
|
||||
# Starts gitstatusd in the background. Does nothing and succeeds if gitstatusd
|
||||
# is already running.
|
||||
#
|
||||
# Usage: gitstatus_start [OPTION]...
|
||||
#
|
||||
# -t FLOAT Fail the self-check on initialization if not getting a response from
|
||||
# gitstatusd for this this many seconds. Defaults to 5.
|
||||
#
|
||||
# -s INT Report at most this many staged changes; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -u INT Report at most this many unstaged changes; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -c INT Report at most this many conflicted changes; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -d INT Report at most this many untracked files; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -m INT Report -1 unstaged, untracked and conflicted if there are more than this many
|
||||
# files in the index. Negative value means infinity. Defaults to -1.
|
||||
#
|
||||
# -e Count files within untracked directories like `git status --untracked-files`.
|
||||
#
|
||||
# -U Unless this option is specified, report zero untracked files for repositories
|
||||
# with status.showUntrackedFiles = false.
|
||||
#
|
||||
# -W Unless this option is specified, report zero untracked files for repositories
|
||||
# with bash.showUntrackedFiles = false.
|
||||
#
|
||||
# -D Unless this option is specified, report zero staged, unstaged and conflicted
|
||||
# changes for repositories with bash.showDirtyState = false.
|
||||
#
|
||||
# -r INT Close git repositories that haven't been used for this many seconds. This is
|
||||
# meant to release resources such as memory and file descriptors. The next request
|
||||
# for a repo that's been closed is much slower than for a repo that hasn't been.
|
||||
# Negative value means infinity. The default is 3600 (one hour).
|
||||
function gitstatus_start() {
|
||||
if [[ "$BASH_VERSION" < 4 ]]; then
|
||||
>&2 printf 'gitstatus_start: need bash version >= 4.0, found %s\n' "$BASH_VERSION"
|
||||
>&2 printf '\n'
|
||||
>&2 printf 'To see the version of the current shell, type:\n'
|
||||
>&2 printf '\n'
|
||||
>&2 printf ' \033[32mecho\033[0m \033[33m"$BASH_VERSION"\033[0m\n'
|
||||
>&2 printf '\n'
|
||||
>&2 printf 'The output of `\033[32mbash\033[0m --version` may be different and is not relevant.\n'
|
||||
return 1
|
||||
fi
|
||||
|
||||
unset OPTIND
|
||||
local opt timeout=5 max_dirty=-1 ttl=3600 extra_flags
|
||||
local max_num_staged=1 max_num_unstaged=1 max_num_conflicted=1 max_num_untracked=1
|
||||
local ignore_status_show_untracked_files
|
||||
while getopts "t:s:u:c:d:m:r:eUWD" opt; do
|
||||
case "$opt" in
|
||||
t) timeout=$OPTARG;;
|
||||
s) max_num_staged=$OPTARG;;
|
||||
u) max_num_unstaged=$OPTARG;;
|
||||
c) max_num_conflicted=$OPTARG;;
|
||||
d) max_num_untracked=$OPTARG;;
|
||||
m) max_dirty=$OPTARG;;
|
||||
r) ttl=$OPTARG;;
|
||||
e) extra_flags+='--recurse-untracked-dirs ';;
|
||||
U) extra_flags+='--ignore-status-show-untracked-files ';;
|
||||
W) extra_flags+='--ignore-bash-show-untracked-files ';;
|
||||
D) extra_flags+='--ignore-bash-show-dirty-state ';;
|
||||
*) return 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
(( OPTIND == $# + 1 )) || { echo "usage: gitstatus_start [OPTION]..." >&2; return 1; }
|
||||
|
||||
[[ -z "${GITSTATUS_DAEMON_PID:-}" ]] || return 0 # already started
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == */* ]]; then
|
||||
local gitstatus_plugin_dir="${BASH_SOURCE[0]%/*}"
|
||||
if [[ "$gitstatus_plugin_dir" != /* ]]; then
|
||||
gitstatus_plugin_dir="$PWD"/"$gitstatus_plugin_dir"
|
||||
fi
|
||||
else
|
||||
local gitstatus_plugin_dir="$PWD"
|
||||
fi
|
||||
|
||||
local tmpdir req_fifo resp_fifo culprit
|
||||
|
||||
function gitstatus_start_impl() {
|
||||
local log_level="${GITSTATUS_LOG_LEVEL:-}"
|
||||
[[ -n "$log_level" || "${GITSTATUS_ENABLE_LOGGING:-0}" != 1 ]] || log_level=INFO
|
||||
|
||||
local uname_sm
|
||||
uname_sm="$(command uname -sm)" || return
|
||||
uname_sm="${uname_sm,,}"
|
||||
local uname_s="${uname_sm% *}"
|
||||
local uname_m="${uname_sm#* }"
|
||||
|
||||
if [[ "${GITSTATUS_NUM_THREADS:-0}" -gt 0 ]]; then
|
||||
local threads="$GITSTATUS_NUM_THREADS"
|
||||
else
|
||||
local cpus
|
||||
if ! command -v sysctl &>/dev/null || [[ "$uname_s" == linux ]] ||
|
||||
! cpus="$(command sysctl -n hw.ncpu)"; then
|
||||
if ! command -v getconf &>/dev/null || ! cpus="$(command getconf _NPROCESSORS_ONLN)"; then
|
||||
cpus=8
|
||||
fi
|
||||
fi
|
||||
local threads=$((cpus > 16 ? 32 : cpus > 0 ? 2 * cpus : 16))
|
||||
fi
|
||||
|
||||
local daemon_args=(
|
||||
--parent-pid="$$"
|
||||
--num-threads="$threads"
|
||||
--max-num-staged="$max_num_staged"
|
||||
--max-num-unstaged="$max_num_unstaged"
|
||||
--max-num-conflicted="$max_num_conflicted"
|
||||
--max-num-untracked="$max_num_untracked"
|
||||
--dirty-max-index-size="$max_dirty"
|
||||
--repo-ttl-seconds="$ttl"
|
||||
$extra_flags)
|
||||
|
||||
tmpdir="$(command mktemp -d "${TMPDIR:-/tmp}"/gitstatus.bash.$$.XXXXXXXXXX)" || return
|
||||
|
||||
if [[ -n "$log_level" ]]; then
|
||||
GITSTATUS_DAEMON_LOG="$tmpdir"/daemon.log
|
||||
[[ "$log_level" == INFO ]] || daemon_args+=(--log-level="$log_level")
|
||||
else
|
||||
GITSTATUS_DAEMON_LOG=/dev/null
|
||||
fi
|
||||
|
||||
req_fifo="$tmpdir"/req.fifo
|
||||
resp_fifo="$tmpdir"/resp.fifo
|
||||
command mkfifo -- "$req_fifo" "$resp_fifo" || return
|
||||
|
||||
{
|
||||
(
|
||||
trap '' INT QUIT TSTP
|
||||
[[ "$GITSTATUS_DAEMON_LOG" == /dev/null ]] || set -x
|
||||
builtin cd /
|
||||
|
||||
(
|
||||
local fd_in fd_out
|
||||
exec {fd_in}<"$req_fifo" {fd_out}>>"$resp_fifo" || exit
|
||||
echo "$BASHPID" >&"$fd_out"
|
||||
|
||||
local _gitstatus_bash_daemon _gitstatus_bash_version _gitstatus_bash_downloaded
|
||||
|
||||
function _gitstatus_set_daemon() {
|
||||
_gitstatus_bash_daemon="$1"
|
||||
_gitstatus_bash_version="$2"
|
||||
_gitstatus_bash_downloaded="$3"
|
||||
}
|
||||
|
||||
set -- -d "$gitstatus_plugin_dir" -s "$uname_s" -m "$uname_m" \
|
||||
-p "printf '.\036' >&$fd_out" -e "$fd_out" -- _gitstatus_set_daemon
|
||||
[[ "${GITSTATUS_AUTO_INSTALL:-1}" -ne 0 ]] || set -- -n "$@"
|
||||
source "$gitstatus_plugin_dir"/install || return
|
||||
[[ -n "$_gitstatus_bash_daemon" ]] || return
|
||||
[[ -n "$_gitstatus_bash_version" ]] || return
|
||||
[[ "$_gitstatus_bash_downloaded" == [01] ]] || return
|
||||
|
||||
local sig=(TERM ILL PIPE)
|
||||
|
||||
if (( UID == EUID )); then
|
||||
local home=~
|
||||
else
|
||||
local user
|
||||
user="$(command id -un)" || return
|
||||
[[ "$user" =~ ^[a-zA-Z0-9_,.-]+$ ]] || return
|
||||
eval "local home=~$user"
|
||||
[[ -n "$home" ]] || return
|
||||
fi
|
||||
|
||||
if [[ -x "$_gitstatus_bash_daemon" ]]; then
|
||||
HOME="$home" "$_gitstatus_bash_daemon" \
|
||||
-G "$_gitstatus_bash_version" "${daemon_args[@]}" <&"$fd_in" >&"$fd_out" &
|
||||
local pid=$!
|
||||
trap "trap - ${sig[*]}; kill $pid &>/dev/null" ${sig[@]}
|
||||
wait "$pid"
|
||||
local ret=$?
|
||||
trap - ${sig[@]}
|
||||
case "$ret" in
|
||||
0|129|130|131|137|141|143|159)
|
||||
echo -nE $'}bye\x1f0\x1e' >&"$fd_out"
|
||||
exit "$ret"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
(( ! _gitstatus_bash_downloaded )) || return
|
||||
[[ "${GITSTATUS_AUTO_INSTALL:-1}" -ne 0 ]] || return
|
||||
[[ "$_gitstatus_bash_daemon" == \
|
||||
"${GITSTATUS_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/gitstatus}"/* ]] || return
|
||||
|
||||
set -- -f "$@"
|
||||
_gitstatus_bash_daemon=
|
||||
_gitstatus_bash_version=
|
||||
_gitstatus_bash_downloaded=
|
||||
source "$gitstatus_plugin_dir"/install || return
|
||||
[[ -n "$_gitstatus_bash_daemon" ]] || return
|
||||
[[ -n "$_gitstatus_bash_version" ]] || return
|
||||
[[ "$_gitstatus_bash_downloaded" == 1 ]] || return
|
||||
|
||||
HOME="$home" "$_gitstatus_bash_daemon" \
|
||||
-G "$_gitstatus_bash_version" "${daemon_args[@]}" <&"$fd_in" >&"$fd_out" &
|
||||
local pid=$!
|
||||
trap "trap - ${sig[*]}; kill $pid &>/dev/null" ${sig[@]}
|
||||
wait "$pid"
|
||||
trap - ${sig[@]}
|
||||
echo -nE $'}bye\x1f0\x1e' >&"$fd_out"
|
||||
) & disown
|
||||
) & disown
|
||||
} 0</dev/null &>"$GITSTATUS_DAEMON_LOG"
|
||||
|
||||
exec {_GITSTATUS_REQ_FD}>>"$req_fifo" {_GITSTATUS_RESP_FD}<"$resp_fifo" || return
|
||||
command rm -f -- "$req_fifo" "$resp_fifo" || return
|
||||
[[ "$GITSTATUS_DAEMON_LOG" != /dev/null ]] || command rmdir -- "$tmpdir" 2>/dev/null
|
||||
|
||||
IFS='' read -r -u $_GITSTATUS_RESP_FD GITSTATUS_DAEMON_PID || return
|
||||
[[ "$GITSTATUS_DAEMON_PID" == [1-9]* ]] || return
|
||||
|
||||
local reply
|
||||
echo -nE $'}hello\x1f\x1e' >&$_GITSTATUS_REQ_FD || return
|
||||
local dl=
|
||||
while true; do
|
||||
reply=
|
||||
if ! IFS='' read -rd $'\x1e' -u $_GITSTATUS_RESP_FD -t "$timeout" reply; then
|
||||
culprit="$reply"
|
||||
return 1
|
||||
fi
|
||||
[[ "$reply" == $'}hello\x1f0' ]] && break
|
||||
if [[ -z "$dl" ]]; then
|
||||
dl=1
|
||||
if [[ -t 2 ]]; then
|
||||
local spinner=('\b\033[33m-\033[0m' '\b\033[33m\\\033[0m' '\b\033[33m|\033[0m' '\b\033[33m/\033[0m')
|
||||
>&2 printf '[\033[33mgitstatus\033[0m] fetching \033[32mgitstatusd\033[0m .. '
|
||||
else
|
||||
local spinner=('.')
|
||||
>&2 printf '[gitstatus] fetching gitstatusd ..'
|
||||
fi
|
||||
fi
|
||||
>&2 printf "${spinner[0]}"
|
||||
spinner=("${spinner[@]:1}" "${spinner[0]}")
|
||||
done
|
||||
|
||||
if [[ -n "$dl" ]]; then
|
||||
if [[ -t 2 ]]; then
|
||||
>&2 printf '\b[\033[32mok\033[0m]\n'
|
||||
else
|
||||
>&2 echo ' [ok]'
|
||||
fi
|
||||
fi
|
||||
|
||||
_GITSTATUS_DIRTY_MAX_INDEX_SIZE=$max_dirty
|
||||
_GITSTATUS_CLIENT_PID="$BASHPID"
|
||||
}
|
||||
|
||||
if ! gitstatus_start_impl; then
|
||||
>&2 printf '\n'
|
||||
>&2 printf '[\033[31mERROR\033[0m]: gitstatus failed to initialize.\n'
|
||||
if [[ -n "${culprit-}" ]]; then
|
||||
>&2 printf '\n%s\n' "$culprit"
|
||||
fi
|
||||
[[ -z "${req_fifo:-}" ]] || command rm -f "$req_fifo"
|
||||
[[ -z "${resp_fifo:-}" ]] || command rm -f "$resp_fifo"
|
||||
unset -f gitstatus_start_impl
|
||||
gitstatus_stop
|
||||
return 1
|
||||
fi
|
||||
|
||||
export _GITSTATUS_CLIENT_PID _GITSTATUS_REQ_FD _GITSTATUS_RESP_FD GITSTATUS_DAEMON_PID
|
||||
unset -f gitstatus_start_impl
|
||||
}
|
||||
|
||||
# Stops gitstatusd if it's running.
|
||||
function gitstatus_stop() {
|
||||
if [[ "${_GITSTATUS_CLIENT_PID:-$BASHPID}" == "$BASHPID" ]]; then
|
||||
[[ -z "${_GITSTATUS_REQ_FD:-}" ]] || exec {_GITSTATUS_REQ_FD}>&- || true
|
||||
[[ -z "${_GITSTATUS_RESP_FD:-}" ]] || exec {_GITSTATUS_RESP_FD}>&- || true
|
||||
[[ -z "${GITSTATUS_DAEMON_PID:-}" ]] || kill "$GITSTATUS_DAEMON_PID" &>/dev/null || true
|
||||
fi
|
||||
unset _GITSTATUS_REQ_FD _GITSTATUS_RESP_FD GITSTATUS_DAEMON_PID
|
||||
unset _GITSTATUS_DIRTY_MAX_INDEX_SIZE _GITSTATUS_CLIENT_PID
|
||||
}
|
||||
|
||||
# Retrives status of a git repository from a directory under its working tree.
|
||||
#
|
||||
# Usage: gitstatus_query [OPTION]...
|
||||
#
|
||||
# -d STR Directory to query. Defaults to $PWD. Has no effect if GIT_DIR is set.
|
||||
# -t FLOAT Timeout in seconds. Will block for at most this long. If no results
|
||||
# are available by then, will return error.
|
||||
# -p Don't compute anything that requires reading Git index. If this option is used,
|
||||
# the following parameters will be 0: VCS_STATUS_INDEX_SIZE,
|
||||
# VCS_STATUS_{NUM,HAS}_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED}.
|
||||
#
|
||||
# On success sets VCS_STATUS_RESULT to one of the following values:
|
||||
#
|
||||
# norepo-sync The directory doesn't belong to a git repository.
|
||||
# ok-sync The directory belongs to a git repository.
|
||||
#
|
||||
# If VCS_STATUS_RESULT is ok-sync, additional variables are set:
|
||||
#
|
||||
# VCS_STATUS_WORKDIR Git repo working directory. Not empty.
|
||||
# VCS_STATUS_COMMIT Commit hash that HEAD is pointing to. Either 40 hex digits or
|
||||
# empty if there is no HEAD (empty repo).
|
||||
# VCS_STATUS_COMMIT_ENCODING Encoding of the HEAD's commit message. Empty value means UTF-8.
|
||||
# VCS_STATUS_COMMIT_SUMMARY The first paragraph of the HEAD's commit message as one line.
|
||||
# VCS_STATUS_LOCAL_BRANCH Local branch name or empty if not on a branch.
|
||||
# VCS_STATUS_REMOTE_NAME The remote name, e.g. "upstream" or "origin".
|
||||
# VCS_STATUS_REMOTE_BRANCH Upstream branch name. Can be empty.
|
||||
# VCS_STATUS_REMOTE_URL Remote URL. Can be empty.
|
||||
# VCS_STATUS_ACTION Repository state, A.K.A. action. Can be empty.
|
||||
# VCS_STATUS_INDEX_SIZE The number of files in the index.
|
||||
# VCS_STATUS_NUM_STAGED The number of staged changes.
|
||||
# VCS_STATUS_NUM_CONFLICTED The number of conflicted changes.
|
||||
# VCS_STATUS_NUM_UNSTAGED The number of unstaged changes.
|
||||
# VCS_STATUS_NUM_UNTRACKED The number of untracked files.
|
||||
# VCS_STATUS_HAS_STAGED 1 if there are staged changes, 0 otherwise.
|
||||
# VCS_STATUS_HAS_CONFLICTED 1 if there are conflicted changes, 0 otherwise.
|
||||
# VCS_STATUS_HAS_UNSTAGED 1 if there are unstaged changes, 0 if there aren't, -1 if
|
||||
# unknown.
|
||||
# VCS_STATUS_NUM_STAGED_NEW The number of staged new files. Note that renamed files
|
||||
# are reported as deleted plus new.
|
||||
# VCS_STATUS_NUM_STAGED_DELETED The number of staged deleted files. Note that renamed files
|
||||
# are reported as deleted plus new.
|
||||
# VCS_STATUS_NUM_UNSTAGED_DELETED The number of unstaged deleted files. Note that renamed files
|
||||
# are reported as deleted plus new.
|
||||
# VCS_STATUS_HAS_UNTRACKED 1 if there are untracked files, 0 if there aren't, -1 if
|
||||
# unknown.
|
||||
# VCS_STATUS_COMMITS_AHEAD Number of commits the current branch is ahead of upstream.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_COMMITS_BEHIND Number of commits the current branch is behind upstream.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_STASHES Number of stashes. Non-negative integer.
|
||||
# VCS_STATUS_TAG The last tag (in lexicographical order) that points to the same
|
||||
# commit as HEAD.
|
||||
# VCS_STATUS_PUSH_REMOTE_NAME The push remote name, e.g. "upstream" or "origin".
|
||||
# VCS_STATUS_PUSH_REMOTE_URL Push remote URL. Can be empty.
|
||||
# VCS_STATUS_PUSH_COMMITS_AHEAD Number of commits the current branch is ahead of push remote.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_PUSH_COMMITS_BEHIND Number of commits the current branch is behind push remote.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_NUM_SKIP_WORKTREE The number of files in the index with skip-worktree bit set.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_NUM_ASSUME_UNCHANGED The number of files in the index with assume-unchanged bit set.
|
||||
# Non-negative integer.
|
||||
#
|
||||
# The point of reporting -1 via VCS_STATUS_HAS_* is to allow the command to skip scanning files in
|
||||
# large repos. See -m flag of gitstatus_start.
|
||||
#
|
||||
# gitstatus_query returns an error if gitstatus_start hasn't been called in the same
|
||||
# shell or the call had failed.
|
||||
function gitstatus_query() {
|
||||
unset OPTIND
|
||||
local opt dir timeout=() no_diff=0
|
||||
while getopts "d:c:t:p" opt "$@"; do
|
||||
case "$opt" in
|
||||
d) dir=$OPTARG;;
|
||||
t) timeout=(-t "$OPTARG");;
|
||||
p) no_diff=1;;
|
||||
*) return 1;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND == $# + 1 )) || { echo "usage: gitstatus_query [OPTION]..." >&2; return 1; }
|
||||
|
||||
[[ -n "$GITSTATUS_DAEMON_PID" ]] || return # not started
|
||||
|
||||
local req_id="$RANDOM.$RANDOM.$RANDOM.$RANDOM"
|
||||
if [[ -z "${GIT_DIR:-}" ]]; then
|
||||
[[ "$dir" == /* ]] || dir="$(pwd -P)/$dir" || return
|
||||
elif [[ "$GIT_DIR" == /* ]]; then
|
||||
dir=:"$GIT_DIR"
|
||||
else
|
||||
dir=:"$(pwd -P)/$GIT_DIR" || return
|
||||
fi
|
||||
echo -nE "$req_id"$'\x1f'"$dir"$'\x1f'"$no_diff"$'\x1e' >&$_GITSTATUS_REQ_FD || return
|
||||
|
||||
local -a resp
|
||||
while true; do
|
||||
IFS=$'\x1f' read -rd $'\x1e' -a resp -u $_GITSTATUS_RESP_FD "${timeout[@]}" || return
|
||||
[[ "${resp[0]}" == "$req_id" ]] && break
|
||||
done
|
||||
|
||||
if [[ "${resp[1]}" == 1 ]]; then
|
||||
VCS_STATUS_RESULT=ok-sync
|
||||
VCS_STATUS_WORKDIR="${resp[2]}"
|
||||
VCS_STATUS_COMMIT="${resp[3]}"
|
||||
VCS_STATUS_LOCAL_BRANCH="${resp[4]}"
|
||||
VCS_STATUS_REMOTE_BRANCH="${resp[5]}"
|
||||
VCS_STATUS_REMOTE_NAME="${resp[6]}"
|
||||
VCS_STATUS_REMOTE_URL="${resp[7]}"
|
||||
VCS_STATUS_ACTION="${resp[8]}"
|
||||
VCS_STATUS_INDEX_SIZE="${resp[9]}"
|
||||
VCS_STATUS_NUM_STAGED="${resp[10]}"
|
||||
VCS_STATUS_NUM_UNSTAGED="${resp[11]}"
|
||||
VCS_STATUS_NUM_CONFLICTED="${resp[12]}"
|
||||
VCS_STATUS_NUM_UNTRACKED="${resp[13]}"
|
||||
VCS_STATUS_COMMITS_AHEAD="${resp[14]}"
|
||||
VCS_STATUS_COMMITS_BEHIND="${resp[15]}"
|
||||
VCS_STATUS_STASHES="${resp[16]}"
|
||||
VCS_STATUS_TAG="${resp[17]}"
|
||||
VCS_STATUS_NUM_UNSTAGED_DELETED="${resp[18]}"
|
||||
VCS_STATUS_NUM_STAGED_NEW="${resp[19]:-0}"
|
||||
VCS_STATUS_NUM_STAGED_DELETED="${resp[20]:-0}"
|
||||
VCS_STATUS_PUSH_REMOTE_NAME="${resp[21]:-}"
|
||||
VCS_STATUS_PUSH_REMOTE_URL="${resp[22]:-}"
|
||||
VCS_STATUS_PUSH_COMMITS_AHEAD="${resp[23]:-0}"
|
||||
VCS_STATUS_PUSH_COMMITS_BEHIND="${resp[24]:-0}"
|
||||
VCS_STATUS_NUM_SKIP_WORKTREE="${resp[25]:-0}"
|
||||
VCS_STATUS_NUM_ASSUME_UNCHANGED="${resp[26]:-0}"
|
||||
VCS_STATUS_COMMIT_ENCODING="${resp[27]-}"
|
||||
VCS_STATUS_COMMIT_SUMMARY="${resp[28]-}"
|
||||
VCS_STATUS_HAS_STAGED=$((VCS_STATUS_NUM_STAGED > 0))
|
||||
if (( _GITSTATUS_DIRTY_MAX_INDEX_SIZE >= 0 &&
|
||||
VCS_STATUS_INDEX_SIZE > _GITSTATUS_DIRTY_MAX_INDEX_SIZE_ )); then
|
||||
VCS_STATUS_HAS_UNSTAGED=-1
|
||||
VCS_STATUS_HAS_CONFLICTED=-1
|
||||
VCS_STATUS_HAS_UNTRACKED=-1
|
||||
else
|
||||
VCS_STATUS_HAS_UNSTAGED=$((VCS_STATUS_NUM_UNSTAGED > 0))
|
||||
VCS_STATUS_HAS_CONFLICTED=$((VCS_STATUS_NUM_CONFLICTED > 0))
|
||||
VCS_STATUS_HAS_UNTRACKED=$((VCS_STATUS_NUM_UNTRACKED > 0))
|
||||
fi
|
||||
else
|
||||
VCS_STATUS_RESULT=norepo-sync
|
||||
unset VCS_STATUS_WORKDIR
|
||||
unset VCS_STATUS_COMMIT
|
||||
unset VCS_STATUS_LOCAL_BRANCH
|
||||
unset VCS_STATUS_REMOTE_BRANCH
|
||||
unset VCS_STATUS_REMOTE_NAME
|
||||
unset VCS_STATUS_REMOTE_URL
|
||||
unset VCS_STATUS_ACTION
|
||||
unset VCS_STATUS_INDEX_SIZE
|
||||
unset VCS_STATUS_NUM_STAGED
|
||||
unset VCS_STATUS_NUM_UNSTAGED
|
||||
unset VCS_STATUS_NUM_CONFLICTED
|
||||
unset VCS_STATUS_NUM_UNTRACKED
|
||||
unset VCS_STATUS_HAS_STAGED
|
||||
unset VCS_STATUS_HAS_UNSTAGED
|
||||
unset VCS_STATUS_HAS_CONFLICTED
|
||||
unset VCS_STATUS_HAS_UNTRACKED
|
||||
unset VCS_STATUS_COMMITS_AHEAD
|
||||
unset VCS_STATUS_COMMITS_BEHIND
|
||||
unset VCS_STATUS_STASHES
|
||||
unset VCS_STATUS_TAG
|
||||
unset VCS_STATUS_NUM_UNSTAGED_DELETED
|
||||
unset VCS_STATUS_NUM_STAGED_NEW
|
||||
unset VCS_STATUS_NUM_STAGED_DELETED
|
||||
unset VCS_STATUS_PUSH_REMOTE_NAME
|
||||
unset VCS_STATUS_PUSH_REMOTE_URL
|
||||
unset VCS_STATUS_PUSH_COMMITS_AHEAD
|
||||
unset VCS_STATUS_PUSH_COMMITS_BEHIND
|
||||
unset VCS_STATUS_NUM_SKIP_WORKTREE
|
||||
unset VCS_STATUS_NUM_ASSUME_UNCHANGED
|
||||
unset VCS_STATUS_COMMIT_ENCODING
|
||||
unset VCS_STATUS_COMMIT_SUMMARY
|
||||
fi
|
||||
}
|
||||
|
||||
# Usage: gitstatus_check.
|
||||
#
|
||||
# Returns 0 if and only if gitstatus_start has succeeded previously.
|
||||
# If it returns non-zero, gitstatus_query is guaranteed to return non-zero.
|
||||
function gitstatus_check() {
|
||||
[[ -n "$GITSTATUS_DAEMON_PID" ]]
|
||||
}
|
@ -0,0 +1,891 @@
|
||||
# Zsh bindings for gitstatus.
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Example: Start gitstatusd, send it a request, wait for response and print it.
|
||||
#
|
||||
# source ~/gitstatus/gitstatus.plugin.zsh
|
||||
# gitstatus_start MY
|
||||
# gitstatus_query -d $PWD MY
|
||||
# typeset -m 'VCS_STATUS_*'
|
||||
#
|
||||
# Output:
|
||||
#
|
||||
# VCS_STATUS_ACTION=''
|
||||
# VCS_STATUS_COMMIT=c000eddcff0fb38df2d0137efe24d9d2d900f209
|
||||
# VCS_STATUS_COMMITS_AHEAD=0
|
||||
# VCS_STATUS_COMMITS_BEHIND=0
|
||||
# VCS_STATUS_COMMIT_ENCODING=''
|
||||
# VCS_STATUS_COMMIT_SUMMARY='pull upstream changes from gitstatus'
|
||||
# VCS_STATUS_HAS_CONFLICTED=0
|
||||
# VCS_STATUS_HAS_STAGED=0
|
||||
# VCS_STATUS_HAS_UNSTAGED=1
|
||||
# VCS_STATUS_HAS_UNTRACKED=1
|
||||
# VCS_STATUS_INDEX_SIZE=33
|
||||
# VCS_STATUS_LOCAL_BRANCH=master
|
||||
# VCS_STATUS_NUM_ASSUME_UNCHANGED=0
|
||||
# VCS_STATUS_NUM_CONFLICTED=0
|
||||
# VCS_STATUS_NUM_STAGED=0
|
||||
# VCS_STATUS_NUM_UNSTAGED=1
|
||||
# VCS_STATUS_NUM_SKIP_WORKTREE=0
|
||||
# VCS_STATUS_NUM_STAGED_NEW=0
|
||||
# VCS_STATUS_NUM_STAGED_DELETED=0
|
||||
# VCS_STATUS_NUM_UNSTAGED_DELETED=0
|
||||
# VCS_STATUS_NUM_UNTRACKED=1
|
||||
# VCS_STATUS_PUSH_COMMITS_AHEAD=0
|
||||
# VCS_STATUS_PUSH_COMMITS_BEHIND=0
|
||||
# VCS_STATUS_PUSH_REMOTE_NAME=''
|
||||
# VCS_STATUS_PUSH_REMOTE_URL=''
|
||||
# VCS_STATUS_REMOTE_BRANCH=master
|
||||
# VCS_STATUS_REMOTE_NAME=origin
|
||||
# VCS_STATUS_REMOTE_URL=git@github.com:romkatv/powerlevel10k.git
|
||||
# VCS_STATUS_RESULT=ok-sync
|
||||
# VCS_STATUS_STASHES=0
|
||||
# VCS_STATUS_TAG=''
|
||||
# VCS_STATUS_WORKDIR=/home/romka/powerlevel10k
|
||||
|
||||
[[ -o 'interactive' ]] || 'return'
|
||||
|
||||
# Temporarily change options.
|
||||
'builtin' 'local' '-a' '_gitstatus_opts'
|
||||
[[ ! -o 'aliases' ]] || _gitstatus_opts+=('aliases')
|
||||
[[ ! -o 'sh_glob' ]] || _gitstatus_opts+=('sh_glob')
|
||||
[[ ! -o 'no_brace_expand' ]] || _gitstatus_opts+=('no_brace_expand')
|
||||
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
|
||||
|
||||
autoload -Uz add-zsh-hook || return
|
||||
zmodload zsh/datetime zsh/system || return
|
||||
zmodload -F zsh/files b:zf_rm || return
|
||||
|
||||
typeset -g _gitstatus_plugin_dir"${1:-}"="${${(%):-%x}:A:h}"
|
||||
|
||||
# Retrives status of a git repo from a directory under its working tree.
|
||||
#
|
||||
## Usage: gitstatus_query [OPTION]... NAME
|
||||
#
|
||||
# -d STR Directory to query. Defaults to the current directory. Has no effect if GIT_DIR
|
||||
# is set.
|
||||
# -c STR Callback function to call once the results are available. Called only after
|
||||
# gitstatus_query returns 0 with VCS_STATUS_RESULT=tout.
|
||||
# -t FLOAT Timeout in seconds. Negative value means infinity. Will block for at most this long.
|
||||
# If no results are available by then: if -c isn't specified, will return 1; otherwise
|
||||
# will set VCS_STATUS_RESULT=tout and return 0.
|
||||
# -p Don't compute anything that requires reading Git index. If this option is used,
|
||||
# the following parameters will be 0: VCS_STATUS_INDEX_SIZE,
|
||||
# VCS_STATUS_{NUM,HAS}_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED}.
|
||||
#
|
||||
# On success sets VCS_STATUS_RESULT to one of the following values:
|
||||
#
|
||||
# tout Timed out waiting for data; will call the user-specified callback later.
|
||||
# norepo-sync The directory isn't a git repo.
|
||||
# ok-sync The directory is a git repo.
|
||||
#
|
||||
# When the callback is called, VCS_STATUS_RESULT is set to one of the following values:
|
||||
#
|
||||
# norepo-async The directory isn't a git repo.
|
||||
# ok-async The directory is a git repo.
|
||||
#
|
||||
# If VCS_STATUS_RESULT is ok-sync or ok-async, additional variables are set:
|
||||
#
|
||||
# VCS_STATUS_WORKDIR Git repo working directory. Not empty.
|
||||
# VCS_STATUS_COMMIT Commit hash that HEAD is pointing to. Either 40 hex digits or
|
||||
# empty if there is no HEAD (empty repo).
|
||||
# VCS_STATUS_COMMIT_ENCODING Encoding of the HEAD's commit message. Empty value means UTF-8.
|
||||
# VCS_STATUS_COMMIT_SUMMARY The first paragraph of the HEAD's commit message as one line.
|
||||
# VCS_STATUS_LOCAL_BRANCH Local branch name or empty if not on a branch.
|
||||
# VCS_STATUS_REMOTE_NAME The remote name, e.g. "upstream" or "origin".
|
||||
# VCS_STATUS_REMOTE_BRANCH Upstream branch name. Can be empty.
|
||||
# VCS_STATUS_REMOTE_URL Remote URL. Can be empty.
|
||||
# VCS_STATUS_ACTION Repository state, A.K.A. action. Can be empty.
|
||||
# VCS_STATUS_INDEX_SIZE The number of files in the index.
|
||||
# VCS_STATUS_NUM_STAGED The number of staged changes.
|
||||
# VCS_STATUS_NUM_CONFLICTED The number of conflicted changes.
|
||||
# VCS_STATUS_NUM_UNSTAGED The number of unstaged changes.
|
||||
# VCS_STATUS_NUM_UNTRACKED The number of untracked files.
|
||||
# VCS_STATUS_HAS_STAGED 1 if there are staged changes, 0 otherwise.
|
||||
# VCS_STATUS_HAS_CONFLICTED 1 if there are conflicted changes, 0 otherwise.
|
||||
# VCS_STATUS_HAS_UNSTAGED 1 if there are unstaged changes, 0 if there aren't, -1 if
|
||||
# unknown.
|
||||
# VCS_STATUS_NUM_STAGED_NEW The number of staged new files. Note that renamed files
|
||||
# are reported as deleted plus new.
|
||||
# VCS_STATUS_NUM_STAGED_DELETED The number of staged deleted files. Note that renamed files
|
||||
# are reported as deleted plus new.
|
||||
# VCS_STATUS_NUM_UNSTAGED_DELETED The number of unstaged deleted files. Note that renamed files
|
||||
# are reported as deleted plus new.
|
||||
# VCS_STATUS_HAS_UNTRACKED 1 if there are untracked files, 0 if there aren't, -1 if
|
||||
# unknown.
|
||||
# VCS_STATUS_COMMITS_AHEAD Number of commits the current branch is ahead of upstream.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_COMMITS_BEHIND Number of commits the current branch is behind upstream.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_STASHES Number of stashes. Non-negative integer.
|
||||
# VCS_STATUS_TAG The last tag (in lexicographical order) that points to the same
|
||||
# commit as HEAD.
|
||||
# VCS_STATUS_PUSH_REMOTE_NAME The push remote name, e.g. "upstream" or "origin".
|
||||
# VCS_STATUS_PUSH_REMOTE_URL Push remote URL. Can be empty.
|
||||
# VCS_STATUS_PUSH_COMMITS_AHEAD Number of commits the current branch is ahead of push remote.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_PUSH_COMMITS_BEHIND Number of commits the current branch is behind push remote.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_NUM_SKIP_WORKTREE The number of files in the index with skip-worktree bit set.
|
||||
# Non-negative integer.
|
||||
# VCS_STATUS_NUM_ASSUME_UNCHANGED The number of files in the index with assume-unchanged bit set.
|
||||
# Non-negative integer.
|
||||
#
|
||||
# The point of reporting -1 via VCS_STATUS_HAS_* is to allow the command to skip scanning files in
|
||||
# large repos. See -m flag of gitstatus_start.
|
||||
#
|
||||
# gitstatus_query returns an error if gitstatus_start hasn't been called in the same shell or
|
||||
# the call had failed.
|
||||
#
|
||||
# !!!!! WARNING: CONCURRENT CALLS WITH THE SAME NAME ARE NOT ALLOWED !!!!!
|
||||
#
|
||||
# It's illegal to call gitstatus_query if the last asynchronous call with the same NAME hasn't
|
||||
# completed yet. If you need to issue concurrent requests, use different NAME arguments.
|
||||
function gitstatus_query"${1:-}"() {
|
||||
emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
|
||||
|
||||
local fsuf=${${(%):-%N}#gitstatus_query}
|
||||
|
||||
unset VCS_STATUS_RESULT
|
||||
|
||||
local opt dir callback OPTARG
|
||||
local -i no_diff OPTIND
|
||||
local -F timeout=-1
|
||||
while getopts ":d:c:t:p" opt; do
|
||||
case $opt in
|
||||
+p) no_diff=0;;
|
||||
p) no_diff=1;;
|
||||
d) dir=$OPTARG;;
|
||||
c) callback=$OPTARG;;
|
||||
t)
|
||||
if [[ $OPTARG != (|+|-)<->(|.<->)(|[eE](|-|+)<->) ]]; then
|
||||
print -ru2 -- "gitstatus_query: invalid -t argument: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
timeout=OPTARG
|
||||
;;
|
||||
\?) print -ru2 -- "gitstatus_query: invalid option: $OPTARG" ; return 1;;
|
||||
:) print -ru2 -- "gitstatus_query: missing required argument: $OPTARG"; return 1;;
|
||||
*) print -ru2 -- "gitstatus_query: invalid option: $opt" ; return 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( OPTIND != ARGC )); then
|
||||
print -ru2 -- "gitstatus_query: exactly one positional argument is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local name=$*[OPTIND]
|
||||
if [[ $name != [[:IDENT:]]## ]]; then
|
||||
print -ru2 -- "gitstatus_query: invalid positional argument: $name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
(( _GITSTATUS_STATE_$name == 2 )) || return
|
||||
|
||||
if [[ -z $GIT_DIR ]]; then
|
||||
[[ $dir == /* ]] || dir=${(%):-%/}/$dir
|
||||
else
|
||||
[[ $GIT_DIR == /* ]] && dir=:$GIT_DIR || dir=:${(%):-%/}/$GIT_DIR
|
||||
fi
|
||||
|
||||
if [[ $dir != (|:)/* ]]; then
|
||||
typeset -g VCS_STATUS_RESULT=norepo-sync
|
||||
_gitstatus_clear$fsuf
|
||||
return 0
|
||||
fi
|
||||
|
||||
local -i req_fd=${(P)${:-_GITSTATUS_REQ_FD_$name}}
|
||||
local req_id=$EPOCHREALTIME
|
||||
print -rnu $req_fd -- $req_id' '$callback$'\x1f'$dir$'\x1f'$no_diff$'\x1e' || return
|
||||
|
||||
(( ++_GITSTATUS_NUM_INFLIGHT_$name ))
|
||||
|
||||
if (( timeout == 0 )); then
|
||||
typeset -g VCS_STATUS_RESULT=tout
|
||||
_gitstatus_clear$fsuf
|
||||
else
|
||||
while true; do
|
||||
_gitstatus_process_response$fsuf $name $timeout $req_id || return
|
||||
[[ $VCS_STATUS_RESULT == *-async ]] || break
|
||||
done
|
||||
fi
|
||||
|
||||
[[ $VCS_STATUS_RESULT != tout || -n $callback ]]
|
||||
}
|
||||
|
||||
# If the last call to gitstatus_query timed out (VCS_STATUS_RESULT=tout), wait for the callback
|
||||
# to be called. Otherwise do nothing.
|
||||
#
|
||||
# Usage: gitstatus_process_results [OPTION]... NAME
|
||||
#
|
||||
# -t FLOAT Timeout in seconds. Negative value means infinity. Will block for at most this long.
|
||||
#
|
||||
# Returns an error only when invoked with incorrect arguments and when gitstatusd isn't running or
|
||||
# broken.
|
||||
#
|
||||
# If a callback gets called, VCS_STATUS_* parameters are set as in gitstatus_query.
|
||||
# VCS_STATUS_RESULT is either norepo-async or ok-async.
|
||||
function gitstatus_process_results"${1:-}"() {
|
||||
emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
|
||||
|
||||
local fsuf=${${(%):-%N}#gitstatus_process_results}
|
||||
|
||||
local opt OPTARG
|
||||
local -i OPTIND
|
||||
local -F timeout=-1
|
||||
while getopts ":t:" opt; do
|
||||
case $opt in
|
||||
t)
|
||||
if [[ $OPTARG != (|+|-)<->(|.<->)(|[eE](|-|+)<->) ]]; then
|
||||
print -ru2 -- "gitstatus_process_results: invalid -t argument: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
timeout=OPTARG
|
||||
;;
|
||||
\?) print -ru2 -- "gitstatus_process_results: invalid option: $OPTARG" ; return 1;;
|
||||
:) print -ru2 -- "gitstatus_process_results: missing required argument: $OPTARG"; return 1;;
|
||||
*) print -ru2 -- "gitstatus_process_results: invalid option: $opt" ; return 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( OPTIND != ARGC )); then
|
||||
print -ru2 -- "gitstatus_process_results: exactly one positional argument is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local name=$*[OPTIND]
|
||||
if [[ $name != [[:IDENT:]]## ]]; then
|
||||
print -ru2 -- "gitstatus_process_results: invalid positional argument: $name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
(( _GITSTATUS_STATE_$name == 2 )) || return
|
||||
|
||||
while (( _GITSTATUS_NUM_INFLIGHT_$name )); do
|
||||
_gitstatus_process_response$fsuf $name $timeout '' || return
|
||||
[[ $VCS_STATUS_RESULT == *-async ]] || break
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function _gitstatus_clear"${1:-}"() {
|
||||
unset VCS_STATUS_{WORKDIR,COMMIT,LOCAL_BRANCH,REMOTE_BRANCH,REMOTE_NAME,REMOTE_URL,ACTION,INDEX_SIZE,NUM_STAGED,NUM_UNSTAGED,NUM_CONFLICTED,NUM_UNTRACKED,HAS_STAGED,HAS_UNSTAGED,HAS_CONFLICTED,HAS_UNTRACKED,COMMITS_AHEAD,COMMITS_BEHIND,STASHES,TAG,NUM_UNSTAGED_DELETED,NUM_STAGED_NEW,NUM_STAGED_DELETED,PUSH_REMOTE_NAME,PUSH_REMOTE_URL,PUSH_COMMITS_AHEAD,PUSH_COMMITS_BEHIND,NUM_SKIP_WORKTREE,NUM_ASSUME_UNCHANGED}
|
||||
}
|
||||
|
||||
function _gitstatus_process_response"${1:-}"() {
|
||||
local name=$1 timeout req_id=$3 buf
|
||||
local -i resp_fd=_GITSTATUS_RESP_FD_$name
|
||||
local -i dirty_max_index_size=_GITSTATUS_DIRTY_MAX_INDEX_SIZE_$name
|
||||
|
||||
(( $2 >= 0 )) && timeout=-t$2 && [[ -t $resp_fd ]]
|
||||
sysread $timeout -i $resp_fd 'buf[$#buf+1]' || {
|
||||
if (( $? == 4 )); then
|
||||
if [[ -n $req_id ]]; then
|
||||
typeset -g VCS_STATUS_RESULT=tout
|
||||
_gitstatus_clear$fsuf
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
gitstatus_stop$fsuf $name
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
while [[ $buf != *$'\x1e' ]]; do
|
||||
if ! sysread -i $resp_fd 'buf[$#buf+1]'; then
|
||||
gitstatus_stop$fsuf $name
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
local s
|
||||
for s in ${(ps:\x1e:)buf}; do
|
||||
local -a resp=("${(@ps:\x1f:)s}")
|
||||
if (( resp[2] )); then
|
||||
if [[ $resp[1] == $req_id' '* ]]; then
|
||||
typeset -g VCS_STATUS_RESULT=ok-sync
|
||||
else
|
||||
typeset -g VCS_STATUS_RESULT=ok-async
|
||||
fi
|
||||
for VCS_STATUS_WORKDIR \
|
||||
VCS_STATUS_COMMIT \
|
||||
VCS_STATUS_LOCAL_BRANCH \
|
||||
VCS_STATUS_REMOTE_BRANCH \
|
||||
VCS_STATUS_REMOTE_NAME \
|
||||
VCS_STATUS_REMOTE_URL \
|
||||
VCS_STATUS_ACTION \
|
||||
VCS_STATUS_INDEX_SIZE \
|
||||
VCS_STATUS_NUM_STAGED \
|
||||
VCS_STATUS_NUM_UNSTAGED \
|
||||
VCS_STATUS_NUM_CONFLICTED \
|
||||
VCS_STATUS_NUM_UNTRACKED \
|
||||
VCS_STATUS_COMMITS_AHEAD \
|
||||
VCS_STATUS_COMMITS_BEHIND \
|
||||
VCS_STATUS_STASHES \
|
||||
VCS_STATUS_TAG \
|
||||
VCS_STATUS_NUM_UNSTAGED_DELETED \
|
||||
VCS_STATUS_NUM_STAGED_NEW \
|
||||
VCS_STATUS_NUM_STAGED_DELETED \
|
||||
VCS_STATUS_PUSH_REMOTE_NAME \
|
||||
VCS_STATUS_PUSH_REMOTE_URL \
|
||||
VCS_STATUS_PUSH_COMMITS_AHEAD \
|
||||
VCS_STATUS_PUSH_COMMITS_BEHIND \
|
||||
VCS_STATUS_NUM_SKIP_WORKTREE \
|
||||
VCS_STATUS_NUM_ASSUME_UNCHANGED \
|
||||
VCS_STATUS_COMMIT_ENCODING \
|
||||
VCS_STATUS_COMMIT_SUMMARY in "${(@)resp[3,29]}"; do
|
||||
done
|
||||
typeset -gi VCS_STATUS_{INDEX_SIZE,NUM_STAGED,NUM_UNSTAGED,NUM_CONFLICTED,NUM_UNTRACKED,COMMITS_AHEAD,COMMITS_BEHIND,STASHES,NUM_UNSTAGED_DELETED,NUM_STAGED_NEW,NUM_STAGED_DELETED,PUSH_COMMITS_AHEAD,PUSH_COMMITS_BEHIND,NUM_SKIP_WORKTREE,NUM_ASSUME_UNCHANGED}
|
||||
typeset -gi VCS_STATUS_HAS_STAGED=$((VCS_STATUS_NUM_STAGED > 0))
|
||||
if (( dirty_max_index_size >= 0 && VCS_STATUS_INDEX_SIZE > dirty_max_index_size )); then
|
||||
typeset -gi \
|
||||
VCS_STATUS_HAS_UNSTAGED=-1 \
|
||||
VCS_STATUS_HAS_CONFLICTED=-1 \
|
||||
VCS_STATUS_HAS_UNTRACKED=-1
|
||||
else
|
||||
typeset -gi \
|
||||
VCS_STATUS_HAS_UNSTAGED=$((VCS_STATUS_NUM_UNSTAGED > 0)) \
|
||||
VCS_STATUS_HAS_CONFLICTED=$((VCS_STATUS_NUM_CONFLICTED > 0)) \
|
||||
VCS_STATUS_HAS_UNTRACKED=$((VCS_STATUS_NUM_UNTRACKED > 0))
|
||||
fi
|
||||
else
|
||||
if [[ $resp[1] == $req_id' '* ]]; then
|
||||
typeset -g VCS_STATUS_RESULT=norepo-sync
|
||||
else
|
||||
typeset -g VCS_STATUS_RESULT=norepo-async
|
||||
fi
|
||||
_gitstatus_clear$fsuf
|
||||
fi
|
||||
(( --_GITSTATUS_NUM_INFLIGHT_$name ))
|
||||
[[ $VCS_STATUS_RESULT == *-async ]] && emulate zsh -c "${resp[1]#* }"
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function _gitstatus_daemon"${1:-}"() {
|
||||
local -i pipe_fd
|
||||
exec 0<&- {pipe_fd}>&1 1>>$daemon_log 2>&1 || return
|
||||
local pgid=$sysparams[pid]
|
||||
[[ $pgid == <1-> ]] || return
|
||||
builtin cd -q / || return
|
||||
|
||||
{
|
||||
{
|
||||
trap '' PIPE
|
||||
|
||||
local uname_sm
|
||||
uname_sm="${${(L)$(command uname -sm)}//ı/i}" || return
|
||||
[[ $uname_sm == [^' ']##' '[^' ']## ]] || return
|
||||
local uname_s=${uname_sm% *}
|
||||
local uname_m=${uname_sm#* }
|
||||
|
||||
if [[ $GITSTATUS_NUM_THREADS == <1-> ]]; then
|
||||
args+=(-t $GITSTATUS_NUM_THREADS)
|
||||
else
|
||||
local cpus
|
||||
if (( ! $+commands[sysctl] )) || [[ $uname_s == linux ]] ||
|
||||
! cpus="$(command sysctl -n hw.ncpu)"; then
|
||||
if (( ! $+commands[getconf] )) || ! cpus="$(command getconf _NPROCESSORS_ONLN)"; then
|
||||
cpus=8
|
||||
fi
|
||||
fi
|
||||
args+=(-t $((cpus > 16 ? 32 : cpus > 0 ? 2 * cpus : 16)))
|
||||
fi
|
||||
|
||||
command mkfifo -- $file_prefix.fifo || return
|
||||
print -rnu $pipe_fd -- ${(l:20:)pgid} || return
|
||||
exec <$file_prefix.fifo || return
|
||||
zf_rm -- $file_prefix.fifo || return
|
||||
|
||||
local _gitstatus_zsh_daemon _gitstatus_zsh_version _gitstatus_zsh_downloaded
|
||||
|
||||
function _gitstatus_set_daemon$fsuf() {
|
||||
_gitstatus_zsh_daemon="$1"
|
||||
_gitstatus_zsh_version="$2"
|
||||
_gitstatus_zsh_downloaded="$3"
|
||||
}
|
||||
|
||||
local gitstatus_plugin_dir_var=_gitstatus_plugin_dir$fsuf
|
||||
local gitstatus_plugin_dir=${(P)gitstatus_plugin_dir_var}
|
||||
builtin set -- -d $gitstatus_plugin_dir -s $uname_s -m $uname_m \
|
||||
-p "printf '\\001' >&$pipe_fd" -e $pipe_fd -- _gitstatus_set_daemon$fsuf
|
||||
[[ ${GITSTATUS_AUTO_INSTALL:-1} == (|-|+)<1-> ]] || builtin set -- -n "$@"
|
||||
builtin source $gitstatus_plugin_dir/install || return
|
||||
[[ -n $_gitstatus_zsh_daemon ]] || return
|
||||
[[ -n $_gitstatus_zsh_version ]] || return
|
||||
[[ $_gitstatus_zsh_downloaded == [01] ]] || return
|
||||
|
||||
if (( UID == EUID )); then
|
||||
local home=~
|
||||
else
|
||||
local user
|
||||
user="$(command id -un)" || return
|
||||
local home=${userdirs[$user]}
|
||||
[[ -n $home ]] || return
|
||||
fi
|
||||
|
||||
if [[ -x $_gitstatus_zsh_daemon ]]; then
|
||||
HOME=$home $_gitstatus_zsh_daemon -G $_gitstatus_zsh_version "${(@)args}" >&$pipe_fd
|
||||
local -i ret=$?
|
||||
[[ $ret == (0|129|130|131|137|141|143|159) ]] && return ret
|
||||
fi
|
||||
|
||||
(( ! _gitstatus_zsh_downloaded )) || return
|
||||
[[ ${GITSTATUS_AUTO_INSTALL:-1} == (|-|+)<1-> ]] || return
|
||||
[[ $_gitstatus_zsh_daemon == \
|
||||
${GITSTATUS_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/gitstatus}/* ]] || return
|
||||
|
||||
builtin set -- -f "$@"
|
||||
_gitstatus_zsh_daemon=
|
||||
_gitstatus_zsh_version=
|
||||
_gitstatus_zsh_downloaded=
|
||||
builtin source $gitstatus_plugin_dir/install || return
|
||||
[[ -n $_gitstatus_zsh_daemon ]] || return
|
||||
[[ -n $_gitstatus_zsh_version ]] || return
|
||||
[[ $_gitstatus_zsh_downloaded == 1 ]] || return
|
||||
|
||||
HOME=$home $_gitstatus_zsh_daemon -G $_gitstatus_zsh_version "${(@)args}" >&$pipe_fd
|
||||
} always {
|
||||
local -i ret=$?
|
||||
zf_rm -f -- $file_prefix.lock $file_prefix.fifo
|
||||
kill -- -$pgid
|
||||
}
|
||||
} &!
|
||||
|
||||
(( lock_fd == -1 )) && return
|
||||
|
||||
{
|
||||
if zsystem flock -- $file_prefix.lock && [[ -e $file_prefix.lock ]]; then
|
||||
zf_rm -f -- $file_prefix.lock $file_prefix.fifo
|
||||
kill -- -$pgid
|
||||
fi
|
||||
} &!
|
||||
}
|
||||
|
||||
# Starts gitstatusd in the background. Does nothing and succeeds if gitstatusd is already running.
|
||||
#
|
||||
# Usage: gitstatus_start [OPTION]... NAME
|
||||
#
|
||||
# -t FLOAT Fail the self-check on initialization if not getting a response from gitstatusd for
|
||||
# this this many seconds. Defaults to 5.
|
||||
#
|
||||
# -s INT Report at most this many staged changes; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -u INT Report at most this many unstaged changes; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -c INT Report at most this many conflicted changes; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -d INT Report at most this many untracked files; negative value means infinity.
|
||||
# Defaults to 1.
|
||||
#
|
||||
# -m INT Report -1 unstaged, untracked and conflicted if there are more than this many
|
||||
# files in the index. Negative value means infinity. Defaults to -1.
|
||||
#
|
||||
# -e Count files within untracked directories like `git status --untracked-files`.
|
||||
#
|
||||
# -U Unless this option is specified, report zero untracked files for repositories
|
||||
# with status.showUntrackedFiles = false.
|
||||
#
|
||||
# -W Unless this option is specified, report zero untracked files for repositories
|
||||
# with bash.showUntrackedFiles = false.
|
||||
#
|
||||
# -D Unless this option is specified, report zero staged, unstaged and conflicted
|
||||
# changes for repositories with bash.showDirtyState = false.
|
||||
function gitstatus_start"${1:-}"() {
|
||||
emulate -L zsh -o no_aliases -o no_bg_nice -o extended_glob -o typeset_silent || return
|
||||
print -rnu2 || return
|
||||
|
||||
local fsuf=${${(%):-%N}#gitstatus_start}
|
||||
|
||||
local opt OPTARG
|
||||
local -i OPTIND
|
||||
local -F timeout=5
|
||||
local -i async=0
|
||||
local -a args=()
|
||||
local -i dirty_max_index_size=-1
|
||||
|
||||
while getopts ":t:s:u:c:d:m:eaUWD" opt; do
|
||||
case $opt in
|
||||
a) async=1;;
|
||||
+a) async=0;;
|
||||
t)
|
||||
if [[ $OPTARG != (|+)<->(|.<->)(|[eE](|-|+)<->) ]] || (( ${timeout::=OPTARG} <= 0 )); then
|
||||
print -ru2 -- "gitstatus_start: invalid -t argument: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
s|u|c|d|m)
|
||||
if [[ $OPTARG != (|-|+)<-> ]]; then
|
||||
print -ru2 -- "gitstatus_start: invalid -$opt argument: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
args+=(-$opt $OPTARG)
|
||||
[[ $opt == m ]] && dirty_max_index_size=OPTARG
|
||||
;;
|
||||
e|U|W|D) args+=-$opt;;
|
||||
+(e|U|W|D)) args=(${(@)args:#-$opt});;
|
||||
\?) print -ru2 -- "gitstatus_start: invalid option: $OPTARG" ; return 1;;
|
||||
:) print -ru2 -- "gitstatus_start: missing required argument: $OPTARG"; return 1;;
|
||||
*) print -ru2 -- "gitstatus_start: invalid option: $opt" ; return 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( OPTIND != ARGC )); then
|
||||
print -ru2 -- "gitstatus_start: exactly one positional argument is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local name=$*[OPTIND]
|
||||
if [[ $name != [[:IDENT:]]## ]]; then
|
||||
print -ru2 -- "gitstatus_start: invalid positional argument: $name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local -i lock_fd resp_fd stderr_fd
|
||||
local file_prefix xtrace=/dev/null daemon_log=/dev/null culprit
|
||||
|
||||
{
|
||||
if (( _GITSTATUS_STATE_$name )); then
|
||||
(( async )) && return
|
||||
(( _GITSTATUS_STATE_$name == 2 )) && return
|
||||
lock_fd=_GITSTATUS_LOCK_FD_$name
|
||||
resp_fd=_GITSTATUS_RESP_FD_$name
|
||||
xtrace=${(P)${:-GITSTATUS_XTRACE_$name}}
|
||||
daemon_log=${(P)${:-GITSTATUS_DAEMON_LOG_$name}}
|
||||
file_prefix=${(P)${:-_GITSTATUS_FILE_PREFIX_$name}}
|
||||
else
|
||||
typeset -gi _GITSTATUS_START_COUNTER
|
||||
local log_level=$GITSTATUS_LOG_LEVEL
|
||||
local file_prefix=${${TMPDIR:-/tmp}:A}/gitstatus.$name.$EUID
|
||||
file_prefix+=.$sysparams[pid].$EPOCHSECONDS.$((++_GITSTATUS_START_COUNTER))
|
||||
(( GITSTATUS_ENABLE_LOGGING )) && : ${log_level:=INFO}
|
||||
if [[ -n $log_level ]]; then
|
||||
xtrace=$file_prefix.xtrace.log
|
||||
daemon_log=$file_prefix.daemon.log
|
||||
fi
|
||||
args+=(-v ${log_level:-FATAL})
|
||||
typeset -g GITSTATUS_XTRACE_$name=$xtrace
|
||||
typeset -g GITSTATUS_DAEMON_LOG_$name=$daemon_log
|
||||
typeset -g _GITSTATUS_FILE_PREFIX_$name=$file_prefix
|
||||
typeset -gi _GITSTATUS_CLIENT_PID_$name="sysparams[pid]"
|
||||
typeset -gi _GITSTATUS_DIRTY_MAX_INDEX_SIZE_$name=dirty_max_index_size
|
||||
fi
|
||||
|
||||
() {
|
||||
if [[ $xtrace != /dev/null && -o no_xtrace ]]; then
|
||||
exec {stderr_fd}>&2 || return
|
||||
exec 2>>$xtrace || return
|
||||
setopt xtrace
|
||||
fi
|
||||
|
||||
setopt monitor || return
|
||||
|
||||
if (( ! _GITSTATUS_STATE_$name )); then
|
||||
if [[ -r /proc/version && "$(</proc/version)" == *Microsoft* ]]; then
|
||||
lock_fd=-1
|
||||
else
|
||||
print -rn >$file_prefix.lock || return
|
||||
zsystem flock -f lock_fd $file_prefix.lock || return
|
||||
[[ $lock_fd == <1-> ]] || return
|
||||
fi
|
||||
|
||||
typeset -gi _GITSTATUS_LOCK_FD_$name=lock_fd
|
||||
|
||||
if [[ $OSTYPE == cygwin* && -d /proc/self/fd ]]; then
|
||||
# Work around bugs in Cygwin 32-bit.
|
||||
#
|
||||
# This hangs:
|
||||
#
|
||||
# emulate -L zsh
|
||||
# () { exec {fd}< $1 } <(:)
|
||||
# =true # hangs here
|
||||
#
|
||||
# This hangs:
|
||||
#
|
||||
# sysopen -r -u fd <(:)
|
||||
local -i fd
|
||||
exec {fd}< <(_gitstatus_daemon$fsuf) || return
|
||||
{
|
||||
[[ -r /proc/self/fd/$fd ]] || return
|
||||
sysopen -r -o cloexec -u resp_fd /proc/self/fd/$fd || return
|
||||
} always {
|
||||
exec {fd} >&- || return
|
||||
}
|
||||
else
|
||||
sysopen -r -o cloexec -u resp_fd <(_gitstatus_daemon$fsuf) || return
|
||||
fi
|
||||
|
||||
typeset -gi GITSTATUS_DAEMON_PID_$name="${sysparams[procsubstpid]:--1}"
|
||||
|
||||
[[ $resp_fd == <1-> ]] || return
|
||||
typeset -gi _GITSTATUS_RESP_FD_$name=resp_fd
|
||||
typeset -gi _GITSTATUS_STATE_$name=1
|
||||
fi
|
||||
|
||||
if (( ! async )); then
|
||||
(( _GITSTATUS_CLIENT_PID_$name == sysparams[pid] )) || return
|
||||
|
||||
local pgid
|
||||
while (( $#pgid < 20 )); do
|
||||
[[ -t $resp_fd ]]
|
||||
sysread -s $((20 - $#pgid)) -t $timeout -i $resp_fd 'pgid[$#pgid+1]' || return
|
||||
done
|
||||
[[ $pgid == ' '#<1-> ]] || return
|
||||
typeset -gi GITSTATUS_DAEMON_PID_$name=pgid
|
||||
|
||||
sysopen -w -o cloexec -u req_fd -- $file_prefix.fifo || return
|
||||
[[ $req_fd == <1-> ]] || return
|
||||
typeset -gi _GITSTATUS_REQ_FD_$name=req_fd
|
||||
|
||||
print -nru $req_fd -- $'}hello\x1f\x1e' || return
|
||||
local expected=$'}hello\x1f0\x1e' actual
|
||||
if (( $+functions[p10k] )) && [[ ! -t 1 && ! -t 0 ]]; then
|
||||
local -F deadline='EPOCHREALTIME + 4'
|
||||
else
|
||||
local -F deadline='1'
|
||||
fi
|
||||
while true; do
|
||||
[[ -t $resp_fd ]]
|
||||
sysread -s 1 -t $timeout -i $resp_fd actual || return
|
||||
[[ $expected == $actual* ]] && break
|
||||
if [[ $actual != $'\1' ]]; then
|
||||
[[ -t $resp_fd ]]
|
||||
while sysread -t $timeout -i $resp_fd 'actual[$#actual+1]'; do
|
||||
[[ -t $resp_fd ]]
|
||||
done
|
||||
culprit=$actual
|
||||
return 1
|
||||
fi
|
||||
(( EPOCHREALTIME < deadline )) && continue
|
||||
if (( deadline > 0 )); then
|
||||
deadline=0
|
||||
if (( stderr_fd )); then
|
||||
unsetopt xtrace
|
||||
exec 2>&$stderr_fd {stderr_fd}>&-
|
||||
stderr_fd=0
|
||||
fi
|
||||
if (( $+functions[p10k] )); then
|
||||
p10k clear-instant-prompt || return
|
||||
fi
|
||||
if [[ $name == POWERLEVEL9K ]]; then
|
||||
local label=powerlevel10k
|
||||
else
|
||||
local label=gitstatus
|
||||
fi
|
||||
if [[ -t 2 ]]; then
|
||||
local spinner=($'\b%3F-%f' $'\b%3F\\%f' $'\b%3F|%f' $'\b%3F/%f')
|
||||
print -Prnu2 -- "[%3F$label%f] fetching %2Fgitstatusd%f .. "
|
||||
else
|
||||
local spinner=('.')
|
||||
print -rnu2 -- "[$label] fetching gitstatusd .."
|
||||
fi
|
||||
fi
|
||||
print -Prnu2 -- $spinner[1]
|
||||
spinner=($spinner[2,-1] $spinner[1])
|
||||
done
|
||||
|
||||
if (( deadline == 0 )); then
|
||||
if [[ -t 2 ]]; then
|
||||
print -Pru2 -- $'\b[%2Fok%f]'
|
||||
else
|
||||
print -ru2 -- ' [ok]'
|
||||
fi
|
||||
if [[ $xtrace != /dev/null && -o no_xtrace ]]; then
|
||||
exec {stderr_fd}>&2 || return
|
||||
exec 2>>$xtrace || return
|
||||
setopt xtrace
|
||||
fi
|
||||
fi
|
||||
|
||||
while (( $#actual < $#expected )); do
|
||||
[[ -t $resp_fd ]]
|
||||
sysread -s $(($#expected - $#actual)) -t $timeout -i $resp_fd 'actual[$#actual+1]' || return
|
||||
done
|
||||
[[ $actual == $expected ]] || return
|
||||
|
||||
function _gitstatus_process_response_$name-$fsuf() {
|
||||
emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
|
||||
local pair=${${(%):-%N}#_gitstatus_process_response_}
|
||||
local name=${pair%%-*}
|
||||
local fsuf=${pair#*-}
|
||||
[[ $name == POWERLEVEL9K && $fsuf == _p9k_ ]] && eval $__p9k_intro_base
|
||||
if (( ARGC == 1 )); then
|
||||
_gitstatus_process_response$fsuf $name 0 ''
|
||||
else
|
||||
gitstatus_stop$fsuf $name
|
||||
fi
|
||||
}
|
||||
if ! zle -F $resp_fd _gitstatus_process_response_$name-$fsuf; then
|
||||
unfunction _gitstatus_process_response_$name-$fsuf
|
||||
return 1
|
||||
fi
|
||||
|
||||
function _gitstatus_cleanup_$name-$fsuf() {
|
||||
emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
|
||||
local pair=${${(%):-%N}#_gitstatus_cleanup_}
|
||||
local name=${pair%%-*}
|
||||
local fsuf=${pair#*-}
|
||||
(( _GITSTATUS_CLIENT_PID_$name == sysparams[pid] )) || return
|
||||
gitstatus_stop$fsuf $name
|
||||
}
|
||||
if ! add-zsh-hook zshexit _gitstatus_cleanup_$name-$fsuf; then
|
||||
unfunction _gitstatus_cleanup_$name-$fsuf
|
||||
return 1
|
||||
fi
|
||||
|
||||
if (( lock_fd != -1 )); then
|
||||
zf_rm -- $file_prefix.lock || return
|
||||
zsystem flock -u $lock_fd || return
|
||||
fi
|
||||
unset _GITSTATUS_LOCK_FD_$name
|
||||
|
||||
typeset -gi _GITSTATUS_STATE_$name=2
|
||||
fi
|
||||
}
|
||||
} always {
|
||||
local -i err=$?
|
||||
(( stderr_fd )) && exec 2>&$stderr_fd {stderr_fd}>&-
|
||||
(( err == 0 )) && return
|
||||
|
||||
gitstatus_stop$fsuf $name
|
||||
|
||||
setopt prompt_percent no_prompt_subst no_prompt_bang
|
||||
(( $+functions[p10k] )) && p10k clear-instant-prompt
|
||||
print -ru2 -- ''
|
||||
print -Pru2 -- '[%F{red}ERROR%f]: gitstatus failed to initialize.'
|
||||
print -ru2 -- ''
|
||||
if [[ -n $culprit ]]; then
|
||||
print -ru2 -- $culprit
|
||||
return err
|
||||
fi
|
||||
if [[ -s $xtrace ]]; then
|
||||
print -ru2 -- ''
|
||||
print -Pru2 -- " Zsh log (%U${xtrace//\%/%%}%u):"
|
||||
print -Pru2 -- '%F{yellow}'
|
||||
print -lru2 -- "${(@)${(@f)$(<$xtrace)}/#/ }"
|
||||
print -Pnru2 -- '%f'
|
||||
fi
|
||||
if [[ -s $daemon_log ]]; then
|
||||
print -ru2 -- ''
|
||||
print -Pru2 -- " Daemon log (%U${daemon_log//\%/%%}%u):"
|
||||
print -Pru2 -- '%F{yellow}'
|
||||
print -lru2 -- "${(@)${(@f)$(<$daemon_log)}/#/ }"
|
||||
print -Pnru2 -- '%f'
|
||||
fi
|
||||
if [[ $GITSTATUS_LOG_LEVEL == DEBUG ]]; then
|
||||
print -ru2 -- ''
|
||||
print -ru2 -- ' System information:'
|
||||
print -Pru2 -- '%F{yellow}'
|
||||
print -ru2 -- " zsh: $ZSH_VERSION"
|
||||
print -ru2 -- " uname -a: $(command uname -a)"
|
||||
print -Pru2 -- '%f'
|
||||
print -ru2 -- ' If you need help, open an issue and attach this whole error message to it:'
|
||||
print -ru2 -- ''
|
||||
print -Pru2 -- ' %Uhttps://github.com/romkatv/gitstatus/issues/new%u'
|
||||
else
|
||||
print -ru2 -- ''
|
||||
local home=~
|
||||
local zshrc=${${${(q)${ZDOTDIR:-~}}/#${(q)home}/'~'}//\%/%%}/.zshrc
|
||||
print -Pru2 -- " Add the following parameter to %U$zshrc%u for extra diagnostics on error:"
|
||||
print -ru2 -- ''
|
||||
print -Pru2 -- ' %BGITSTATUS_LOG_LEVEL=DEBUG%b'
|
||||
print -ru2 -- ''
|
||||
print -ru2 -- ' Restart Zsh to retry gitstatus initialization:'
|
||||
print -ru2 -- ''
|
||||
print -Pru2 -- ' %F{green}%Uexec%u zsh%f'
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
# Stops gitstatusd if it's running.
|
||||
#
|
||||
# Usage: gitstatus_stop NAME.
|
||||
function gitstatus_stop"${1:-}"() {
|
||||
emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
|
||||
|
||||
local fsuf=${${(%):-%N}#gitstatus_stop}
|
||||
|
||||
if (( ARGC != 1 )); then
|
||||
print -ru2 -- "gitstatus_stop: exactly one positional argument is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local name=$1
|
||||
if [[ $name != [[:IDENT:]]## ]]; then
|
||||
print -ru2 -- "gitstatus_stop: invalid positional argument: $name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local state_var=_GITSTATUS_STATE_$name
|
||||
local req_fd_var=_GITSTATUS_REQ_FD_$name
|
||||
local resp_fd_var=_GITSTATUS_RESP_FD_$name
|
||||
local lock_fd_var=_GITSTATUS_LOCK_FD_$name
|
||||
local client_pid_var=_GITSTATUS_CLIENT_PID_$name
|
||||
local daemon_pid_var=GITSTATUS_DAEMON_PID_$name
|
||||
local inflight_var=_GITSTATUS_NUM_INFLIGHT_$name
|
||||
local file_prefix_var=_GITSTATUS_FILE_PREFIX_$name
|
||||
local dirty_max_index_size_var=_GITSTATUS_DIRTY_MAX_INDEX_SIZE_$name
|
||||
|
||||
local req_fd=${(P)req_fd_var}
|
||||
local resp_fd=${(P)resp_fd_var}
|
||||
local lock_fd=${(P)lock_fd_var}
|
||||
local daemon_pid=${(P)daemon_pid_var}
|
||||
local file_prefix=${(P)file_prefix_var}
|
||||
|
||||
local cleanup=_gitstatus_cleanup_$name-$fsuf
|
||||
local process=_gitstatus_process_response_$name-$fsuf
|
||||
|
||||
if (( $+functions[$cleanup] )); then
|
||||
add-zsh-hook -d zshexit $cleanup
|
||||
unfunction -- $cleanup
|
||||
fi
|
||||
|
||||
if (( $+functions[$process] )); then
|
||||
[[ -n $resp_fd ]] && zle -F $resp_fd
|
||||
unfunction -- $process
|
||||
fi
|
||||
|
||||
[[ $daemon_pid == <1-> ]] && kill -- -$daemon_pid 2>/dev/null
|
||||
[[ $file_prefix == /* ]] && zf_rm -f -- $file_prefix.lock $file_prefix.fifo
|
||||
[[ $lock_fd == <1-> ]] && zsystem flock -u $lock_fd
|
||||
[[ $req_fd == <1-> ]] && exec {req_fd}>&-
|
||||
[[ $resp_fd == <1-> ]] && exec {resp_fd}>&-
|
||||
|
||||
unset $state_var $req_fd_var $lock_fd_var $resp_fd_var $client_pid_var $daemon_pid_var
|
||||
unset $inflight_var $file_prefix_var $dirty_max_index_size_var
|
||||
|
||||
unset VCS_STATUS_RESULT
|
||||
_gitstatus_clear$fsuf
|
||||
}
|
||||
|
||||
# Usage: gitstatus_check NAME.
|
||||
#
|
||||
# Returns 0 if and only if `gitstatus_start NAME` has succeeded previously.
|
||||
# If it returns non-zero, gitstatus_query NAME is guaranteed to return non-zero.
|
||||
function gitstatus_check"${1:-}"() {
|
||||
emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
|
||||
|
||||
local fsuf=${${(%):-%N}#gitstatus_check}
|
||||
|
||||
if (( ARGC != 1 )); then
|
||||
print -ru2 -- "gitstatus_check: exactly one positional argument is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local name=$1
|
||||
if [[ $name != [[:IDENT:]]## ]]; then
|
||||
print -ru2 -- "gitstatus_check: invalid positional argument: $name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
(( _GITSTATUS_STATE_$name == 2 ))
|
||||
}
|
||||
|
||||
(( ${#_gitstatus_opts} )) && setopt ${_gitstatus_opts[@]}
|
||||
'builtin' 'unset' '_gitstatus_opts'
|
@ -0,0 +1,104 @@
|
||||
# Simple Bash prompt with Git status.
|
||||
|
||||
# Source gitstatus.plugin.sh from $GITSTATUS_DIR or from the same directory
|
||||
# in which the current script resides if the variable isn't set.
|
||||
if [[ -n "${GITSTATUS_DIR:-}" ]]; then
|
||||
source "$GITSTATUS_DIR" || return
|
||||
elif [[ "${BASH_SOURCE[0]}" == */* ]]; then
|
||||
source "${BASH_SOURCE[0]%/*}/gitstatus.plugin.sh" || return
|
||||
else
|
||||
source gitstatus.plugin.sh || return
|
||||
fi
|
||||
|
||||
# Sets GITSTATUS_PROMPT to reflect the state of the current git repository.
|
||||
# The value is empty if not in a git repository. Forwards all arguments to
|
||||
# gitstatus_query.
|
||||
#
|
||||
# Example value of GITSTATUS_PROMPT: master ⇣42⇡42 ⇠42⇢42 *42 merge ~42 +42 !42 ?42
|
||||
#
|
||||
# master current branch
|
||||
# ⇣42 local branch is 42 commits behind the remote
|
||||
# ⇡42 local branch is 42 commits ahead of the remote
|
||||
# ⇠42 local branch is 42 commits behind the push remote
|
||||
# ⇢42 local branch is 42 commits ahead of the push remote
|
||||
# *42 42 stashes
|
||||
# merge merge in progress
|
||||
# ~42 42 merge conflicts
|
||||
# +42 42 staged changes
|
||||
# !42 42 unstaged changes
|
||||
# ?42 42 untracked files
|
||||
function gitstatus_prompt_update() {
|
||||
GITSTATUS_PROMPT=""
|
||||
|
||||
gitstatus_query "$@" || return 1 # error
|
||||
[[ "$VCS_STATUS_RESULT" == ok-sync ]] || return 0 # not a git repo
|
||||
|
||||
local reset=$'\001\e[0m\002' # no color
|
||||
local clean=$'\001\e[38;5;076m\002' # green foreground
|
||||
local untracked=$'\001\e[38;5;014m\002' # teal foreground
|
||||
local modified=$'\001\e[38;5;011m\002' # yellow foreground
|
||||
local conflicted=$'\001\e[38;5;196m\002' # red foreground
|
||||
|
||||
local p
|
||||
|
||||
local where # branch name, tag or commit
|
||||
if [[ -n "$VCS_STATUS_LOCAL_BRANCH" ]]; then
|
||||
where="$VCS_STATUS_LOCAL_BRANCH"
|
||||
elif [[ -n "$VCS_STATUS_TAG" ]]; then
|
||||
p+="${reset}#"
|
||||
where="$VCS_STATUS_TAG"
|
||||
else
|
||||
p+="${reset}@"
|
||||
where="${VCS_STATUS_COMMIT:0:8}"
|
||||
fi
|
||||
|
||||
(( ${#where} > 32 )) && where="${where:0:12}…${where: -12}" # truncate long branch names and tags
|
||||
p+="${clean}${where}"
|
||||
|
||||
# ⇣42 if behind the remote.
|
||||
(( VCS_STATUS_COMMITS_BEHIND )) && p+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}"
|
||||
# ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42.
|
||||
(( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && p+=" "
|
||||
(( VCS_STATUS_COMMITS_AHEAD )) && p+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}"
|
||||
# ⇠42 if behind the push remote.
|
||||
(( VCS_STATUS_PUSH_COMMITS_BEHIND )) && p+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}"
|
||||
(( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && p+=" "
|
||||
# ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42.
|
||||
(( VCS_STATUS_PUSH_COMMITS_AHEAD )) && p+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}"
|
||||
# *42 if have stashes.
|
||||
(( VCS_STATUS_STASHES )) && p+=" ${clean}*${VCS_STATUS_STASHES}"
|
||||
# 'merge' if the repo is in an unusual state.
|
||||
[[ -n "$VCS_STATUS_ACTION" ]] && p+=" ${conflicted}${VCS_STATUS_ACTION}"
|
||||
# ~42 if have merge conflicts.
|
||||
(( VCS_STATUS_NUM_CONFLICTED )) && p+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}"
|
||||
# +42 if have staged changes.
|
||||
(( VCS_STATUS_NUM_STAGED )) && p+=" ${modified}+${VCS_STATUS_NUM_STAGED}"
|
||||
# !42 if have unstaged changes.
|
||||
(( VCS_STATUS_NUM_UNSTAGED )) && p+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}"
|
||||
# ?42 if have untracked files. It's really a question mark, your font isn't broken.
|
||||
(( VCS_STATUS_NUM_UNTRACKED )) && p+=" ${untracked}?${VCS_STATUS_NUM_UNTRACKED}"
|
||||
|
||||
GITSTATUS_PROMPT="${p}${reset}"
|
||||
}
|
||||
|
||||
# Start gitstatusd in the background.
|
||||
gitstatus_stop && gitstatus_start -s -1 -u -1 -c -1 -d -1
|
||||
|
||||
# On every prompt, fetch git status and set GITSTATUS_PROMPT.
|
||||
PROMPT_COMMAND=gitstatus_prompt_update
|
||||
PROMPT_DIRTRIM=3
|
||||
|
||||
# Enable promptvars so that ${GITSTATUS_PROMPT} in PS1 is expanded.
|
||||
shopt -s promptvars
|
||||
|
||||
# Customize prompt. Put $GITSTATUS_PROMPT in it reflect git status.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# user@host ~/projects/skynet master ⇡42
|
||||
# $ █
|
||||
PS1='\[\033[01;32m\]\u@\h\[\033[00m\] ' # green user@host
|
||||
PS1+='\[\033[01;34m\]\w\[\033[00m\]' # blue current working directory
|
||||
PS1+='${GITSTATUS_PROMPT:+ $GITSTATUS_PROMPT}' # git status (requires promptvars option)
|
||||
PS1+='\n\[\033[01;$((31+!$?))m\]\$\[\033[00m\] ' # green/red (success/error) $/# (normal/root)
|
||||
PS1+='\[\e]0;\u@\h: \w\a\]' # terminal title: user@host: dir
|
@ -0,0 +1,111 @@
|
||||
# Simple Zsh prompt with Git status.
|
||||
|
||||
# Source gitstatus.plugin.zsh from $GITSTATUS_DIR or from the same directory
|
||||
# in which the current script resides if the variable isn't set.
|
||||
source "${GITSTATUS_DIR:-${${(%):-%x}:h}}/gitstatus.plugin.zsh" || return
|
||||
|
||||
# Sets GITSTATUS_PROMPT to reflect the state of the current git repository. Empty if not
|
||||
# in a git repository. In addition, sets GITSTATUS_PROMPT_LEN to the number of columns
|
||||
# $GITSTATUS_PROMPT will occupy when printed.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# GITSTATUS_PROMPT='master ⇣42⇡42 ⇠42⇢42 *42 merge ~42 +42 !42 ?42'
|
||||
# GITSTATUS_PROMPT_LEN=39
|
||||
#
|
||||
# master current branch
|
||||
# ⇣42 local branch is 42 commits behind the remote
|
||||
# ⇡42 local branch is 42 commits ahead of the remote
|
||||
# ⇠42 local branch is 42 commits behind the push remote
|
||||
# ⇢42 local branch is 42 commits ahead of the push remote
|
||||
# *42 42 stashes
|
||||
# merge merge in progress
|
||||
# ~42 42 merge conflicts
|
||||
# +42 42 staged changes
|
||||
# !42 42 unstaged changes
|
||||
# ?42 42 untracked files
|
||||
function gitstatus_prompt_update() {
|
||||
emulate -L zsh
|
||||
typeset -g GITSTATUS_PROMPT=''
|
||||
typeset -gi GITSTATUS_PROMPT_LEN=0
|
||||
|
||||
# Call gitstatus_query synchronously. Note that gitstatus_query can also be called
|
||||
# asynchronously; see documentation in gitstatus.plugin.zsh.
|
||||
gitstatus_query 'MY' || return 1 # error
|
||||
[[ $VCS_STATUS_RESULT == 'ok-sync' ]] || return 0 # not a git repo
|
||||
|
||||
local clean='%76F' # green foreground
|
||||
local modified='%178F' # yellow foreground
|
||||
local untracked='%39F' # blue foreground
|
||||
local conflicted='%196F' # red foreground
|
||||
|
||||
local p
|
||||
|
||||
local where # branch name, tag or commit
|
||||
if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then
|
||||
where=$VCS_STATUS_LOCAL_BRANCH
|
||||
elif [[ -n $VCS_STATUS_TAG ]]; then
|
||||
p+='%f#'
|
||||
where=$VCS_STATUS_TAG
|
||||
else
|
||||
p+='%f@'
|
||||
where=${VCS_STATUS_COMMIT[1,8]}
|
||||
fi
|
||||
|
||||
(( $#where > 32 )) && where[13,-13]="…" # truncate long branch names and tags
|
||||
p+="${clean}${where//\%/%%}" # escape %
|
||||
|
||||
# ⇣42 if behind the remote.
|
||||
(( VCS_STATUS_COMMITS_BEHIND )) && p+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}"
|
||||
# ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42.
|
||||
(( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && p+=" "
|
||||
(( VCS_STATUS_COMMITS_AHEAD )) && p+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}"
|
||||
# ⇠42 if behind the push remote.
|
||||
(( VCS_STATUS_PUSH_COMMITS_BEHIND )) && p+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}"
|
||||
(( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && p+=" "
|
||||
# ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42.
|
||||
(( VCS_STATUS_PUSH_COMMITS_AHEAD )) && p+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}"
|
||||
# *42 if have stashes.
|
||||
(( VCS_STATUS_STASHES )) && p+=" ${clean}*${VCS_STATUS_STASHES}"
|
||||
# 'merge' if the repo is in an unusual state.
|
||||
[[ -n $VCS_STATUS_ACTION ]] && p+=" ${conflicted}${VCS_STATUS_ACTION}"
|
||||
# ~42 if have merge conflicts.
|
||||
(( VCS_STATUS_NUM_CONFLICTED )) && p+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}"
|
||||
# +42 if have staged changes.
|
||||
(( VCS_STATUS_NUM_STAGED )) && p+=" ${modified}+${VCS_STATUS_NUM_STAGED}"
|
||||
# !42 if have unstaged changes.
|
||||
(( VCS_STATUS_NUM_UNSTAGED )) && p+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}"
|
||||
# ?42 if have untracked files. It's really a question mark, your font isn't broken.
|
||||
(( VCS_STATUS_NUM_UNTRACKED )) && p+=" ${untracked}?${VCS_STATUS_NUM_UNTRACKED}"
|
||||
|
||||
GITSTATUS_PROMPT="${p}%f"
|
||||
|
||||
# The length of GITSTATUS_PROMPT after removing %f and %F.
|
||||
GITSTATUS_PROMPT_LEN="${(m)#${${GITSTATUS_PROMPT//\%\%/x}//\%(f|<->F)}}"
|
||||
}
|
||||
|
||||
# Start gitstatusd instance with name "MY". The same name is passed to
|
||||
# gitstatus_query in gitstatus_prompt_update. The flags with -1 as values
|
||||
# enable staged, unstaged, conflicted and untracked counters.
|
||||
gitstatus_stop 'MY' && gitstatus_start -s -1 -u -1 -c -1 -d -1 'MY'
|
||||
|
||||
# On every prompt, fetch git status and set GITSTATUS_PROMPT.
|
||||
autoload -Uz add-zsh-hook
|
||||
add-zsh-hook precmd gitstatus_prompt_update
|
||||
|
||||
# Enable/disable the right prompt options.
|
||||
setopt no_prompt_bang prompt_percent prompt_subst
|
||||
|
||||
# Customize prompt. Put $GITSTATUS_PROMPT in it to reflect git status.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# user@host ~/projects/skynet master ⇡42
|
||||
# % █
|
||||
#
|
||||
# The current directory gets truncated from the left if the whole prompt doesn't fit on the line.
|
||||
PROMPT='%70F%n@%m%f ' # green user@host
|
||||
PROMPT+='%39F%$((-GITSTATUS_PROMPT_LEN-1))<…<%~%<<%f' # blue current working directory
|
||||
PROMPT+='${GITSTATUS_PROMPT:+ $GITSTATUS_PROMPT}' # git status
|
||||
PROMPT+=$'\n' # new line
|
||||
PROMPT+='%F{%(?.76.196)}%#%f ' # %/# (normal/root); green/red (ok/error)
|
471
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/install
Executable file
471
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/install
Executable file
@ -0,0 +1,471 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script does not have a stable API.
|
||||
|
||||
_gitstatus_install_daemon_found() {
|
||||
local installed="$1"
|
||||
shift
|
||||
[ $# = 0 ] || "$@" "$daemon" "$version" "$installed"
|
||||
}
|
||||
|
||||
_gitstatus_install_main() {
|
||||
if [ -n "${ZSH_VERSION:-}" ]; then
|
||||
emulate -L sh -o no_unset
|
||||
else
|
||||
set -u
|
||||
fi
|
||||
|
||||
local argv1="$1"
|
||||
shift
|
||||
|
||||
local no_check= no_install= uname_s= uname_m= gitstatus_dir= dl_status= e=
|
||||
local opt= OPTARG= OPTIND=1
|
||||
|
||||
while getopts ':s:m:d:p:e:fnh' opt "$@"; do
|
||||
case "$opt" in
|
||||
h)
|
||||
command cat <<\END
|
||||
Usage: install [-s KERNEL] [-m ARCH] [-d DIR] [-p CMD] [-e ERRFD] [-f|-n] [-- CMD [ARG]...]
|
||||
|
||||
If positional arguments are specified, call this on success:
|
||||
|
||||
CMD [ARG]... DAEMON VERSION INSTALLED
|
||||
|
||||
DAEMON is path to gitstatusd. VERSION is a glob pattern for the
|
||||
version this daemon should support; it's supposed to be passed as
|
||||
-G to gitstatusd. INSTALLED is 1 if gitstatusd has just been
|
||||
downloaded and 0 otherwise.
|
||||
|
||||
Options:
|
||||
|
||||
-s KERNEL use this instead of lowercase `uname -s`
|
||||
-m ARCH use this instead of lowercase `uname -m`
|
||||
-d DIR use this instead of `dirname "$0"`
|
||||
-p CMD eval this every second while downloading gitstatusd
|
||||
-e ERRFD write error messages to this file descriptor
|
||||
-f download gitstatusd even if there is one locally
|
||||
-n do not download gitstatusd (fail instead)
|
||||
END
|
||||
return
|
||||
;;
|
||||
n)
|
||||
if [ -n "$no_install" ]; then
|
||||
>&2 echo "[gitstatus] error: duplicate option: -$opt"
|
||||
return 1
|
||||
fi
|
||||
no_install=1
|
||||
;;
|
||||
f)
|
||||
if [ -n "$no_check" ]; then
|
||||
>&2 echo "[gitstatus] error: duplicate option: -$opt"
|
||||
return 1
|
||||
fi
|
||||
no_check=1
|
||||
;;
|
||||
d)
|
||||
if [ -n "$gitstatus_dir" ]; then
|
||||
>&2 echo "[gitstatus] error: duplicate option: -$opt"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
gitstatus_dir="$OPTARG"
|
||||
;;
|
||||
p)
|
||||
if [ -n "$dl_status" ]; then
|
||||
>&2 echo "[gitstatus] error: duplicate option: -$opt"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
dl_status="$OPTARG"
|
||||
;;
|
||||
e)
|
||||
if [ -n "$e" ]; then
|
||||
>&2 echo "[gitstatus] error: duplicate option: -$opt"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
e="$OPTARG"
|
||||
;;
|
||||
m)
|
||||
if [ -n "$uname_m" ]; then
|
||||
>&2 echo "[gitstatus] error: duplicate option: -$opt"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
uname_m="$OPTARG"
|
||||
;;
|
||||
s)
|
||||
if [ -n "$uname_s" ]; then
|
||||
>&2 echo "[gitstatus] error: duplicate option: -$opt"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$OPTARG" ]; then
|
||||
>&2 echo "[error] incorrect value of -$opt: $OPTARG"
|
||||
return 1
|
||||
fi
|
||||
uname_s="$OPTARG"
|
||||
;;
|
||||
\?) >&2 echo "[gitstatus] error: invalid option: -$OPTARG" ; return 1;;
|
||||
:) >&2 echo "[gitstatus] error: missing required argument: -$OPTARG"; return 1;;
|
||||
*) >&2 echo "[gitstatus] internal error: unhandled option: -$opt" ; return 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift "$((OPTIND - 1))"
|
||||
|
||||
: "${e:=2}"
|
||||
: "${gitstatus_dir:=$argv1}"
|
||||
|
||||
if [ -n "$no_check" -a -n "$no_install" ]; then
|
||||
>&2 echo "[gitstatus] error: incompatible options: -f, -n"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$uname_s" ]; then
|
||||
uname_s="$(command uname -s)" || return
|
||||
uname_s="$(printf '%s' "$uname_s" | command tr '[A-Z]' '[a-z]')" || return
|
||||
fi
|
||||
if [ -z "$uname_m" ]; then
|
||||
uname_m="$(command uname -m)" || return
|
||||
uname_m="$(printf '%s' "$uname_m" | command tr '[A-Z]' '[a-z]')" || return
|
||||
fi
|
||||
|
||||
local daemon="${GITSTATUS_DAEMON:-}"
|
||||
local cache_dir="${GITSTATUS_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/gitstatus}"
|
||||
|
||||
if [ -z "$no_check" ]; then
|
||||
if [ -n "${daemon##/*}" ]; then
|
||||
>&2 echo "[gitstatus] error: GITSTATUS_DAEMON is not absolute path: $daemon"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$daemon" -a -e "$gitstatus_dir"/usrbin/gitstatusd ]; then
|
||||
daemon="$gitstatus_dir"/usrbin/gitstatusd
|
||||
fi
|
||||
if [ -n "$daemon" ]; then
|
||||
local gitstatus_version= libgit2_version=
|
||||
if ! . "$gitstatus_dir"/build.info; then
|
||||
>&2 echo "[gitstatus] internal error: failed to source build.info"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$gitstatus_version" ]; then
|
||||
>&2 echo "[gitstatus] internal error: empty gitstatus_version in build.info"
|
||||
return 1
|
||||
fi
|
||||
local version="$gitstatus_version"
|
||||
_gitstatus_install_daemon_found 0 "$@"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
while IFS= read -r line; do
|
||||
line="${line###*}"
|
||||
[ -n "$line" ] || continue
|
||||
|
||||
local uname_s_glob= uname_m_glob= file= version= sha256=
|
||||
eval "$line" || return
|
||||
|
||||
if [ -z "$uname_s_glob" -o \
|
||||
-z "$uname_m_glob" -o \
|
||||
-z "$file" -o \
|
||||
-z "$version" -o \
|
||||
-z "$sha256" ]; then
|
||||
>&2 echo "[gitstatus] internal error: invalid install.info line: $line"
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$uname_s" in
|
||||
$uname_s_glob) ;;
|
||||
*) continue;;
|
||||
esac
|
||||
case "$uname_m" in
|
||||
$uname_m_glob) ;;
|
||||
*) continue;;
|
||||
esac
|
||||
|
||||
# Found a match. The while loop will terminate during this iteration.
|
||||
|
||||
if [ -z "$no_check" ]; then
|
||||
# Check if a suitable gitstatusd already exists.
|
||||
local daemon="$gitstatus_dir"/usrbin/"$file"
|
||||
if [ ! -e "$daemon" ]; then
|
||||
daemon="$cache_dir"/"$file"
|
||||
[ -e "$daemon" ] || daemon=
|
||||
fi
|
||||
if [ -n "$daemon" ]; then
|
||||
_gitstatus_install_daemon_found 0 "$@"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# No suitable gitstatusd exists. Need to download.
|
||||
|
||||
if [ -n "$no_install" ]; then
|
||||
>&2 echo "[gitstatus] error: no gitstatusd found and installation is disabled"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local daemon="$cache_dir"/"$file"
|
||||
|
||||
if [ -n "${cache_dir##/*}" ]; then
|
||||
>&2 echo "[gitstatus] error: GITSTATUS_CACHE_DIR is not absolute: $cache_dir"
|
||||
return 1
|
||||
fi
|
||||
if [ ! -d "$cache_dir" ] && ! mkdir -p -- "$cache_dir" || [ ! -w "$cache_dir" ]; then
|
||||
local dir="$cache_dir"
|
||||
while true; do
|
||||
if [ -e "$dir" ]; then
|
||||
if [ ! -d "$dir" ]; then
|
||||
>&"$e" printf 'Not a directory: \033[4;31m%s\033[0m\n' "$dir"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf 'Delete it, then restart your shell.\n'
|
||||
elif [ ! -w "$dir" ]; then
|
||||
>&"$e" printf 'Directory is not writable: \033[4;31m%s\033[0m\n' "$dir"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf 'Make it writable, then restart your shell.\n'
|
||||
fi
|
||||
break
|
||||
fi
|
||||
if [ "$dir" = / ] || [ "$dir" = . ]; then
|
||||
break
|
||||
fi
|
||||
dir="$(dirname -- "$dir")"
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tmpdir
|
||||
if ! command -v mktemp >/dev/null 2>&1 ||
|
||||
! tmpdir="$(command mktemp -d "${TMPDIR:-/tmp}"/gitstatus-install.XXXXXXXXXX)"; then
|
||||
tmpdir="${TMPDIR:-/tmp}/gitstatus-install.tmp.$$"
|
||||
if ! mkdir -p -- "$tmpdir"; then
|
||||
local dir="${TMPDIR:-/tmp}"
|
||||
if [ -z "${TMPDIR:-}" ]; then
|
||||
local label='directory'
|
||||
else
|
||||
local label='directory (\033[1mTMPDIR\033[m)'
|
||||
fi
|
||||
if [ ! -e "$dir" ]; then
|
||||
>&"$e" printf 'Temporary '"$label"' does not exist: \033[4;31m%s\033[0m\n' "$dir"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf 'Create it, then restart your shell.\n'
|
||||
elif [ ! -d "$dir" ]; then
|
||||
>&"$e" printf 'Not a '"$label"': \033[4;31m%s\033[0m\n' "$dir"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf 'Make it a directory, then restart your shell.\n'
|
||||
elif [ ! -w "$dir" ]; then
|
||||
>&"$e" printf 'Temporary '"$label"' is not writable: \033[4;31m%s\033[0m\n' "$dir"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf 'Make it writable, then restart your shell.\n'
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then
|
||||
>&"$e" printf 'Please install \033[32mcurl\033[0m or \033[32mwget\033[0m, then restart your shell.\n'
|
||||
return 1
|
||||
fi
|
||||
|
||||
(
|
||||
run_cmd() {
|
||||
command -v "$1" >/dev/null 2>/dev/null || return 127
|
||||
local trapped= pid die ret
|
||||
trap 'trapped=1' $sig
|
||||
# The only reason for suppressing stderr is that `curl -f` cannot be silenced:
|
||||
# `-s` doesn't work despite what the docs say.
|
||||
command "$@" 2>/dev/null &
|
||||
ret="$?"
|
||||
if [ "$ret" = 0 ]; then
|
||||
pid="$!"
|
||||
die="trap - $sig; kill -- $pid 2>/dev/null; wait -- $pid 2>/dev/null; exit 1"
|
||||
trap "$die" $sig
|
||||
[ -z "$trapped" ] || eval "$die"
|
||||
wait -- "$pid" 2>/dev/null
|
||||
ret="$?"
|
||||
fi
|
||||
trap - $sig
|
||||
[ -z "$trapped" ] || exit
|
||||
return "$ret"
|
||||
}
|
||||
|
||||
check_sha256() {
|
||||
local data_file="$tmpdir"/"$1".tar.gz
|
||||
local hash_file="$tmpdir"/"$1".tar.gz.sha256
|
||||
local hash=
|
||||
if command -v shasum >/dev/null 2>/dev/null; then
|
||||
if run_cmd shasum -b -a 256 -- "$data_file" >"$hash_file"; then
|
||||
IFS= read -r hash <"$hash_file" || hash=
|
||||
hash="${hash%% *}"
|
||||
fi
|
||||
elif command -v sha256sum >/dev/null 2>/dev/null; then
|
||||
if run_cmd sha256sum -b -- "$data_file" >"$hash_file"; then
|
||||
IFS= read -r hash <"$hash_file" || hash=
|
||||
hash="${hash%% *}"
|
||||
fi
|
||||
elif command -v sha256 >/dev/null 2>/dev/null; then
|
||||
if run_cmd sha256 -- "$data_file" </dev/null >"$hash_file"; then
|
||||
IFS= read -r hash <"$hash_file" || hash=
|
||||
# Ignore sha256 output if it's from hashalot. It's incompatible.
|
||||
if [ ${#hash} -lt 64 ]; then
|
||||
hash=
|
||||
else
|
||||
hash="${hash##* }"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
[ "$1" = 1 -a -z "$hash" -o "$hash" = "$sha256" ]
|
||||
}
|
||||
|
||||
local url1="https://github.com/romkatv/gitstatus/releases/download/$version/$file.tar.gz"
|
||||
local url2="https://gitee.com/romkatv/gitstatus/raw/release-$version/release/$file.tar.gz"
|
||||
local sig='INT QUIT TERM ILL PIPE'
|
||||
|
||||
fetch() {
|
||||
if [ "$1" != 1 ] && command -v sleep >/dev/null 2>/dev/null; then
|
||||
if ! run_cmd sleep "$1"; then
|
||||
echo -n >"$tmpdir"/"$1".status
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
local cmd part url ret
|
||||
for cmd in 'curl -q -kfsSL' 'wget --no-config -qO-' 'wget -qO-' 'curl -kfsSL'; do
|
||||
part=0
|
||||
while true; do
|
||||
if [ "$part" = 2 ]; then
|
||||
ret=1
|
||||
break
|
||||
elif [ "$part" = 0 ]; then
|
||||
url="$2"
|
||||
else
|
||||
url="$2"."$part"
|
||||
fi
|
||||
run_cmd $cmd -- "$url" >>"$tmpdir"/"$1".tar.gz
|
||||
ret="$?"
|
||||
[ "$ret" = 0 ] || break
|
||||
check_sha256 "$1" && break
|
||||
part=$((part+1))
|
||||
done
|
||||
[ "$ret" = 0 ] && break
|
||||
run_cmd rm -f -- "$tmpdir"/"$1".tar.gz && continue
|
||||
ret="$?"
|
||||
break
|
||||
done
|
||||
echo -n >"$tmpdir"/"$1".status
|
||||
return "$ret"
|
||||
}
|
||||
|
||||
local trapped=
|
||||
trap 'trapped=1' $sig
|
||||
fetch 1 "$url1" &
|
||||
local pid1="$!"
|
||||
fetch 2 "$url2" &
|
||||
local pid2="$!"
|
||||
|
||||
local die="trap - $sig; kill -- $pid1 $pid2 2>/dev/null; wait -- $pid1 $pid2 2>/dev/null; exit 1"
|
||||
trap "$die" $sig
|
||||
[ -z "$trapped" ] || eval "$die"
|
||||
|
||||
local n=
|
||||
while true; do
|
||||
[ -z "$dl_status" ] || eval "$dl_status" || eval "$die"
|
||||
if command -v sleep >/dev/null 2>/dev/null; then
|
||||
command sleep 1
|
||||
elif command -v true >/dev/null 2>/dev/null; then
|
||||
command true
|
||||
fi
|
||||
if [ -n "$pid1" -a -e "$tmpdir"/1.status ]; then
|
||||
wait -- "$pid1" 2>/dev/null
|
||||
local ret="$?"
|
||||
pid1=
|
||||
if [ "$ret" = 0 ]; then
|
||||
if [ -n "$pid2" ]; then
|
||||
kill -- "$pid2" 2>/dev/null
|
||||
wait -- "$pid2" 2>/dev/null
|
||||
fi
|
||||
n=1
|
||||
break
|
||||
elif [ -z "$pid2" ]; then
|
||||
break
|
||||
else
|
||||
die="trap - $sig; kill -- $pid2 2>/dev/null; wait -- $pid2 2>/dev/null; exit 1"
|
||||
trap "$die" $sig
|
||||
fi
|
||||
elif [ -n "$pid2" -a -e "$tmpdir"/2.status ]; then
|
||||
wait -- "$pid2" 2>/dev/null
|
||||
local ret="$?"
|
||||
pid2=
|
||||
if [ "$ret" = 0 ]; then
|
||||
if [ -n "$pid1" ]; then
|
||||
kill -- "$pid1" 2>/dev/null
|
||||
wait -- "$pid1" 2>/dev/null
|
||||
fi
|
||||
n=2
|
||||
break
|
||||
elif [ -z "$pid1" ]; then
|
||||
break
|
||||
else
|
||||
die="trap - $sig; kill -- $pid1 2>/dev/null; wait -- $pid1 2>/dev/null; exit 1"
|
||||
trap "$die" $sig
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
trap - $sig
|
||||
|
||||
if [ -z "$n" ]; then
|
||||
>&"$e" printf 'Failed to download \033[32m%s\033[0m from any mirror:\n' "$file"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf ' 1. \033[4m%s\033[0m\n' "$url1"
|
||||
>&"$e" printf ' 2. \033[4m%s\033[0m\n' "$url2"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf 'Check your internet connection, then restart your shell.\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command tar -C "$tmpdir" -xzf "$tmpdir"/"$n".tar.gz || exit
|
||||
|
||||
local tmpfile
|
||||
if ! command -v mktemp >/dev/null 2>&1 ||
|
||||
! tmpfile="$(command mktemp "$cache_dir"/gitstatusd.XXXXXXXXXX)"; then
|
||||
tmpfile="$cache_dir"/gitstatusd.tmp.$$
|
||||
fi
|
||||
|
||||
command mv -f -- "$tmpdir"/"$file" "$tmpfile" || exit
|
||||
command mv -f -- "$tmpfile" "$cache_dir"/"$file" && exit
|
||||
command rm -f -- "$cache_dir"/"$file"
|
||||
command mv -f -- "$tmpfile" "$cache_dir"/"$file" && exit
|
||||
command rm -f -- "$tmpfile"
|
||||
exit 1
|
||||
)
|
||||
|
||||
local ret="$?"
|
||||
command rm -rf -- "$tmpdir"
|
||||
[ "$ret" = 0 ] || return
|
||||
|
||||
_gitstatus_install_daemon_found 1 "$@"
|
||||
return
|
||||
done <"$gitstatus_dir"/install.info
|
||||
|
||||
>&"$e" printf 'There is no prebuilt \033[32mgitstatusd\033[0m for \033[1m%s\033[0m.\n' "$uname_s $uname_m"
|
||||
>&"$e" printf '\n'
|
||||
>&"$e" printf 'See: \033[4mhttps://github.com/romkatv/gitstatus#compiling\033[0m\n'
|
||||
return 1
|
||||
}
|
||||
|
||||
if [ -z "${0##*/*}" ]; then
|
||||
_gitstatus_install_main "${0%/*}" "$@"
|
||||
else
|
||||
_gitstatus_install_main . "$@"
|
||||
fi
|
@ -0,0 +1,34 @@
|
||||
# 2
|
||||
#
|
||||
# This file is used by ./install and indirectly by shell bindings.
|
||||
#
|
||||
# The first line is read by powerlevel10k instant prompt. It must
|
||||
# be updated whenever the content of this file changes. The actual
|
||||
# value doesn't matter as long as it's unique. Consecutive integers
|
||||
# work fine.
|
||||
|
||||
# Official gitstatusd binaries.
|
||||
uname_s_glob="cygwin_nt-10.0"; uname_m_glob="i686"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="fe132c412c460c2889d731012d280207fe2b4a3c94d077fb4f1c06ed5d319a42";
|
||||
uname_s_glob="cygwin_nt-10.0"; uname_m_glob="x86_64"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="c84cade0d6b86e04c27a6055f45851f6b46d6b88ba58772f7ca8ef4d295c800f";
|
||||
uname_s_glob="darwin"; uname_m_glob="arm64"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="ad973948cca4bdcf83b7fcdda70c489a404488ea7304712721f1100b73ec7cbe";
|
||||
uname_s_glob="darwin"; uname_m_glob="x86_64"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="b13455d56cf7b6f07efb7da088057bbc1212847c88b59493918d6f9c0c157160";
|
||||
uname_s_glob="freebsd"; uname_m_glob="amd64"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.3"; sha256="73b5622ec9737e93f3fafe117b47ce8de33037be3e2bff283f36668f5852668a";
|
||||
uname_s_glob="linux"; uname_m_glob="aarch64"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="89b87181b2005527a558cdbc32b12b0f15a1a12bb69865ec216ca5a0266a6c4f";
|
||||
uname_s_glob="linux"; uname_m_glob="armv6l"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="4bf5a0d0a082f544a48536ad3675930d5d2cc6a8cf906710045e0788f51192b3";
|
||||
uname_s_glob="linux"; uname_m_glob="armv7l"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="2b9deb29f86c8209114b71b94fc2e1ed936a1658808a1bee46f4a82fd6a1f8cc";
|
||||
uname_s_glob="linux"; uname_m_glob="armv8l"; file="gitstatusd-${uname_s}-aarch64"; version="v1.5.1"; sha256="89b87181b2005527a558cdbc32b12b0f15a1a12bb69865ec216ca5a0266a6c4f";
|
||||
uname_s_glob="linux"; uname_m_glob="i686"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="4998bf7889f625df71f1da5757915b678e04039cc8cba00ae10950352c7329f9";
|
||||
uname_s_glob="linux"; uname_m_glob="ppc64le"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="e5336dc8e23406c649bafeea83ff17df1726b05ee490f67bae549e55a9a7a7c4";
|
||||
uname_s_glob="linux"; uname_m_glob="x86_64"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="0e8bbc46c17f5cd6e0db98b74c48f4b68f464f98550c8254f6cfcfd936ad1fcf";
|
||||
uname_s_glob="msys_nt-10.0"; uname_m_glob="i686"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="7f9b849fc52e7a95b9b933e25121ad5ae990a1871aad6616922ad7bcf1eebf20";
|
||||
uname_s_glob="msys_nt-10.0"; uname_m_glob="x86_64"; file="gitstatusd-${uname_s}-${uname_m}"; version="v1.5.1"; sha256="5d3c626b5ee564dbc13ddba89752dc58b0efe925b26dbd8b2304849d9ba01732";
|
||||
|
||||
# Fallbacks to official gitstatusd binaries.
|
||||
uname_s_glob="cygwin_nt-*"; uname_m_glob="i686"; file="gitstatusd-cygwin_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="fe132c412c460c2889d731012d280207fe2b4a3c94d077fb4f1c06ed5d319a42";
|
||||
uname_s_glob="cygwin_nt-*"; uname_m_glob="x86_64"; file="gitstatusd-cygwin_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="c84cade0d6b86e04c27a6055f45851f6b46d6b88ba58772f7ca8ef4d295c800f";
|
||||
uname_s_glob="mingw32_nt-*"; uname_m_glob="i686"; file="gitstatusd-msys_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="7f9b849fc52e7a95b9b933e25121ad5ae990a1871aad6616922ad7bcf1eebf20";
|
||||
uname_s_glob="mingw32_nt-*"; uname_m_glob="x86_64"; file="gitstatusd-msys_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="5d3c626b5ee564dbc13ddba89752dc58b0efe925b26dbd8b2304849d9ba01732";
|
||||
uname_s_glob="mingw64_nt-*"; uname_m_glob="i686"; file="gitstatusd-msys_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="7f9b849fc52e7a95b9b933e25121ad5ae990a1871aad6616922ad7bcf1eebf20";
|
||||
uname_s_glob="mingw64_nt-*"; uname_m_glob="x86_64"; file="gitstatusd-msys_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="5d3c626b5ee564dbc13ddba89752dc58b0efe925b26dbd8b2304849d9ba01732";
|
||||
uname_s_glob="msys_nt-*"; uname_m_glob="i686"; file="gitstatusd-msys_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="7f9b849fc52e7a95b9b933e25121ad5ae990a1871aad6616922ad7bcf1eebf20";
|
||||
uname_s_glob="msys_nt-*"; uname_m_glob="x86_64"; file="gitstatusd-msys_nt-10.0-${uname_m}"; version="v1.5.1"; sha256="5d3c626b5ee564dbc13ddba89752dc58b0efe925b26dbd8b2304849d9ba01732";
|
368
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/mbuild
Executable file
368
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/mbuild
Executable file
@ -0,0 +1,368 @@
|
||||
#!/usr/bin/env zsh
|
||||
#
|
||||
# This script does not have a stable API.
|
||||
#
|
||||
# Usage: mbuild [-b git-ref] [kernel-arch]...
|
||||
#
|
||||
# Builds a bunch of gitstatusd-* binaries. Without arguments builds binaries
|
||||
# for all platforms. git-ref defaults to master.
|
||||
#
|
||||
# Before using this script you need to set up build servers and list them
|
||||
# in ~/.ssh/config. There should be a Host entry for every value of `assets`
|
||||
# association defined below. VMs and cloud instances work as well as physical
|
||||
# machines, including localhost. As long as the machine has been set up as
|
||||
# described below and you can SSH to it without password, it should work.
|
||||
#
|
||||
# ===[ Build Server Setup ]===
|
||||
#
|
||||
# Linux
|
||||
#
|
||||
# - Install docker.
|
||||
# $ apt install docker.io # adjust appropriately if there is no `apt`
|
||||
# $ usermod -aG docker $USER # not needed if going to build as root
|
||||
# - Install git.
|
||||
# $ apt install git # adjust appropriately if there is no `apt`
|
||||
#
|
||||
# macOS
|
||||
#
|
||||
# - Install compiler tools:
|
||||
# $ xcode-select --install
|
||||
# - Install homebrew: https://brew.sh/.
|
||||
# $ bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
|
||||
#
|
||||
# FreeBSD
|
||||
#
|
||||
# - Install git.
|
||||
# $ pkg install git
|
||||
#
|
||||
# Windows
|
||||
#
|
||||
# - Disable Windows Defender (optional).
|
||||
# ps> Set-MpPreference -DisableRealtimeMonitoring $true
|
||||
# - Install 64-bit and 32-bit msys2: https://www.msys2.org/wiki/MSYS2-installation/.
|
||||
# - Open each of them after installation, type `pacman -Syu --noconfirm` and close the window.
|
||||
# - Then run in powershell while having no msys2 or cygwin windows open:
|
||||
# ps> C:\msys32\autorebase.bat
|
||||
# ps> C:\msys64\autorebase.bat
|
||||
# - Install 64-bit and 32-bit cygwin: https://cygwin.com/install.html.
|
||||
# - Choose to install 32-bit to c:/cygwin32 instead of the default c:/cygwin.
|
||||
# - Select these packages: binutils, cmake, gcc-core, gcc-g++, git, make, perl, wget.
|
||||
#
|
||||
# IMPORTANT: Install msys2 and cygwin one at a time.
|
||||
#
|
||||
# IMPORTANT: msys2 builder can reboot the build machine.
|
||||
#
|
||||
# Option 1: OpenSSH for Windows
|
||||
#
|
||||
# - Install OpenSSH: https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse.
|
||||
# ps> Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
|
||||
# ps> Start-Service sshd
|
||||
# ps> Set-Service -Name sshd -StartupType 'Automatic'
|
||||
# - Enable publickey authentication: https://stackoverflow.com/a/50502015/1095235.
|
||||
# ps> cd $env:USERPROFILE
|
||||
# ps> mkdir .ssh
|
||||
# ps> notepad.exe .ssh/authorized_keys
|
||||
# - Paste your public key, save, close.
|
||||
# ps> icacls .ssh/authorized_keys /inheritance:r
|
||||
# ps> notepad.exe C:\ProgramData\ssh\sshd_config
|
||||
# - Comment out these two lines, save, close:
|
||||
# # Match Group administrators
|
||||
# # AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
|
||||
# ps> Restart-Service sshd
|
||||
#
|
||||
# Option 2: OpenSSH from WSL
|
||||
#
|
||||
# - Install WSL.
|
||||
# - Install Ubuntu.
|
||||
# - Install sshd.
|
||||
# $ apt install openssh-server
|
||||
# $ dpkg-reconfigure openssh-server
|
||||
# $ cat >/etc/ssh/sshd_config <<\END
|
||||
# ClientAliveInterval 60
|
||||
# AcceptEnv TERM LANG LC_*
|
||||
# PermitRootLogin no
|
||||
# AllowTcpForwarding no
|
||||
# AllowAgentForwarding no
|
||||
# AllowStreamLocalForwarding no
|
||||
# AuthenticationMethods publickey
|
||||
# END
|
||||
# service ssh --full-restart
|
||||
# - Add your public ssh key to ~/.ssh/authorized_keys.
|
||||
# - Make `sshd` start when Windows boots.
|
||||
|
||||
'emulate' '-L' 'zsh' '-o' 'no_aliases' '-o' 'err_return'
|
||||
setopt no_unset extended_glob pipe_fail prompt_percent typeset_silent \
|
||||
no_prompt_subst no_prompt_bang pushd_silent warn_create_global
|
||||
|
||||
autoload -Uz is-at-least
|
||||
|
||||
if ! is-at-least 5.1 || [[ $ZSH_VERSION == 5.4.* ]]; then
|
||||
print -ru2 -- "[error] unsupported zsh version: $ZSH_VERSION"
|
||||
return 1
|
||||
fi
|
||||
|
||||
zmodload zsh/system
|
||||
|
||||
local -r git_url='https://github.com/romkatv/gitstatus.git'
|
||||
|
||||
local -rA assets=(
|
||||
# target kernel-arch hostname of the build machine
|
||||
cygwin_nt-10.0-i686 build-windows-x86_64
|
||||
cygwin_nt-10.0-x86_64 build-windows-x86_64
|
||||
msys_nt-10.0-i686 build-windows-x86_64
|
||||
msys_nt-10.0-x86_64 build-windows-x86_64
|
||||
darwin-arm64 build-macos-arm64
|
||||
darwin-x86_64 build-macos-x86_64
|
||||
freebsd-amd64 build-freebsd-amd64
|
||||
linux-aarch64 build-linux-aarch64
|
||||
linux-armv6l build-linux-armv7l
|
||||
linux-armv7l build-linux-armv7l
|
||||
linux-i686 build-linux-x86_64
|
||||
linux-ppc64le build-linux-ppc64le
|
||||
linux-x86_64 build-linux-x86_64
|
||||
)
|
||||
|
||||
local -rA protocol=(
|
||||
'cygwin_nt-10.0-*' windows
|
||||
'msys_nt-10.0-*' windows
|
||||
'darwin-*' unix
|
||||
'freebsd-*' unix
|
||||
'linux-*' unix
|
||||
)
|
||||
|
||||
local -r rootdir=${ZSH_SCRIPT:h}
|
||||
local -r logs=$rootdir/logs
|
||||
local -r locks=$rootdir/locks
|
||||
local -r binaries=$rootdir/usrbin
|
||||
|
||||
function usage() {
|
||||
print -r -- 'usage: mbuild [-b REF] [KERNEL-ARCH]...'
|
||||
}
|
||||
|
||||
local OPTARG opt git_ref=master
|
||||
local -i OPTIND
|
||||
while getopts ":b:h" opt; do
|
||||
case $opt in
|
||||
h) usage; return 0;;
|
||||
b) [[ -n $OPTARG ]]; git_ref=$OPTARG;;
|
||||
\?) print -ru2 -- "mbuild: invalid option: -$OPTARG" ; return 1;;
|
||||
:) print -ru2 -- "mbuild: missing required argument: -$OPTARG"; return 1;;
|
||||
*) print -ru2 -- "mbuild: invalid option: -$opt" ; return 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
(( $# )) || set -- ${(ko)assets}
|
||||
set -- ${(u)@}
|
||||
|
||||
local platform
|
||||
for platform; do
|
||||
if (( ! $+assets[$platform] )); then
|
||||
print -ru2 -- "mbuild: invalid platform: $platform"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
local build='
|
||||
rm -rf gitstatus
|
||||
git clone --recursive --shallow-submodules --depth=1 -b '$git_ref' '$git_url'
|
||||
cd gitstatus
|
||||
if command -v zsh >/dev/null 2>&1; then
|
||||
sh=zsh
|
||||
elif command -v dash >/dev/null 2>&1; then
|
||||
sh=dash
|
||||
elif command -v ash >/dev/null 2>&1; then
|
||||
sh=ash
|
||||
else
|
||||
sh=sh
|
||||
fi
|
||||
$sh -x ./build -m '
|
||||
|
||||
function build-unix() {
|
||||
local intro flags=(-sw)
|
||||
case $2 in
|
||||
linux-ppc64le) ;;
|
||||
linux-*) flags+=(-d docker);;
|
||||
darwin-arm64) intro='PATH="/opt/local/bin:$PATH"';;
|
||||
darwin-*) intro='PATH="/usr/local/bin:$PATH"';;
|
||||
esac
|
||||
ssh $1 -- /bin/sh -uex <<<"
|
||||
$intro
|
||||
cd /tmp
|
||||
$build ${2##*-} ${(j: :)${(@q)flags}}"
|
||||
scp $1:/tmp/gitstatus/usrbin/gitstatusd $binaries/gitstatusd-$2
|
||||
}
|
||||
|
||||
function build-windows() {
|
||||
local shell=$(ssh $1 'echo $0')
|
||||
if [[ $shell == '$0'* ]]; then
|
||||
local c='c:'
|
||||
else
|
||||
local c='/mnt/c'
|
||||
fi
|
||||
|
||||
local tmp env bin intro flags=(-w)
|
||||
case $2 in
|
||||
cygwin_nt-10.0-i686) bin='cygwin32/bin' ;|
|
||||
cygwin_nt-10.0-x86_64) bin='cygwin64/bin' ;|
|
||||
msys_nt-10.0-i686) bin='msys32/usr/bin';|
|
||||
msys_nt-10.0-x86_64) bin='msys64/usr/bin';|
|
||||
cygwin_nt-10.0-*)
|
||||
tmp='/cygdrive/c/tmp'
|
||||
;|
|
||||
msys_nt-10.0-*)
|
||||
tmp='/c/tmp'
|
||||
env='MSYSTEM=MSYS'
|
||||
# TODO: fix this (some errors about PGP keys).
|
||||
# flags+=(-s)
|
||||
# intro='pacman -S --needed --noconfirm git; '
|
||||
intro+='PATH="$PATH:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl"'
|
||||
while true; do
|
||||
# TODO: run autorebase only when getting an error that can be fixed by autorebasing.
|
||||
break
|
||||
local out
|
||||
out="$(ssh $1 cmd.exe "$c/${bin%%/*}/autorebase.bat" 2>&1)"
|
||||
[[ $out == *"The following DLLs couldn't be rebased"* ]] || break
|
||||
# Reboot to get rid of whatever is using those DLLs.
|
||||
ssh $1 powershell.exe <<<'Restart-Computer -Force' || true
|
||||
sleep 30
|
||||
while ! ssh $1 <<<''; do sleep 5; done
|
||||
done
|
||||
() {
|
||||
while true; do
|
||||
# TODO: fix this (some errors about PGP keys).
|
||||
break
|
||||
local -i fd
|
||||
exec {fd}< <(
|
||||
ssh $1 $c/$bin/env.exe $env c:/$bin/bash.exe -l 2>&1 <<<"
|
||||
pacman -Syu --noconfirm
|
||||
exit")
|
||||
{
|
||||
local line
|
||||
while true; do
|
||||
IFS= read -u $fd -r line || return 0
|
||||
if [[ $line == *"warning: terminate MSYS2"* ]]; then
|
||||
# At this point the machine is hosed. A rogue process with a corrupted name
|
||||
# is eating all CPU. The top SSH connection won't terminate on its own.
|
||||
ssh $1 powershell.exe <<<'Restart-Computer -Force' || true
|
||||
sleep 30
|
||||
while ! ssh $1 <<<''; do sleep 5; done
|
||||
break
|
||||
fi
|
||||
done
|
||||
} always {
|
||||
exec {fd}<&-
|
||||
kill -- -$sysparams[procsubstpid] 2>/dev/null || true
|
||||
}
|
||||
done
|
||||
} "$@"
|
||||
;|
|
||||
esac
|
||||
|
||||
ssh $1 $c/$bin/env.exe $env c:/$bin/bash.exe -l <<<"
|
||||
set -uex
|
||||
$intro
|
||||
mkdir -p -- $tmp
|
||||
cd -- $tmp
|
||||
$build ${2##*-} ${(j: :)${(@q)flags}}
|
||||
exit"
|
||||
scp $1:$c/tmp/gitstatus/usrbin/gitstatusd $binaries/gitstatusd-$2
|
||||
chmod +x $binaries/gitstatusd-$2
|
||||
}
|
||||
|
||||
function build() (
|
||||
setopt xtrace
|
||||
local platform=$1
|
||||
local machine=$assets[$platform]
|
||||
print -n >>$locks/$machine
|
||||
zsystem flock $locks/$machine
|
||||
build-${protocol[(k)$platform]} $machine $platform
|
||||
local tmp=gitstatusd-$platform.tmp.$$.tar.gz
|
||||
( cd -q -- $binaries; tar --owner=0 --group=0 -I 'gzip -9' -cf $tmp gitstatusd-$platform )
|
||||
mv -f -- $binaries/$tmp $binaries/gitstatusd-$platform.tar.gz
|
||||
)
|
||||
|
||||
function mbuild() {
|
||||
local platform pid pids=()
|
||||
for platform; do
|
||||
build $platform &>$logs/$platform &
|
||||
print -r -- "starting build for $platform on $assets[$platform] (pid $!)"
|
||||
pids+=($platform $!)
|
||||
done
|
||||
local failed=()
|
||||
for platform pid in $pids; do
|
||||
print -rn -- "$platform => "
|
||||
if wait $pid; then
|
||||
print -r -- "ok"
|
||||
else
|
||||
print -r -- "error"
|
||||
failed+=$platform
|
||||
fi
|
||||
done
|
||||
(( $#failed )) || return 0
|
||||
print
|
||||
print -r -- "Error logs:"
|
||||
print
|
||||
for platform in $failed; do
|
||||
print -r -- " $platform => $logs/$platform"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Copied from https://github.com/romkatv/run-process-tree.
|
||||
function run-process-tree() {
|
||||
zmodload zsh/parameter zsh/param/private || return
|
||||
local -P opt=(${(kv)options[@]}) || return
|
||||
local -P pat=(${patchars[@]}) || return
|
||||
local -P dis_pat=(${dis_patchars[@]}) || return
|
||||
emulate -L zsh -o err_return || return
|
||||
setopt monitor traps_async pipe_fail no_unset
|
||||
zmodload zsh/system
|
||||
|
||||
if (( $# == 0 )); then
|
||||
print -ru2 -- 'usage: run-process-tree command [arg]...'
|
||||
return 1
|
||||
fi
|
||||
|
||||
local -P stdout REPLY
|
||||
exec {stdout}>&1
|
||||
{
|
||||
{
|
||||
local -Pi pipe
|
||||
local -P gid=$sysparams[pid]
|
||||
local -P sig=(ABRT EXIT HUP ILL INT PIPE QUIT TERM ZERR)
|
||||
local -P trap=(trap "trap - $sig; kill -- -$sysparams[pid]" $sig)
|
||||
|
||||
exec {pipe}>&1 1>&$stdout
|
||||
$trap
|
||||
|
||||
{
|
||||
$trap
|
||||
while sleep 1 && print -u $pipe .; do; done
|
||||
} 2>/dev/null &
|
||||
local -Pi watchdog=$!
|
||||
|
||||
{
|
||||
trap - ZERR
|
||||
exec {pipe}>&-
|
||||
enable -p -- $pat
|
||||
disable -p -- $dis_pat
|
||||
options=($opt zle off monitor off)
|
||||
"$@"
|
||||
} &
|
||||
local -Pi ret
|
||||
wait $! || ret=$?
|
||||
|
||||
trap "exit $ret" TERM
|
||||
kill $watchdog
|
||||
wait $watchdog
|
||||
return ret
|
||||
} | while read; do; done || return
|
||||
} always {
|
||||
exec {stdout}>&-
|
||||
}
|
||||
}
|
||||
|
||||
mkdir -p -- $logs $locks $binaries
|
||||
run-process-tree mbuild $@
|
@ -0,0 +1,37 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_ALGORITHM_H_
|
||||
#define ROMKATV_GITSTATUS_ALGORITHM_H_
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// Requires: Iter is a BidirectionalIterator.
|
||||
//
|
||||
// Returns iterator pointing to the last value in [begin, end) that compares equal to the value, or
|
||||
// begin if none compare equal.
|
||||
template <class Iter, class T>
|
||||
Iter FindLast(Iter begin, Iter end, const T& val) {
|
||||
while (begin != end && !(*--end == val)) {}
|
||||
return end;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_ALGORITHM_H_
|
@ -0,0 +1,118 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "arena.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
#include "bits.h"
|
||||
#include "check.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
size_t Clamp(size_t min, size_t val, size_t max) { return std::min(max, std::max(min, val)); }
|
||||
|
||||
static const uintptr_t kSingularity = reinterpret_cast<uintptr_t>(&kSingularity);
|
||||
|
||||
} // namespace
|
||||
|
||||
// Triple singularity. We are all fucked.
|
||||
Arena::Block Arena::g_empty_block = {kSingularity, kSingularity, kSingularity};
|
||||
|
||||
Arena::Arena(Arena::Options opt) : opt_(std::move(opt)), top_(&g_empty_block) {
|
||||
CHECK(opt_.min_block_size <= opt_.max_block_size);
|
||||
}
|
||||
|
||||
Arena::Arena(Arena&& other) : Arena() { *this = std::move(other); }
|
||||
|
||||
Arena::~Arena() {
|
||||
// See comments in Makefile for the reason sized deallocation is not used.
|
||||
for (const Block& b : blocks_) ::operator delete(reinterpret_cast<void*>(b.start));
|
||||
}
|
||||
|
||||
Arena& Arena::operator=(Arena&& other) {
|
||||
if (this != &other) {
|
||||
// In case std::vector ever gets small object optimization.
|
||||
size_t idx = other.reusable_ ? other.top_ - other.blocks_.data() : 0;
|
||||
opt_ = other.opt_;
|
||||
blocks_ = std::move(other.blocks_);
|
||||
reusable_ = other.reusable_;
|
||||
top_ = reusable_ ? blocks_.data() + idx : &g_empty_block;
|
||||
other.blocks_.clear();
|
||||
other.reusable_ = 0;
|
||||
other.top_ = &g_empty_block;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Arena::Reuse(size_t num_blocks) {
|
||||
reusable_ = std::min(reusable_, num_blocks);
|
||||
for (size_t i = reusable_; i != blocks_.size(); ++i) {
|
||||
const Block& b = blocks_[i];
|
||||
// See comments in Makefile for the reason sized deallocation is not used.
|
||||
::operator delete(reinterpret_cast<void*>(b.start));
|
||||
}
|
||||
blocks_.resize(reusable_);
|
||||
if (reusable_) {
|
||||
top_ = blocks_.data();
|
||||
top_->tip = top_->start;
|
||||
} else {
|
||||
top_ = &g_empty_block;
|
||||
}
|
||||
}
|
||||
|
||||
void Arena::AddBlock(size_t size, size_t alignment) {
|
||||
if (alignment > alignof(std::max_align_t)) {
|
||||
size += alignment - 1;
|
||||
} else {
|
||||
size = std::max(size, alignment);
|
||||
}
|
||||
if (size <= top_->size() && top_ < blocks_.data() + reusable_ - 1) {
|
||||
assert(blocks_.front().size() == top_->size());
|
||||
++top_;
|
||||
top_->tip = top_->start;
|
||||
return;
|
||||
}
|
||||
if (size <= opt_.max_alloc_threshold) {
|
||||
size =
|
||||
std::max(size, Clamp(opt_.min_block_size, NextPow2(top_->size() + 1), opt_.max_block_size));
|
||||
}
|
||||
|
||||
auto p = reinterpret_cast<uintptr_t>(::operator new(size));
|
||||
blocks_.push_back(Block{p, p, p + size});
|
||||
if (reusable_) {
|
||||
if (size < blocks_.front().size()) {
|
||||
top_ = &blocks_.back();
|
||||
return;
|
||||
}
|
||||
if (size > blocks_.front().size()) reusable_ = 0;
|
||||
}
|
||||
std::swap(blocks_.back(), blocks_[reusable_]);
|
||||
top_ = &blocks_[reusable_++];
|
||||
}
|
||||
|
||||
void* Arena::AllocateSlow(size_t size, size_t alignment) {
|
||||
assert(alignment && !(alignment & (alignment - 1)));
|
||||
AddBlock(size, alignment);
|
||||
assert(Align(top_->tip, alignment) + size <= top_->end);
|
||||
return Allocate(size, alignment);
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
273
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/arena.h
Normal file
273
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/arena.h
Normal file
@ -0,0 +1,273 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_ARENA_H_
|
||||
#define ROMKATV_GITSTATUS_ARENA_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "string_view.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// Thread-compatible. Very fast and very flexible w.r.t. allocation size and alignment.
|
||||
//
|
||||
// Natural API extensions:
|
||||
//
|
||||
// // Donates a block to the arena. When the time comes, it'll be freed with
|
||||
// // free(p, size, userdata).
|
||||
// void Donate(void* p, size_t size, void* userdata, void(*free)(void*, size_t, void*));
|
||||
class Arena {
|
||||
public:
|
||||
struct Options {
|
||||
// The first call to Allocate() will allocate a block of this size. There is one exception when
|
||||
// the first requested allocation size is larger than this limit. Subsequent blocks will be
|
||||
// twice as large as the last until they saturate at max_block_size.
|
||||
size_t min_block_size = 64;
|
||||
|
||||
// Allocate blocks at most this large. There is one exception when the requested allocation
|
||||
// size is larger than this limit.
|
||||
size_t max_block_size = 8 << 10;
|
||||
|
||||
// When the size of the first allocation in a block is larger than this threshold, the block
|
||||
// size will be equal to the allocation size. This is meant to reduce memory waste when making
|
||||
// many allocations with sizes slightly over max_block_size / 2. With max_alloc_threshold equal
|
||||
// to max_block_size / N, the upper bound on wasted memory when making many equally-sized
|
||||
// allocations is 100.0 / (N + 1) percent. When making allocations of different sizes, the upper
|
||||
// bound on wasted memory is 50%.
|
||||
size_t max_alloc_threshold = 1 << 10;
|
||||
|
||||
// Natural extensions:
|
||||
//
|
||||
// void* userdata;
|
||||
// void (*alloc)(size_t size, size_t alignment, void* userdata);
|
||||
// void (*free)(void* p, size_t size, void* userdata);
|
||||
};
|
||||
|
||||
// Requires: opt.min_block_size <= opt.max_block_size.
|
||||
//
|
||||
// Doesn't allocate any memory.
|
||||
Arena(Options opt);
|
||||
Arena() : Arena(Options()) {}
|
||||
Arena(Arena&&);
|
||||
~Arena();
|
||||
|
||||
Arena& operator=(Arena&& other);
|
||||
|
||||
// Requires: alignment is a power of 2.
|
||||
//
|
||||
// Result is never null and always aligned. If size is zero, the result may be equal to the last.
|
||||
// Alignment above alignof(std::max_align_t) is supported. There is no requirement for alignment
|
||||
// to be less than size or to divide it.
|
||||
inline void* Allocate(size_t size, size_t alignment) {
|
||||
assert(alignment && !(alignment & (alignment - 1)));
|
||||
uintptr_t p = Align(top_->tip, alignment);
|
||||
uintptr_t e = p + size;
|
||||
if (e <= top_->end) {
|
||||
top_->tip = e;
|
||||
return reinterpret_cast<void*>(p);
|
||||
}
|
||||
return AllocateSlow(size, alignment);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T* Allocate(size_t n) {
|
||||
static_assert(!std::is_reference<T>(), "");
|
||||
return static_cast<T*>(Allocate(n * sizeof(T), alignof(T)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T* Allocate() {
|
||||
return Allocate<T>(1);
|
||||
}
|
||||
|
||||
inline char* MemDup(const char* p, size_t len) {
|
||||
char* res = Allocate<char>(len);
|
||||
std::memcpy(res, p, len);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Copies the null-terminated string (including the trailing null character) to the arena and
|
||||
// returns a pointer to the copy.
|
||||
inline char* StrDup(const char* s) {
|
||||
size_t len = std::strlen(s);
|
||||
return MemDup(s, len + 1);
|
||||
}
|
||||
|
||||
// Guarantees: !StrDup(p, len)[len].
|
||||
inline char* StrDup(const char* p, size_t len) {
|
||||
char* res = Allocate<char>(len + 1);
|
||||
std::memcpy(res, p, len);
|
||||
res[len] = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Guarantees: !StrDup(s)[s.len].
|
||||
inline char* StrDup(StringView s) {
|
||||
return StrDup(s.ptr, s.len);
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
inline char* StrCat(const Ts&... ts) {
|
||||
return [&](std::initializer_list<StringView> ss) {
|
||||
size_t len = 0;
|
||||
for (StringView s : ss) len += s.len;
|
||||
char* p = Allocate<char>(len + 1);
|
||||
for (StringView s : ss) {
|
||||
std::memcpy(p, s.ptr, s.len);
|
||||
p += s.len;
|
||||
}
|
||||
*p = 0;
|
||||
return p - len;
|
||||
}({ts...});
|
||||
}
|
||||
|
||||
// Copies/moves `val` to the arena and returns a pointer to it.
|
||||
template <class T>
|
||||
inline std::remove_const_t<std::remove_reference_t<T>>* Dup(T&& val) {
|
||||
return DirectInit<std::remove_const_t<std::remove_reference_t<T>>>(std::forward<T>(val));
|
||||
}
|
||||
|
||||
// The same as `new T{args...}` but on the arena.
|
||||
template <class T, class... Args>
|
||||
inline T* DirectInit(Args&&... args) {
|
||||
T* res = Allocate<T>();
|
||||
::new (const_cast<void*>(static_cast<const void*>(res))) T(std::forward<Args>(args)...);
|
||||
return res;
|
||||
}
|
||||
|
||||
// The same as `new T(args...)` but on the arena.
|
||||
template <class T, class... Args>
|
||||
inline T* BraceInit(Args&&... args) {
|
||||
T* res = Allocate<T>();
|
||||
::new (const_cast<void*>(static_cast<const void*>(res))) T{std::forward<Args>(args)...};
|
||||
return res;
|
||||
}
|
||||
|
||||
// Tip() and TipSize() allow you to allocate the remainder of the current block. They can be
|
||||
// useful if you are flexible w.r.t. the allocation size.
|
||||
//
|
||||
// Invariant:
|
||||
//
|
||||
// const void* tip = Tip();
|
||||
// void* p = Allocate(TipSize(), 1); // grab the remainder of the current block
|
||||
// assert(p == tip);
|
||||
const void* Tip() const { return reinterpret_cast<const void*>(top_->tip); }
|
||||
size_t TipSize() const { return top_->end - top_->tip; }
|
||||
|
||||
// Invalidates all allocations (without running destructors of allocated objects) and frees all
|
||||
// blocks except at most the specified number of blocks. The retained blocks will be used to
|
||||
// fulfil future allocation requests.
|
||||
void Reuse(size_t num_blocks = std::numeric_limits<size_t>::max());
|
||||
|
||||
private:
|
||||
struct Block {
|
||||
size_t size() const { return end - start; }
|
||||
uintptr_t start;
|
||||
uintptr_t tip;
|
||||
uintptr_t end;
|
||||
};
|
||||
|
||||
inline static size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); };
|
||||
|
||||
void AddBlock(size_t size, size_t alignment);
|
||||
bool ReuseBlock(size_t size, size_t alignment);
|
||||
|
||||
__attribute__((noinline)) void* AllocateSlow(size_t size, size_t alignment);
|
||||
|
||||
Options opt_;
|
||||
std::vector<Block> blocks_;
|
||||
// Invariant: !blocks_.empty() <= reusable_ && reusable_ <= blocks_.size().
|
||||
size_t reusable_ = 0;
|
||||
// Invariant: (top_ == &g_empty_block) == blocks_.empty().
|
||||
// Invariant: blocks_.empty() || top_ == &blocks_.back() || top_ < blocks_.data() + reusable_.
|
||||
Block* top_;
|
||||
|
||||
static Block g_empty_block;
|
||||
};
|
||||
|
||||
// Copies of ArenaAllocator use the same thread-compatible Arena without synchronization.
|
||||
template <class T>
|
||||
class ArenaAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = ArenaAllocator<U>;
|
||||
};
|
||||
using is_always_equal = std::false_type;
|
||||
|
||||
ArenaAllocator(Arena* arena = nullptr) : arena_(*arena) {}
|
||||
|
||||
Arena& arena() const { return arena_; }
|
||||
|
||||
pointer address(reference x) const { return &x; }
|
||||
const_pointer address(const_reference x) const { return &x; }
|
||||
pointer allocate(size_type n, const void* hint = nullptr) { return arena_.Allocate<T>(n); }
|
||||
void deallocate(T* p, std::size_t n) {}
|
||||
size_type max_size() const { return std::numeric_limits<size_type>::max() / sizeof(value_type); }
|
||||
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, Args&&... args) {
|
||||
::new (const_cast<void*>(static_cast<const void*>(p))) U(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
void destroy(U* p) {
|
||||
p->~U();
|
||||
}
|
||||
|
||||
bool operator==(const ArenaAllocator& other) const { return &arena_ == &other.arena_; }
|
||||
bool operator!=(const ArenaAllocator& other) const { return &arena_ != &other.arena_; }
|
||||
|
||||
private:
|
||||
Arena& arena_;
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct LazyWithArena;
|
||||
|
||||
template <template <class, class> class C, class T1, class A>
|
||||
struct LazyWithArena<C<T1, A>> {
|
||||
using type = C<T1, ArenaAllocator<typename C<T1, A>::value_type>>;
|
||||
};
|
||||
|
||||
template <template <class, class, class> class C, class T1, class T2, class A>
|
||||
struct LazyWithArena<C<T1, T2, A>> {
|
||||
using type = C<T1, T2, ArenaAllocator<typename C<T1, T2, A>::value_type>>;
|
||||
};
|
||||
|
||||
template <class C>
|
||||
using WithArena = typename LazyWithArena<C>::type;
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_DIR_H_
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_BITS_H_
|
||||
#define ROMKATV_GITSTATUS_BITS_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
inline size_t NextPow2(size_t n) { return n < 2 ? 1 : (~size_t{0} >> __builtin_clzll(n - 1)) + 1; }
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_BITS_H_
|
@ -0,0 +1,61 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_CHECK_H_
|
||||
#define ROMKATV_GITSTATUS_CHECK_H_
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
// The argument must be an expression convertible to bool.
|
||||
// Does nothing if the expression evalutes to true. Otherwise
|
||||
// it's equivalent to LOG(FATAL).
|
||||
#define CHECK(cond...) \
|
||||
static_cast<void>(0), (!!(cond)) ? static_cast<void>(0) : LOG(FATAL) << #cond << ": "
|
||||
|
||||
#define VERIFY(cond...) \
|
||||
static_cast<void>(0), ::gitstatus::internal_check::Thrower(!(cond)) \
|
||||
? static_cast<void>(0) \
|
||||
: LOG(ERROR) << #cond << ": "
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
struct Exception : std::exception {
|
||||
const char* what() const noexcept override { return "Exception"; }
|
||||
};
|
||||
|
||||
namespace internal_check {
|
||||
|
||||
class Thrower {
|
||||
public:
|
||||
Thrower(bool should_throw) : throw_(should_throw) {}
|
||||
Thrower(Thrower&&) = delete;
|
||||
explicit operator bool() const { return !throw_; }
|
||||
~Thrower() noexcept(false) {
|
||||
if (throw_) throw Exception();
|
||||
}
|
||||
|
||||
private:
|
||||
bool throw_;
|
||||
};
|
||||
|
||||
} // namespace internal_check
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_CHECK_H_
|
@ -0,0 +1,157 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "check_dir_mtime.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "check.h"
|
||||
#include "dir.h"
|
||||
#include "logging.h"
|
||||
#include "print.h"
|
||||
#include "scope_guard.h"
|
||||
#include "stat.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kDirPrefix[] = ".gitstatus.";
|
||||
|
||||
void Touch(const char* path) {
|
||||
int fd = creat(path, 0444);
|
||||
VERIFY(fd >= 0) << Errno();
|
||||
CHECK(!close(fd)) << Errno();
|
||||
}
|
||||
|
||||
bool StatChanged(const char* path, const struct stat& prev) {
|
||||
struct stat cur;
|
||||
VERIFY(!lstat(path, &cur)) << Errno();
|
||||
return !StatEq(prev, cur);
|
||||
}
|
||||
|
||||
void RemoveStaleDirs(const char* root_dir) {
|
||||
int dir_fd = open(root_dir, O_DIRECTORY | O_CLOEXEC);
|
||||
if (dir_fd < 0) return;
|
||||
ON_SCOPE_EXIT(&) { CHECK(!close(dir_fd)) << Errno(); };
|
||||
|
||||
Arena arena;
|
||||
std::vector<char*> entries;
|
||||
const std::time_t now = std::time(nullptr);
|
||||
if (!ListDir(dir_fd, arena, entries,
|
||||
/* precompose_unicode = */ false,
|
||||
/* case_sensitive = */ true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string path = root_dir;
|
||||
const size_t root_dir_len = path.size();
|
||||
|
||||
for (const char* entry : entries) {
|
||||
if (std::strlen(entry) < std::strlen(kDirPrefix)) continue;
|
||||
if (std::memcmp(entry, kDirPrefix, std::strlen(kDirPrefix))) continue;
|
||||
|
||||
struct stat st;
|
||||
if (fstatat(dir_fd, entry, &st, AT_SYMLINK_NOFOLLOW)) {
|
||||
LOG(WARN) << "Cannot stat " << Print(entry) << " in " << Print(root_dir) << ": " << Errno();
|
||||
continue;
|
||||
}
|
||||
if (MTim(st).tv_sec + 10 > now) continue;
|
||||
|
||||
path.resize(root_dir_len);
|
||||
path += entry;
|
||||
size_t dir_len = path.size();
|
||||
|
||||
path += "/b/1";
|
||||
if (unlink(path.c_str()) && errno != ENOENT) {
|
||||
LOG(WARN) << "Cannot unlink " << Print(path) << ": " << Errno();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const char* d : {"/a/1", "/a", "/b", ""}) {
|
||||
path.resize(dir_len);
|
||||
path += d;
|
||||
if (rmdir(path.c_str()) && errno != ENOENT) {
|
||||
LOG(WARN) << "Cannot remove " << Print(path) << ": " << Errno();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CheckDirMtime(const char* root_dir) {
|
||||
try {
|
||||
RemoveStaleDirs(root_dir);
|
||||
|
||||
std::string tmp = std::string() + root_dir + kDirPrefix + "XXXXXX";
|
||||
VERIFY(mkdtemp(&tmp[0])) << Errno();
|
||||
ON_SCOPE_EXIT(&) { rmdir(tmp.c_str()); };
|
||||
|
||||
std::string a_dir = tmp + "/a";
|
||||
VERIFY(!mkdir(a_dir.c_str(), 0755)) << Errno();
|
||||
ON_SCOPE_EXIT(&) { rmdir(a_dir.c_str()); };
|
||||
struct stat a_st;
|
||||
VERIFY(!lstat(a_dir.c_str(), &a_st)) << Errno();
|
||||
|
||||
std::string b_dir = tmp + "/b";
|
||||
VERIFY(!mkdir(b_dir.c_str(), 0755)) << Errno();
|
||||
ON_SCOPE_EXIT(&) { rmdir(b_dir.c_str()); };
|
||||
struct stat b_st;
|
||||
VERIFY(!lstat(b_dir.c_str(), &b_st)) << Errno();
|
||||
|
||||
while (sleep(1)) {
|
||||
// zzzz
|
||||
}
|
||||
|
||||
std::string a1 = a_dir + "/1";
|
||||
VERIFY(!mkdir(a1.c_str(), 0755)) << Errno();
|
||||
ON_SCOPE_EXIT(&) { rmdir(a1.c_str()); };
|
||||
if (!StatChanged(a_dir.c_str(), a_st)) {
|
||||
LOG(WARN) << "Creating a directory doesn't change mtime of the parent: " << Print(root_dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string b1 = b_dir + "/1";
|
||||
Touch(b1.c_str());
|
||||
ON_SCOPE_EXIT(&) { unlink(b1.c_str()); };
|
||||
if (!StatChanged(b_dir.c_str(), b_st)) {
|
||||
LOG(WARN) << "Creating a file doesn't change mtime of the parent: " << Print(root_dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "All mtime checks have passes. Enabling untracked cache: " << Print(root_dir);
|
||||
return true;
|
||||
} catch (const Exception&) {
|
||||
LOG(WARN) << "Error while testing for mtime capability: " << Print(root_dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,31 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_CHECK_DIR_MTIME_H_
|
||||
#define ROMKATV_GITSTATUS_CHECK_DIR_MTIME_H_
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// Similar to `git update-index --test-untracked-cache` but performs all tests
|
||||
// in parallel, so the total testing time is one second regardless of the number
|
||||
// of tests. It also performs fewer tests because gitstatus imposes fewer
|
||||
// requirements on the filesystem in order to take advantage of untracked cache.
|
||||
bool CheckDirMtime(const char* root_dir);
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_CHECK_DIR_MTIME_H_
|
237
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/dir.cc
Normal file
237
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/dir.cc
Normal file
@ -0,0 +1,237 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "dir.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <endian.h>
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <iconv.h>
|
||||
#endif
|
||||
|
||||
#include "bits.h"
|
||||
#include "check.h"
|
||||
#include "scope_guard.h"
|
||||
#include "string_cmp.h"
|
||||
#include "tribool.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
bool Dots(const char* name) {
|
||||
if (name[0] == '.') {
|
||||
if (name[1] == 0) return true;
|
||||
if (name[1] == '.' && name[2] == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The linux-specific implementation is about 20% faster than the generic (posix) implementation.
|
||||
#ifdef __linux__
|
||||
|
||||
uint64_t Read64(const void* p) {
|
||||
uint64_t res;
|
||||
std::memcpy(&res, p, 8);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Write64(uint64_t x, void* p) { std::memcpy(p, &x, 8); }
|
||||
|
||||
void SwapBytes(char** begin, char** end) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
for (; begin != end; ++begin) Write64(__builtin_bswap64(Read64(*begin)), *begin);
|
||||
#elif __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
|
||||
#error "sorry, not implemented"
|
||||
#endif
|
||||
}
|
||||
|
||||
template <bool kCaseSensitive>
|
||||
void SortEntries(char** begin, char** end) {
|
||||
static_assert(kCaseSensitive, "");
|
||||
SwapBytes(begin, end);
|
||||
std::sort(begin, end, [](const char* a, const char* b) {
|
||||
uint64_t x = Read64(a);
|
||||
uint64_t y = Read64(b);
|
||||
// Add 5 for good luck.
|
||||
return x < y || (x == y && std::memcmp(a + 5, b + 5, 256) < 0);
|
||||
});
|
||||
SwapBytes(begin, end);
|
||||
}
|
||||
|
||||
template <>
|
||||
void SortEntries<false>(char** begin, char** end) {
|
||||
std::sort(begin, end, StrLt<false>());
|
||||
}
|
||||
|
||||
bool ListDir(int dir_fd, Arena& arena, std::vector<char*>& entries, bool precompose_unicode,
|
||||
bool case_sensitive) {
|
||||
struct linux_dirent64 {
|
||||
ino64_t d_ino;
|
||||
off64_t d_off;
|
||||
unsigned short d_reclen;
|
||||
unsigned char d_type;
|
||||
char d_name[];
|
||||
};
|
||||
|
||||
constexpr size_t kBufSize = 8 << 10;
|
||||
const size_t orig_size = entries.size();
|
||||
|
||||
while (true) {
|
||||
char* buf = static_cast<char*>(arena.Allocate(kBufSize, alignof(linux_dirent64)));
|
||||
// Save 256 bytes for the rainy day.
|
||||
int n = syscall(SYS_getdents64, dir_fd, buf, kBufSize - 256);
|
||||
if (n < 0) {
|
||||
entries.resize(orig_size);
|
||||
return false;
|
||||
}
|
||||
for (int pos = 0; pos < n;) {
|
||||
auto* ent = reinterpret_cast<linux_dirent64*>(buf + pos);
|
||||
if (!Dots(ent->d_name)) entries.push_back(ent->d_name);
|
||||
pos += ent->d_reclen;
|
||||
}
|
||||
if (n == 0) break;
|
||||
// The following optimization relies on SYS_getdents64 always returning as many
|
||||
// entries as would fit. This is not guaranteed by the specification and I don't
|
||||
// know if this is true in practice. The optimization has no measurable effect on
|
||||
// gitstatus performance, so it's turned off.
|
||||
//
|
||||
// if (n + sizeof(linux_dirent64) + 512 <= kBufSize) break;
|
||||
}
|
||||
|
||||
if (case_sensitive) {
|
||||
SortEntries<true>(entries.data() + orig_size, entries.data() + entries.size());
|
||||
} else {
|
||||
SortEntries<false>(entries.data() + orig_size, entries.data() + entries.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else // __linux__
|
||||
|
||||
namespace {
|
||||
|
||||
char* DirentDup(Arena& arena, const struct dirent& ent, size_t len) {
|
||||
char* p = arena.Allocate<char>(len + 2);
|
||||
*p++ = ent.d_type;
|
||||
std::memcpy(p, ent.d_name, len + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
std::atomic<bool> g_iconv_error(true);
|
||||
|
||||
Tribool IConvTry(char* inp, size_t ins, char* outp, size_t outs) {
|
||||
if (outs == 0) return Tribool::kUnknown;
|
||||
iconv_t ic = iconv_open("UTF-8", "UTF-8-MAC");
|
||||
if (ic == (iconv_t)-1) {
|
||||
if (g_iconv_error.load(std::memory_order_relaxed) &&
|
||||
g_iconv_error.exchange(false, std::memory_order_relaxed)) {
|
||||
LOG(ERROR) << "iconv_open(\"UTF-8\", \"UTF-8-MAC\") failed";
|
||||
}
|
||||
return Tribool::kFalse;
|
||||
}
|
||||
ON_SCOPE_EXIT(&) { CHECK(iconv_close(ic) == 0) << Errno(); };
|
||||
--outs;
|
||||
if (iconv(ic, &inp, &ins, &outp, &outs) >= 0) {
|
||||
*outp = 0;
|
||||
return Tribool::kTrue;
|
||||
}
|
||||
return errno == E2BIG ? Tribool::kUnknown : Tribool::kFalse;
|
||||
}
|
||||
|
||||
char* DirenvConvert(Arena& arena, struct dirent& ent, bool do_convert) {
|
||||
if (!do_convert) return DirentDup(arena, ent, std::strlen(ent.d_name));
|
||||
|
||||
size_t len = 0;
|
||||
do_convert = false;
|
||||
for (unsigned char c; (c = ent.d_name[len]); ++len) {
|
||||
if (c & 0x80) do_convert = true;
|
||||
}
|
||||
if (!do_convert) return DirentDup(arena, ent, len);
|
||||
|
||||
size_t n = NextPow2(len + 2);
|
||||
while (true) {
|
||||
char* p = arena.Allocate<char>(n);
|
||||
switch (IConvTry(ent.d_name, len, p + 1, n - 1)) {
|
||||
case Tribool::kFalse:
|
||||
return DirentDup(arena, ent, len);
|
||||
case Tribool::kTrue:
|
||||
*p = ent.d_type;
|
||||
return p + 1;
|
||||
case Tribool::kUnknown:
|
||||
break;
|
||||
}
|
||||
n *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
#else // __APPLE__
|
||||
|
||||
char* DirenvConvert(Arena& arena, struct dirent& ent, bool do_convert) {
|
||||
return DirentDup(arena, ent, std::strlen(ent.d_name));
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ListDir(int dir_fd, Arena& arena, std::vector<char*>& entries, bool precompose_unicode,
|
||||
bool case_sensitive) {
|
||||
const size_t orig_size = entries.size();
|
||||
dir_fd = dup(dir_fd);
|
||||
if (dir_fd < 0) return false;
|
||||
DIR* dir = fdopendir(dir_fd);
|
||||
if (!dir) {
|
||||
CHECK(!close(dir_fd)) << Errno();
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(&) { CHECK(!closedir(dir)) << Errno(); };
|
||||
while (struct dirent* ent = (errno = 0, readdir(dir))) {
|
||||
if (Dots(ent->d_name)) continue;
|
||||
entries.push_back(DirenvConvert(arena, *ent, precompose_unicode));
|
||||
}
|
||||
if (errno) {
|
||||
entries.resize(orig_size);
|
||||
return false;
|
||||
}
|
||||
StrSort(entries.data() + orig_size, entries.data() + entries.size(), case_sensitive);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,50 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_DIR_H_
|
||||
#define ROMKATV_GITSTATUS_DIR_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "arena.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// On error, leaves entries unchaged and returns false. Does not throw.
|
||||
//
|
||||
// On success, appends names of files from the specified directory to entries and returns true.
|
||||
// Every appended entry is a null-terminated string. At -1 offset is its d_type. All elements
|
||||
// point into the arena. They are sorted either by strcmp or strcasecmp depending on case_sensitive.
|
||||
//
|
||||
// Does not close dir_fd.
|
||||
//
|
||||
// There are two distinct implementations of ListDir -- one for Linux and another for everything
|
||||
// else. The linux-specific implementation is 20% faster.
|
||||
//
|
||||
// The reason sorting is bundled with directory listing is performance on Linux. The API of
|
||||
// getdents64 allows for much faster sorting than what can be done with a plain vector<char*>.
|
||||
// For the POSIX implementation there is no need to bundle sorting in this way. In fact, it's
|
||||
// done at the end with a generic StrSort() call.
|
||||
//
|
||||
// For best results, reuse the arena and vector for multiple calls to avoid heap allocations.
|
||||
bool ListDir(int dir_fd, Arena& arena, std::vector<char*>& entries, bool precompose_unicode,
|
||||
bool case_sensitive);
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_DIR_H_
|
250
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/git.cc
Normal file
250
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/git.cc
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "git.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "arena.h"
|
||||
#include "check.h"
|
||||
#include "print.h"
|
||||
#include "scope_guard.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
const char* GitError() {
|
||||
const git_error* err = git_error_last();
|
||||
return err && err->message ? err->message : "unknown error";
|
||||
}
|
||||
|
||||
std::string RepoState(git_repository* repo) {
|
||||
Arena arena;
|
||||
StringView gitdir(git_repository_path(repo));
|
||||
|
||||
// These names mostly match gitaction in vcs_info:
|
||||
// https://github.com/zsh-users/zsh/blob/master/Functions/VCS_Info/Backends/VCS_INFO_get_data_git.
|
||||
auto State = [&]() {
|
||||
switch (git_repository_state(repo)) {
|
||||
case GIT_REPOSITORY_STATE_NONE:
|
||||
return "";
|
||||
case GIT_REPOSITORY_STATE_MERGE:
|
||||
return "merge";
|
||||
case GIT_REPOSITORY_STATE_REVERT:
|
||||
return "revert";
|
||||
case GIT_REPOSITORY_STATE_REVERT_SEQUENCE:
|
||||
return "revert-seq";
|
||||
case GIT_REPOSITORY_STATE_CHERRYPICK:
|
||||
return "cherry";
|
||||
case GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE:
|
||||
return "cherry-seq";
|
||||
case GIT_REPOSITORY_STATE_BISECT:
|
||||
return "bisect";
|
||||
case GIT_REPOSITORY_STATE_REBASE:
|
||||
return "rebase";
|
||||
case GIT_REPOSITORY_STATE_REBASE_INTERACTIVE:
|
||||
return "rebase-i";
|
||||
case GIT_REPOSITORY_STATE_REBASE_MERGE:
|
||||
return "rebase-m";
|
||||
case GIT_REPOSITORY_STATE_APPLY_MAILBOX:
|
||||
return "am";
|
||||
case GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE:
|
||||
return "am/rebase";
|
||||
}
|
||||
return "action";
|
||||
};
|
||||
|
||||
auto DirExists = [&](StringView name) {
|
||||
int fd = open(arena.StrCat(gitdir, "/", name), O_DIRECTORY | O_CLOEXEC);
|
||||
if (fd < 0) return false;
|
||||
CHECK(!close(fd)) << Errno();
|
||||
return true;
|
||||
};
|
||||
|
||||
auto ReadFile = [&](StringView name) {
|
||||
std::ifstream strm(arena.StrCat(gitdir, "/", name));
|
||||
std::string res;
|
||||
strm >> res;
|
||||
return res;
|
||||
};
|
||||
|
||||
std::string next;
|
||||
std::string last;
|
||||
|
||||
if (DirExists("rebase-merge")) {
|
||||
next = ReadFile("rebase-merge/msgnum");
|
||||
last = ReadFile("rebase-merge/end");
|
||||
} else if (DirExists("rebase-apply")) {
|
||||
next = ReadFile("rebase-apply/next");
|
||||
last = ReadFile("rebase-apply/last");
|
||||
}
|
||||
|
||||
std::ostringstream res;
|
||||
res << State();
|
||||
if (!next.empty() && !last.empty()) res << ' ' << next << '/' << last;
|
||||
return res.str();
|
||||
}
|
||||
|
||||
size_t CountRange(git_repository* repo, const std::string& range) {
|
||||
git_revwalk* walk = nullptr;
|
||||
VERIFY(!git_revwalk_new(&walk, repo)) << GitError();
|
||||
ON_SCOPE_EXIT(=) { git_revwalk_free(walk); };
|
||||
VERIFY(!git_revwalk_push_range(walk, range.c_str())) << GitError();
|
||||
size_t res = 0;
|
||||
while (true) {
|
||||
git_oid oid;
|
||||
switch (git_revwalk_next(&oid, walk)) {
|
||||
case 0:
|
||||
++res;
|
||||
break;
|
||||
case GIT_ITEROVER:
|
||||
return res;
|
||||
default:
|
||||
LOG(ERROR) << "git_revwalk_next: " << range << ": " << GitError();
|
||||
throw Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t NumStashes(git_repository* repo) {
|
||||
size_t res = 0;
|
||||
auto* cb = +[](size_t index, const char* message, const git_oid* stash_id, void* payload) {
|
||||
++*static_cast<size_t*>(payload);
|
||||
return 0;
|
||||
};
|
||||
if (!git_stash_foreach(repo, cb, &res)) return res;
|
||||
// Example error: failed to parse signature - malformed e-mail.
|
||||
// See https://github.com/romkatv/powerlevel10k/issues/216.
|
||||
LOG(WARN) << "git_stash_foreach: " << GitError();
|
||||
return 0;
|
||||
}
|
||||
|
||||
git_reference* Head(git_repository* repo) {
|
||||
git_reference* symbolic = nullptr;
|
||||
switch (git_reference_lookup(&symbolic, repo, "HEAD")) {
|
||||
case 0:
|
||||
break;
|
||||
case GIT_ENOTFOUND:
|
||||
return nullptr;
|
||||
default:
|
||||
LOG(ERROR) << "git_reference_lookup: " << GitError();
|
||||
throw Exception();
|
||||
}
|
||||
|
||||
git_reference* direct = nullptr;
|
||||
if (git_reference_resolve(&direct, symbolic)) {
|
||||
LOG(INFO) << "Empty git repo (no HEAD)";
|
||||
return symbolic;
|
||||
}
|
||||
git_reference_free(symbolic);
|
||||
return direct;
|
||||
}
|
||||
|
||||
const char* LocalBranchName(const git_reference* ref) {
|
||||
CHECK(ref);
|
||||
git_reference_t type = git_reference_type(ref);
|
||||
switch (type) {
|
||||
case GIT_REFERENCE_DIRECT: {
|
||||
return git_reference_is_branch(ref) ? git_reference_shorthand(ref) : "";
|
||||
}
|
||||
case GIT_REFERENCE_SYMBOLIC: {
|
||||
static constexpr char kHeadPrefix[] = "refs/heads/";
|
||||
const char* target = git_reference_symbolic_target(ref);
|
||||
if (!target) return "";
|
||||
size_t len = std::strlen(target);
|
||||
if (len < sizeof(kHeadPrefix)) return "";
|
||||
if (std::memcmp(target, kHeadPrefix, sizeof(kHeadPrefix) - 1)) return "";
|
||||
return target + (sizeof(kHeadPrefix) - 1);
|
||||
}
|
||||
case GIT_REFERENCE_INVALID:
|
||||
case GIT_REFERENCE_ALL:
|
||||
break;
|
||||
}
|
||||
LOG(ERROR) << "Invalid reference type: " << type;
|
||||
throw Exception();
|
||||
}
|
||||
|
||||
RemotePtr GetRemote(git_repository* repo, const git_reference* local) {
|
||||
git_remote* remote;
|
||||
git_buf symref = {};
|
||||
if (git_branch_remote(&remote, &symref, repo, git_reference_name(local))) return nullptr;
|
||||
ON_SCOPE_EXIT(&) {
|
||||
git_remote_free(remote);
|
||||
git_buf_free(&symref);
|
||||
};
|
||||
|
||||
git_reference* ref;
|
||||
if (git_reference_lookup(&ref, repo, symref.ptr)) return nullptr;
|
||||
ON_SCOPE_EXIT(&) { if (ref) git_reference_free(ref); };
|
||||
|
||||
const char* branch = nullptr;
|
||||
std::string name = remote ? git_remote_name(remote) : ".";
|
||||
if (git_branch_name(&branch, ref)) {
|
||||
branch = "";
|
||||
} else if (remote) {
|
||||
VERIFY(std::strstr(branch, name.c_str()) == branch);
|
||||
VERIFY(branch[name.size()] == '/');
|
||||
branch += name.size() + 1;
|
||||
}
|
||||
|
||||
auto res = std::make_unique<Remote>();
|
||||
res->name = std::move(name);
|
||||
res->branch = branch;
|
||||
res->url = remote ? (git_remote_url(remote) ?: "") : "";
|
||||
res->ref = std::exchange(ref, nullptr);
|
||||
return RemotePtr(res.release());
|
||||
}
|
||||
|
||||
PushRemotePtr GetPushRemote(git_repository* repo, const git_reference* local) {
|
||||
git_remote* remote;
|
||||
git_buf symref = {};
|
||||
if (git_branch_push_remote(&remote, &symref, repo, git_reference_name(local))) return nullptr;
|
||||
ON_SCOPE_EXIT(&) {
|
||||
git_remote_free(remote);
|
||||
git_buf_free(&symref);
|
||||
};
|
||||
|
||||
git_reference* ref;
|
||||
if (git_reference_lookup(&ref, repo, symref.ptr)) return nullptr;
|
||||
ON_SCOPE_EXIT(&) { if (ref) git_reference_free(ref); };
|
||||
|
||||
std::string name = remote ? git_remote_name(remote) : ".";
|
||||
|
||||
auto res = std::make_unique<PushRemote>();
|
||||
res->name = std::move(name);
|
||||
res->url = remote ? (git_remote_url(remote) ?: "") : "";
|
||||
res->ref = std::exchange(ref, nullptr);
|
||||
return PushRemotePtr(res.release());
|
||||
}
|
||||
|
||||
CommitMessage GetCommitMessage(git_repository* repo, const git_oid& id) {
|
||||
git_commit* commit;
|
||||
VERIFY(!git_commit_lookup(&commit, repo, &id)) << GitError();
|
||||
ON_SCOPE_EXIT(=) { git_commit_free(commit); };
|
||||
return {.encoding = git_commit_message_encoding(commit) ?: "",
|
||||
.summary = git_commit_summary(commit) ?: ""};
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
115
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/git.h
Normal file
115
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/git.h
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_GIT_H_
|
||||
#define ROMKATV_GITSTATUS_GIT_H_
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// Not null.
|
||||
const char* GitError();
|
||||
|
||||
// Not null.
|
||||
std::string RepoState(git_repository* repo);
|
||||
|
||||
// Returns the number of commits in the range.
|
||||
size_t CountRange(git_repository* repo, const std::string& range);
|
||||
|
||||
// How many stashes are there?
|
||||
size_t NumStashes(git_repository* repo);
|
||||
|
||||
// Returns the origin URL or an empty string. Not null.
|
||||
std::string RemoteUrl(git_repository* repo, const git_reference* ref);
|
||||
|
||||
// Returns reference to HEAD or null if not found. The reference is symbolic if the repo is empty
|
||||
// and direct otherwise.
|
||||
git_reference* Head(git_repository* repo);
|
||||
|
||||
// Returns the name of the local branch, or an empty string.
|
||||
const char* LocalBranchName(const git_reference* ref);
|
||||
|
||||
struct CommitMessage {
|
||||
// Can be empty, meaning "UTF-8".
|
||||
std::string encoding;
|
||||
// The first paragraph of the commit's message as a one-liner.
|
||||
std::string summary;
|
||||
};
|
||||
|
||||
CommitMessage GetCommitMessage(git_repository* repo, const git_oid& id);
|
||||
|
||||
struct Remote {
|
||||
// Tip of the remote branch.
|
||||
git_reference* ref;
|
||||
|
||||
// Name of the tracking remote. For example, "origin".
|
||||
std::string name;
|
||||
|
||||
// Name of the tracking remote branch. For example, "master".
|
||||
std::string branch;
|
||||
|
||||
// URL of the tracking remote. For example, "https://foo.com/repo.git".
|
||||
std::string url;
|
||||
|
||||
// Note: pushurl is not exposed (but could be).
|
||||
|
||||
struct Free {
|
||||
void operator()(const Remote* p) const {
|
||||
if (p) {
|
||||
if (p->ref) git_reference_free(p->ref);
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct PushRemote {
|
||||
// Tip of the remote branch.
|
||||
git_reference* ref;
|
||||
|
||||
// Name of the tracking remote. For example, "origin".
|
||||
std::string name;
|
||||
|
||||
// URL of the tracking remote. For example, "https://foo.com/repo.git".
|
||||
std::string url;
|
||||
|
||||
// Note: pushurl is not exposed (but could be).
|
||||
|
||||
struct Free {
|
||||
void operator()(const PushRemote* p) const {
|
||||
if (p) {
|
||||
if (p->ref) git_reference_free(p->ref);
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using RemotePtr = std::unique_ptr<Remote, Remote::Free>;
|
||||
using PushRemotePtr = std::unique_ptr<PushRemote, PushRemote::Free>;
|
||||
|
||||
RemotePtr GetRemote(git_repository* repo, const git_reference* local);
|
||||
PushRemotePtr GetPushRemote(git_repository* repo, const git_reference* local);
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_GIT_H_
|
@ -0,0 +1,219 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include "check.h"
|
||||
#include "git.h"
|
||||
#include "logging.h"
|
||||
#include "options.h"
|
||||
#include "print.h"
|
||||
#include "repo.h"
|
||||
#include "repo_cache.h"
|
||||
#include "request.h"
|
||||
#include "response.h"
|
||||
#include "scope_guard.h"
|
||||
#include "thread_pool.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace gitstatus {
|
||||
namespace {
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
void Truncate(std::string& s, size_t max_len) {
|
||||
if (s.size() > max_len) s.resize(max_len);
|
||||
}
|
||||
|
||||
void ProcessRequest(const Options& opts, RepoCache& cache, Request req) {
|
||||
Timer timer;
|
||||
ON_SCOPE_EXIT(&) { timer.Report("request"); };
|
||||
|
||||
ResponseWriter resp(req.id);
|
||||
Repo* repo = cache.Open(req.dir, req.from_dotgit);
|
||||
if (!repo) return;
|
||||
|
||||
git_config* cfg;
|
||||
VERIFY(!git_repository_config(&cfg, repo->repo())) << GitError();
|
||||
ON_SCOPE_EXIT(=) { git_config_free(cfg); };
|
||||
VERIFY(!git_config_refresh(cfg)) << GitError();
|
||||
|
||||
// Symbolic reference if and only if the repo is empty.
|
||||
git_reference* head = Head(repo->repo());
|
||||
if (!head) return;
|
||||
ON_SCOPE_EXIT(=) { git_reference_free(head); };
|
||||
|
||||
// Null if and only if the repo is empty.
|
||||
const git_oid* head_target = git_reference_target(head);
|
||||
|
||||
// Looking up tags may take some time. Do it in the background while we check for stuff.
|
||||
// Note that GetTagName() doesn't access index, so it'll overlap with index reading and
|
||||
// parsing.
|
||||
std::future<std::string> tag = repo->GetTagName(head_target);
|
||||
ON_SCOPE_EXIT(&) {
|
||||
if (tag.valid()) {
|
||||
try {
|
||||
tag.wait();
|
||||
} catch (const Exception&) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Repository working directory. Absolute; no trailing slash. E.g., "/home/romka/gitstatus".
|
||||
StringView workdir(git_repository_workdir(repo->repo()));
|
||||
if (workdir.len == 0) return;
|
||||
if (workdir.len > 1 && workdir.ptr[workdir.len - 1] == '/') --workdir.len;
|
||||
resp.Print(workdir);
|
||||
|
||||
// Revision. Either 40 hex digits or an empty string for empty repo.
|
||||
resp.Print(head_target ? git_oid_tostr_s(head_target) : "");
|
||||
|
||||
// Local branch name (e.g., "master") or empty string if not on a branch.
|
||||
resp.Print(LocalBranchName(head));
|
||||
|
||||
// Remote tracking branch or null.
|
||||
RemotePtr remote = GetRemote(repo->repo(), head);
|
||||
|
||||
// Tracking remote branch name (e.g., "master") or empty string if there is no tracking remote.
|
||||
resp.Print(remote ? remote->branch : "");
|
||||
|
||||
// Tracking remote name (e.g., "origin") or empty string if there is no tracking remote.
|
||||
resp.Print(remote ? remote->name : "");
|
||||
|
||||
// Tracking remote URL or empty string if there is no tracking remote.
|
||||
resp.Print(remote ? remote->url : "");
|
||||
|
||||
// Repository state, A.K.A. action. For example, "merge".
|
||||
resp.Print(RepoState(repo->repo()));
|
||||
|
||||
IndexStats stats;
|
||||
// Look for staged, unstaged and untracked. This is where most of the time is spent.
|
||||
if (req.diff) stats = repo->GetIndexStats(head_target, cfg);
|
||||
|
||||
// The number of files in the index.
|
||||
resp.Print(stats.index_size);
|
||||
// The number of staged changes. At most opts.max_num_staged.
|
||||
resp.Print(stats.num_staged);
|
||||
// The number of unstaged changes. At most opts.max_num_unstaged. 0 if index is too large.
|
||||
resp.Print(stats.num_unstaged);
|
||||
// The number of conflicted changes. At most opts.max_num_conflicted. 0 if index is too large.
|
||||
resp.Print(stats.num_conflicted);
|
||||
// The number of untracked changes. At most opts.max_num_untracked. 0 if index is too large.
|
||||
resp.Print(stats.num_untracked);
|
||||
|
||||
if (remote && remote->ref) {
|
||||
const char* ref = git_reference_name(remote->ref);
|
||||
// Number of commits we are ahead of upstream. Non-negative integer.
|
||||
resp.Print(CountRange(repo->repo(), ref + "..HEAD"s));
|
||||
// Number of commits we are behind upstream. Non-negative integer.
|
||||
resp.Print(CountRange(repo->repo(), "HEAD.."s + ref));
|
||||
} else {
|
||||
resp.Print("0");
|
||||
resp.Print("0");
|
||||
}
|
||||
|
||||
// Number of stashes. Non-negative integer.
|
||||
resp.Print(NumStashes(repo->repo()));
|
||||
|
||||
// Tag that points to HEAD (e.g., "v4.2") or empty string if there aren't any. The same as
|
||||
// `git describe --tags --exact-match`.
|
||||
resp.Print(tag.get());
|
||||
|
||||
// The number of unstaged deleted files. At most stats.num_unstaged.
|
||||
resp.Print(stats.num_unstaged_deleted);
|
||||
// The number of staged new files. At most stats.num_staged.
|
||||
resp.Print(stats.num_staged_new);
|
||||
// The number of staged deleted files. At most stats.num_staged.
|
||||
resp.Print(stats.num_staged_deleted);
|
||||
|
||||
// Push remote or null.
|
||||
PushRemotePtr push_remote = GetPushRemote(repo->repo(), head);
|
||||
|
||||
// Push remote name (e.g., "origin") or empty string if there is no push remote.
|
||||
resp.Print(push_remote ? push_remote->name : "");
|
||||
|
||||
// Push remote URL or empty string if there is no push remote.
|
||||
resp.Print(push_remote ? push_remote->url : "");
|
||||
|
||||
if (push_remote && push_remote->ref) {
|
||||
const char* ref = git_reference_name(push_remote->ref);
|
||||
// Number of commits we are ahead of push remote. Non-negative integer.
|
||||
resp.Print(CountRange(repo->repo(), ref + "..HEAD"s));
|
||||
// Number of commits we are behind upstream. Non-negative integer.
|
||||
resp.Print(CountRange(repo->repo(), "HEAD.."s + ref));
|
||||
} else {
|
||||
resp.Print("0");
|
||||
resp.Print("0");
|
||||
}
|
||||
|
||||
// The number of files in the index with skip-worktree bit set.
|
||||
resp.Print(stats.num_skip_worktree);
|
||||
// The number of files in the index with assume-unchanged bit set.
|
||||
resp.Print(stats.num_assume_unchanged);
|
||||
|
||||
CommitMessage msg = head_target ? GetCommitMessage(repo->repo(), *head_target) : CommitMessage();
|
||||
Truncate(msg.summary, opts.max_commit_summary_length);
|
||||
resp.Print(msg.encoding);
|
||||
resp.Print(msg.summary);
|
||||
|
||||
resp.Dump("with git status");
|
||||
}
|
||||
|
||||
int GitStatus(int argc, char** argv) {
|
||||
tzset();
|
||||
Options opts = ParseOptions(argc, argv);
|
||||
g_min_log_level = opts.log_level;
|
||||
for (int i = 0; i != argc; ++i) LOG(INFO) << "argv[" << i << "]: " << Print(argv[i]);
|
||||
RequestReader reader(fileno(stdin), opts.lock_fd, opts.parent_pid);
|
||||
RepoCache cache(opts);
|
||||
|
||||
InitGlobalThreadPool(opts.num_threads);
|
||||
git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0);
|
||||
git_libgit2_opts(GIT_OPT_DISABLE_INDEX_CHECKSUM_VERIFICATION, 1);
|
||||
git_libgit2_opts(GIT_OPT_DISABLE_INDEX_FILEPATH_VALIDATION, 1);
|
||||
git_libgit2_opts(GIT_OPT_DISABLE_READNG_PACKED_TAGS, 1);
|
||||
git_libgit2_init();
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
Request req;
|
||||
if (reader.ReadRequest(req)) {
|
||||
LOG(INFO) << "Processing request: " << req;
|
||||
try {
|
||||
ProcessRequest(opts, cache, req);
|
||||
LOG(INFO) << "Successfully processed request: " << req;
|
||||
} catch (const Exception&) {
|
||||
LOG(ERROR) << "Error processing request: " << req;
|
||||
}
|
||||
} else if (opts.repo_ttl >= Duration()) {
|
||||
cache.Free(Clock::now() - opts.repo_ttl);
|
||||
}
|
||||
} catch (const Exception&) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace gitstatus
|
||||
|
||||
int main(int argc, char** argv) { gitstatus::GitStatus(argc, argv); }
|
@ -0,0 +1,456 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "index.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <stack>
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "check.h"
|
||||
#include "dir.h"
|
||||
#include "git.h"
|
||||
#include "index.h"
|
||||
#include "print.h"
|
||||
#include "scope_guard.h"
|
||||
#include "stat.h"
|
||||
#include "string_cmp.h"
|
||||
#include "thread_pool.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
void CommonDir(Str<> str, const char* a, const char* b, size_t* dir_len, size_t* dir_depth) {
|
||||
*dir_len = 0;
|
||||
*dir_depth = 0;
|
||||
for (size_t i = 1; str.Eq(*a, *b) && *a; ++i, ++a, ++b) {
|
||||
if (*a == '/') {
|
||||
*dir_len = i;
|
||||
++*dir_depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Weight(const IndexDir& dir) { return 1 + dir.subdirs.size() + dir.files.size(); }
|
||||
|
||||
bool MTimeEq(const git_index_time& index, const struct timespec& workdir) {
|
||||
if (index.seconds != workdir.tv_sec) return false;
|
||||
if (int64_t{index.nanoseconds} == workdir.tv_nsec) return true;
|
||||
#ifdef GITSTATUS_ZERO_NSEC
|
||||
return index.nanoseconds == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsModified(const git_index_entry* entry, const struct stat& st, const RepoCaps& caps) {
|
||||
mode_t mode = st.st_mode;
|
||||
if (S_ISREG(mode)) {
|
||||
if (!caps.has_symlinks && S_ISLNK(entry->mode)) {
|
||||
mode = entry->mode;
|
||||
} else if (!caps.trust_filemode) {
|
||||
mode = entry->mode;
|
||||
} else {
|
||||
mode = S_IFREG | (mode & 0100 ? 0755 : 0644);
|
||||
}
|
||||
} else {
|
||||
mode &= S_IFMT;
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
|
||||
#define COND(field, cond...) \
|
||||
if (cond) { \
|
||||
} else \
|
||||
res = true, \
|
||||
LOG(DEBUG) << "Dirty candidate (modified): " << Print(entry->path) << ": " #field " "
|
||||
|
||||
COND(ino, !entry->ino || entry->ino == static_cast<std::uint32_t>(st.st_ino))
|
||||
<< entry->ino << " => " << static_cast<std::uint32_t>(st.st_ino);
|
||||
|
||||
COND(stage, GIT_INDEX_ENTRY_STAGE(entry) == 0) << "=> " << GIT_INDEX_ENTRY_STAGE(entry);
|
||||
COND(fsize, int64_t{entry->file_size} == st.st_size) << entry->file_size << " => " << st.st_size;
|
||||
COND(mtime, MTimeEq(entry->mtime, MTim(st))) << Print(entry->mtime) << " => " << Print(MTim(st));
|
||||
COND(mode, entry->mode == mode) << std::oct << entry->mode << " => " << std::oct << mode;
|
||||
|
||||
#undef COND
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int OpenDir(int parent_fd, const char* name) {
|
||||
return openat(parent_fd, name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
}
|
||||
|
||||
void OpenTail(int* fds, size_t nfds, int root_fd, StringView dirname, Arena& arena) {
|
||||
CHECK(fds && nfds && root_fd >= 0);
|
||||
std::fill(fds, fds + nfds, -1);
|
||||
if (!dirname.len) return;
|
||||
CHECK(dirname.len > 1);
|
||||
CHECK(dirname.ptr[0] != '/');
|
||||
CHECK(dirname.ptr[dirname.len - 1] == '/');
|
||||
|
||||
char* begin = arena.StrDup(dirname.ptr, dirname.len - 1);
|
||||
WithArena<std::vector<const char*>> subdirs(&arena);
|
||||
subdirs.reserve(nfds + 1);
|
||||
|
||||
for (char* sep = begin + dirname.len - 1; subdirs.size() < nfds;) {
|
||||
sep = FindLast(begin, sep, '/');
|
||||
if (sep == begin) break;
|
||||
*sep = 0;
|
||||
subdirs.push_back(sep + 1);
|
||||
}
|
||||
subdirs.push_back(begin);
|
||||
if (subdirs.size() < nfds + 1) subdirs.push_back(".");
|
||||
CHECK(subdirs.size() <= nfds + 1);
|
||||
|
||||
for (size_t i = subdirs.size(); i != 1; --i) {
|
||||
const char* path = subdirs[i - 1];
|
||||
if ((root_fd = OpenDir(root_fd, path)) < 0) {
|
||||
for (; i != subdirs.size(); ++i) {
|
||||
CHECK(!close(fds[i - 1])) << Errno();
|
||||
fds[i - 1] = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
fds[i - 2] = root_fd;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const char*> ScanDirs(git_index* index, int root_fd, IndexDir* const* begin,
|
||||
IndexDir* const* end, const RepoCaps& caps,
|
||||
const ScanOpts& opts) {
|
||||
const Str<> str(caps.case_sensitive);
|
||||
|
||||
Arena arena;
|
||||
std::vector<const char*> dirty_candidates;
|
||||
std::vector<char*> entries;
|
||||
entries.reserve(128);
|
||||
|
||||
auto AddCandidate = [&](const char* kind, const char* path) {
|
||||
if (kind) LOG(DEBUG) << "Dirty candidate (" << kind << "): " << Print(path);
|
||||
dirty_candidates.push_back(path);
|
||||
};
|
||||
|
||||
constexpr ssize_t kDirStackSize = 5;
|
||||
int dir_fd[kDirStackSize];
|
||||
std::fill(std::begin(dir_fd), std::end(dir_fd), -1);
|
||||
auto Close = [](int& fd) {
|
||||
if (fd >= 0) {
|
||||
CHECK(!close(fd)) << Errno();
|
||||
fd = -1;
|
||||
}
|
||||
};
|
||||
auto CloseAll = [&] { std::for_each(std::begin(dir_fd), std::end(dir_fd), Close); };
|
||||
ON_SCOPE_EXIT(&) { CloseAll(); };
|
||||
if (begin != end) OpenTail(dir_fd, kDirStackSize, root_fd, (*begin)->path, arena);
|
||||
|
||||
for (IndexDir* const* it = begin; it != end; ++it) {
|
||||
IndexDir& dir = **it;
|
||||
|
||||
auto Basename = [&](const git_index_entry* e) { return e->path + dir.path.len; };
|
||||
|
||||
auto AddUnmached = [&](StringView basename) {
|
||||
if (!basename.len) {
|
||||
dir.st = {};
|
||||
dir.unmatched.clear();
|
||||
dir.arena.Reuse();
|
||||
} else if (str.Eq(basename, StringView(".git/"))) {
|
||||
return;
|
||||
}
|
||||
char* path = dir.arena.StrCat(dir.path, basename);
|
||||
dir.unmatched.push_back(path);
|
||||
AddCandidate(basename.len ? "new" : "unreadable", path);
|
||||
};
|
||||
|
||||
auto StatFiles = [&]() {
|
||||
struct stat st;
|
||||
for (const git_index_entry* file : dir.files) {
|
||||
if (fstatat(*dir_fd, Basename(file), &st, AT_SYMLINK_NOFOLLOW)) {
|
||||
AddCandidate(errno == ENOENT ? "deleted" : "unreadable", file->path);
|
||||
} else if (IsModified(file, st, caps)) {
|
||||
AddCandidate(nullptr, file->path);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ssize_t d = 0;
|
||||
if ((it == begin || (d = it[-1]->depth + 1 - dir.depth) < kDirStackSize) && dir_fd[d] >= 0) {
|
||||
CHECK(d >= 0);
|
||||
int fd = OpenDir(dir_fd[d], arena.StrDup(dir.basename.ptr, dir.basename.len));
|
||||
for (ssize_t i = 0; i != d; ++i) Close(dir_fd[i]);
|
||||
std::rotate(dir_fd, dir_fd + (d ? d : kDirStackSize) - 1, dir_fd + kDirStackSize);
|
||||
Close(*dir_fd);
|
||||
*dir_fd = fd;
|
||||
} else {
|
||||
CloseAll();
|
||||
if (dir.path.len) {
|
||||
CHECK(dir.path.ptr[0] != '/');
|
||||
CHECK(dir.path.ptr[dir.path.len - 1] == '/');
|
||||
*dir_fd = OpenDir(root_fd, arena.StrDup(dir.path.ptr, dir.path.len - 1));
|
||||
} else {
|
||||
VERIFY((*dir_fd = dup(root_fd)) >= 0) << Errno();
|
||||
}
|
||||
}
|
||||
if (*dir_fd < 0) {
|
||||
CloseAll();
|
||||
AddUnmached("");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!opts.include_untracked) {
|
||||
StatFiles();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opts.untracked_cache != Tribool::kFalse) {
|
||||
struct stat st;
|
||||
if (fstat(*dir_fd, &st)) {
|
||||
AddUnmached("");
|
||||
continue;
|
||||
}
|
||||
if (opts.untracked_cache == Tribool::kTrue && StatEq(st, dir.st)) {
|
||||
StatFiles();
|
||||
for (const char* path : dir.unmatched) AddCandidate("new", path);
|
||||
continue;
|
||||
}
|
||||
dir.st = st;
|
||||
}
|
||||
|
||||
entries.clear();
|
||||
arena.Reuse();
|
||||
if (!ListDir(*dir_fd, arena, entries, caps.precompose_unicode, caps.case_sensitive)) {
|
||||
AddUnmached("");
|
||||
continue;
|
||||
}
|
||||
dir.unmatched.clear();
|
||||
dir.arena.Reuse();
|
||||
|
||||
const git_index_entry* const* file = dir.files.data();
|
||||
const git_index_entry* const* file_end = file + dir.files.size();
|
||||
const StringView* subdir = dir.subdirs.data();
|
||||
const StringView* subdir_end = subdir + dir.subdirs.size();
|
||||
|
||||
for (char* entry : entries) {
|
||||
bool matched = false;
|
||||
|
||||
for (; file != file_end; ++file) {
|
||||
int cmp = str.Cmp(Basename(*file), entry);
|
||||
if (cmp < 0) {
|
||||
AddCandidate("deleted", (*file)->path);
|
||||
} else if (cmp == 0) {
|
||||
struct stat st;
|
||||
if (fstatat(*dir_fd, entry, &st, AT_SYMLINK_NOFOLLOW)) {
|
||||
AddCandidate("unreadable", (*file)->path);
|
||||
} else if (IsModified(*file, st, caps)) {
|
||||
AddCandidate(nullptr, (*file)->path);
|
||||
}
|
||||
matched = true;
|
||||
++file;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched) continue;
|
||||
|
||||
for (; subdir != subdir_end; ++subdir) {
|
||||
int cmp = str.Cmp(*subdir, entry);
|
||||
if (cmp > 0) break;
|
||||
if (cmp == 0) {
|
||||
matched = true;
|
||||
++subdir;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
StringView basename(entry);
|
||||
if (entry[-1] == DT_DIR) entry[basename.len++] = '/';
|
||||
AddUnmached(basename);
|
||||
}
|
||||
}
|
||||
|
||||
for (; file != file_end; ++file) AddCandidate("deleted", (*file)->path);
|
||||
}
|
||||
|
||||
return dirty_candidates;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RepoCaps::RepoCaps(git_repository* repo, git_index* index) {
|
||||
trust_filemode = git_index_is_filemode_trustworthy(index);
|
||||
has_symlinks = git_index_supports_symlinks(index);
|
||||
case_sensitive = git_index_is_case_sensitive(index);
|
||||
precompose_unicode = git_index_precompose_unicode(index);
|
||||
LOG(DEBUG) << "Repository capabilities for " << Print(git_repository_workdir(repo)) << ": "
|
||||
<< "is_filemode_trustworthy = " << std::boolalpha << trust_filemode << ", "
|
||||
<< "index_supports_symlinks = " << std::boolalpha << has_symlinks << ", "
|
||||
<< "index_is_case_sensitive = " << std::boolalpha << case_sensitive << ", "
|
||||
<< "precompose_unicode = " << std::boolalpha << precompose_unicode;
|
||||
}
|
||||
|
||||
Index::Index(git_repository* repo, git_index* index)
|
||||
: dirs_(&arena_),
|
||||
splits_(&arena_),
|
||||
git_index_(index),
|
||||
root_dir_(git_repository_workdir(repo)),
|
||||
caps_(repo, index) {
|
||||
size_t total_weight = InitDirs(index);
|
||||
InitSplits(total_weight);
|
||||
}
|
||||
|
||||
size_t Index::InitDirs(git_index* index) {
|
||||
const Str<> str(git_index_is_case_sensitive(index));
|
||||
const size_t index_size = git_index_entrycount(index);
|
||||
dirs_.reserve(index_size / 8);
|
||||
std::stack<IndexDir*> stack;
|
||||
stack.push(arena_.DirectInit<IndexDir>(&arena_));
|
||||
|
||||
size_t total_weight = 0;
|
||||
auto PopDir = [&] {
|
||||
CHECK(!stack.empty());
|
||||
IndexDir* top = stack.top();
|
||||
CHECK(top->depth + 1 == stack.size());
|
||||
if (!std::is_sorted(top->subdirs.begin(), top->subdirs.end(), str.Lt)) {
|
||||
StrSort(top->subdirs.begin(), top->subdirs.end(), str.case_sensitive);
|
||||
}
|
||||
total_weight += Weight(*top);
|
||||
dirs_.push_back(top);
|
||||
stack.pop();
|
||||
};
|
||||
|
||||
for (size_t i = 0; i != index_size; ++i) {
|
||||
const git_index_entry* entry = git_index_get_byindex_no_sort(index, i);
|
||||
IndexDir* prev = stack.top();
|
||||
size_t common_len, common_depth;
|
||||
CommonDir(str, prev->path.ptr, entry->path, &common_len, &common_depth);
|
||||
CHECK(common_depth <= prev->depth);
|
||||
|
||||
for (size_t i = common_depth; i != prev->depth; ++i) PopDir();
|
||||
|
||||
for (const char* p = entry->path + common_len; (p = std::strchr(p, '/')); ++p) {
|
||||
IndexDir* top = stack.top();
|
||||
StringView subdir(entry->path + top->path.len, p);
|
||||
top->subdirs.push_back(subdir);
|
||||
IndexDir* dir = arena_.DirectInit<IndexDir>(&arena_);
|
||||
dir->path = StringView(entry->path, p - entry->path + 1);
|
||||
dir->basename = subdir;
|
||||
dir->depth = stack.size();
|
||||
CHECK(dir->path.ptr[dir->path.len - 1] == '/');
|
||||
stack.push(dir);
|
||||
}
|
||||
|
||||
CHECK(!stack.empty());
|
||||
IndexDir* dir = stack.top();
|
||||
dir->files.push_back(entry);
|
||||
}
|
||||
|
||||
CHECK(!stack.empty());
|
||||
do {
|
||||
PopDir();
|
||||
} while (!stack.empty());
|
||||
std::reverse(dirs_.begin(), dirs_.end());
|
||||
|
||||
return total_weight;
|
||||
}
|
||||
|
||||
void Index::InitSplits(size_t total_weight) {
|
||||
constexpr size_t kMinShardWeight = 512;
|
||||
const size_t kNumShards = 16 * GlobalThreadPool()->num_threads();
|
||||
const size_t shard_weight = std::max(kMinShardWeight, total_weight / kNumShards);
|
||||
|
||||
splits_.reserve(kNumShards + 1);
|
||||
splits_.push_back(0);
|
||||
|
||||
for (size_t i = 0, w = 0; i != dirs_.size(); ++i) {
|
||||
w += Weight(*dirs_[i]);
|
||||
if (w >= shard_weight) {
|
||||
w = 0;
|
||||
splits_.push_back(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (splits_.back() != dirs_.size()) splits_.push_back(dirs_.size());
|
||||
CHECK(splits_.size() <= kNumShards + 1);
|
||||
CHECK(std::is_sorted(splits_.begin(), splits_.end()));
|
||||
CHECK(std::adjacent_find(splits_.begin(), splits_.end()) == splits_.end());
|
||||
}
|
||||
|
||||
std::vector<const char*> Index::GetDirtyCandidates(const ScanOpts& opts) {
|
||||
int root_fd = open(root_dir_, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
VERIFY(root_fd >= 0);
|
||||
ON_SCOPE_EXIT(&) { CHECK(!close(root_fd)) << Errno(); };
|
||||
|
||||
CHECK(!splits_.empty());
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
size_t inflight = splits_.size() - 1;
|
||||
bool error = false;
|
||||
std::vector<const char*> res;
|
||||
|
||||
for (size_t i = 0; i != splits_.size() - 1; ++i) {
|
||||
size_t from = splits_[i];
|
||||
size_t to = splits_[i + 1];
|
||||
|
||||
GlobalThreadPool()->Schedule([&, from, to]() {
|
||||
ON_SCOPE_EXIT(&) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
CHECK(inflight);
|
||||
if (--inflight == 0) cv.notify_one();
|
||||
};
|
||||
try {
|
||||
std::vector<const char*> candidates =
|
||||
ScanDirs(git_index_, root_fd, dirs_.data() + from, dirs_.data() + to, caps_, opts);
|
||||
if (!candidates.empty()) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
res.insert(res.end(), candidates.begin(), candidates.end());
|
||||
}
|
||||
} catch (const Exception&) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
error = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
while (inflight) cv.wait(lock);
|
||||
}
|
||||
|
||||
VERIFY(!error);
|
||||
StrSort(res.begin(), res.end(), git_index_is_case_sensitive(git_index_));
|
||||
auto StrEq = [](const char* a, const char* b) { return !strcmp(a, b); };
|
||||
res.erase(std::unique(res.begin(), res.end(), StrEq), res.end());
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,84 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_INDEX_H_
|
||||
#define ROMKATV_GITSTATUS_INDEX_H_
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "arena.h"
|
||||
#include "options.h"
|
||||
#include "string_view.h"
|
||||
#include "tribool.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
struct RepoCaps {
|
||||
RepoCaps(git_repository* repo, git_index* index);
|
||||
|
||||
bool trust_filemode;
|
||||
bool has_symlinks;
|
||||
bool case_sensitive;
|
||||
bool precompose_unicode;
|
||||
};
|
||||
|
||||
struct ScanOpts {
|
||||
bool include_untracked;
|
||||
Tribool untracked_cache;
|
||||
};
|
||||
|
||||
struct IndexDir {
|
||||
explicit IndexDir(Arena* arena) : files(arena), subdirs(arena) {}
|
||||
|
||||
StringView path;
|
||||
StringView basename;
|
||||
size_t depth = 0;
|
||||
struct stat st = {};
|
||||
WithArena<std::vector<const git_index_entry*>> files;
|
||||
WithArena<std::vector<StringView>> subdirs;
|
||||
|
||||
Arena arena;
|
||||
std::vector<const char*> unmatched;
|
||||
};
|
||||
|
||||
class Index {
|
||||
public:
|
||||
Index(git_repository* repo, git_index* index);
|
||||
|
||||
std::vector<const char*> GetDirtyCandidates(const ScanOpts& opts);
|
||||
|
||||
private:
|
||||
size_t InitDirs(git_index* index);
|
||||
void InitSplits(size_t total_weight);
|
||||
|
||||
Arena arena_;
|
||||
WithArena<std::vector<IndexDir*>> dirs_;
|
||||
WithArena<std::vector<size_t>> splits_;
|
||||
git_index* git_index_;
|
||||
const char* root_dir_;
|
||||
RepoCaps caps_;
|
||||
};
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_GIT_H_
|
@ -0,0 +1,139 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace internal_logging {
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex g_log_mutex;
|
||||
|
||||
constexpr char kHexLower[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
void FormatThreadId(char (&out)[2 * sizeof(std::uintptr_t) + 1]) {
|
||||
std::uintptr_t tid = (std::uintptr_t)pthread_self();
|
||||
char* p = out + sizeof(out) - 1;
|
||||
*p = 0;
|
||||
do {
|
||||
--p;
|
||||
*p = kHexLower[tid & 0xF];
|
||||
tid >>= 4;
|
||||
} while (p != out);
|
||||
}
|
||||
|
||||
void FormatCurrentTime(char (&out)[64]) {
|
||||
std::time_t time = std::time(nullptr);
|
||||
struct tm tm;
|
||||
if (localtime_r(&time, &tm) != &tm || std::strftime(out, sizeof(out), "%F %T", &tm) == 0) {
|
||||
std::strcpy(out, "undef");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LogStreamBase::LogStreamBase(const char* file, int line, LogLevel lvl)
|
||||
: errno_(errno), file_(file), line_(line), lvl_(LogLevelStr(lvl)) {
|
||||
strm_ = std::make_unique<std::ostringstream>();
|
||||
}
|
||||
|
||||
void LogStreamBase::Flush() {
|
||||
{
|
||||
std::string msg = strm_->str();
|
||||
char tid[2 * sizeof(std::uintptr_t) + 1];
|
||||
FormatThreadId(tid);
|
||||
char time[64];
|
||||
FormatCurrentTime(time);
|
||||
|
||||
std::unique_lock<std::mutex> lock(g_log_mutex);
|
||||
std::fprintf(stderr, "[%s %s %s %s:%d] %s\n", time, tid, lvl_, file_, line_, msg.c_str());
|
||||
}
|
||||
strm_.reset();
|
||||
errno = errno_;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, Errno e) {
|
||||
// GNU C Library uses a buffer of 1024 characters for strerror(). Mimic to avoid truncations.
|
||||
char buf[1024];
|
||||
auto x = strerror_r(e.err, buf, sizeof(buf));
|
||||
// There are two versions of strerror_r with different semantics. We can figure out which
|
||||
// one we've got by looking at the result type.
|
||||
if (std::is_same<decltype(x), int>::value) {
|
||||
// XSI-compliant version.
|
||||
strm << (x ? "unknown error" : buf);
|
||||
} else if (std::is_same<decltype(x), char*>::value) {
|
||||
// GNU-specific version.
|
||||
strm << x;
|
||||
} else {
|
||||
// Something else entirely.
|
||||
strm << "unknown error";
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
} // namespace internal_logging
|
||||
|
||||
LogLevel g_min_log_level = INFO;
|
||||
|
||||
const char* LogLevelStr(LogLevel lvl) {
|
||||
switch (lvl) {
|
||||
case DEBUG:
|
||||
return "DEBUG";
|
||||
case INFO:
|
||||
return "INFO";
|
||||
case WARN:
|
||||
return "WARN";
|
||||
case ERROR:
|
||||
return "ERROR";
|
||||
case FATAL:
|
||||
return "FATAL";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
bool ParseLogLevel(const char* s, LogLevel& lvl) {
|
||||
if (!s)
|
||||
return false;
|
||||
else if (!std::strcmp(s, "DEBUG"))
|
||||
lvl = DEBUG;
|
||||
else if (!std::strcmp(s, "INFO"))
|
||||
lvl = INFO;
|
||||
else if (!std::strcmp(s, "WARN"))
|
||||
lvl = WARN;
|
||||
else if (!std::strcmp(s, "ERROR"))
|
||||
lvl = ERROR;
|
||||
else if (!std::strcmp(s, "FATAL"))
|
||||
lvl = FATAL;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,124 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_LOGGING_H_
|
||||
#define ROMKATV_GITSTATUS_LOGGING_H_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#define LOG(severity) LOG_I(severity)
|
||||
|
||||
#define LOG_I(severity) \
|
||||
(::gitstatus::severity < ::gitstatus::g_min_log_level) \
|
||||
? static_cast<void>(0) \
|
||||
: ::gitstatus::internal_logging::Assignable() = \
|
||||
::gitstatus::internal_logging::LogStream<::gitstatus::severity>(__FILE__, __LINE__, \
|
||||
::gitstatus::severity) \
|
||||
.ref()
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
enum LogLevel {
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
FATAL,
|
||||
};
|
||||
|
||||
const char* LogLevelStr(LogLevel lvl);
|
||||
bool ParseLogLevel(const char* s, LogLevel& lvl);
|
||||
|
||||
extern LogLevel g_min_log_level;
|
||||
|
||||
namespace internal_logging {
|
||||
|
||||
struct Assignable {
|
||||
template <class T>
|
||||
void operator=(const T&) const {}
|
||||
};
|
||||
|
||||
class LogStreamBase {
|
||||
public:
|
||||
LogStreamBase(const char* file, int line, LogLevel lvl);
|
||||
|
||||
LogStreamBase& ref() { return *this; }
|
||||
std::ostream& strm() { return *strm_; }
|
||||
int stashed_errno() const { return errno_; }
|
||||
|
||||
protected:
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
int errno_;
|
||||
const char* file_;
|
||||
int line_;
|
||||
const char* lvl_;
|
||||
std::unique_ptr<std::ostringstream> strm_;
|
||||
};
|
||||
|
||||
template <LogLevel>
|
||||
class LogStream : public LogStreamBase {
|
||||
public:
|
||||
using LogStreamBase::LogStreamBase;
|
||||
~LogStream() { this->Flush(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
class LogStream<FATAL> : public LogStreamBase {
|
||||
public:
|
||||
using LogStreamBase::LogStreamBase;
|
||||
~LogStream() __attribute__((noreturn)) {
|
||||
this->Flush();
|
||||
std::abort();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
LogStreamBase& operator<<(LogStreamBase& strm, const T& val) {
|
||||
strm.strm() << val;
|
||||
return strm;
|
||||
}
|
||||
|
||||
inline LogStreamBase& operator<<(LogStreamBase& strm, std::ostream& (*manip)(std::ostream&)) {
|
||||
strm.strm() << manip;
|
||||
return strm;
|
||||
}
|
||||
|
||||
struct Errno {
|
||||
int err;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, Errno e);
|
||||
|
||||
struct StashedErrno {};
|
||||
|
||||
inline LogStreamBase& operator<<(LogStreamBase& strm, StashedErrno) {
|
||||
return strm << Errno{strm.stashed_errno()};
|
||||
}
|
||||
|
||||
} // namespace internal_logging
|
||||
|
||||
inline internal_logging::Errno Errno(int err) { return {err}; }
|
||||
inline internal_logging::StashedErrno Errno() { return {}; }
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_LOGGING_H_
|
@ -0,0 +1,362 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "options.h"
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "print.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
long ParseLong(const char* s) {
|
||||
errno = 0;
|
||||
char* end = nullptr;
|
||||
long res = std::strtol(s, &end, 10);
|
||||
if (*end || end == s || errno) {
|
||||
std::cerr << "gitstatusd: not an integer: " << s << std::endl;
|
||||
std::exit(10);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
long ParseInt(const char* s) {
|
||||
long res = ParseLong(s);
|
||||
if (res < INT_MIN || res > INT_MAX) {
|
||||
std::cerr << "gitstatusd: integer out of bounds: " << s << std::endl;
|
||||
std::exit(10);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t ParseSizeT(const char* s) {
|
||||
static_assert(sizeof(long) <= sizeof(size_t), "");
|
||||
long res = ParseLong(s);
|
||||
return res >= 0 ? res : -1;
|
||||
}
|
||||
|
||||
void PrintUsage() {
|
||||
std::cout << "Usage: gitstatusd [OPTION]...\n"
|
||||
<< "Print machine-readable status of the git repos for directores in stdin.\n"
|
||||
<< "\n"
|
||||
<< "OPTIONS\n"
|
||||
<< " -l, --lock-fd=NUM [default=-1]\n"
|
||||
<< " If non-negative, check whether the specified file descriptor is locked when\n"
|
||||
<< " not receiving any requests for one second; exit if it isn't locked.\n"
|
||||
<< "\n"
|
||||
<< " -p, --parent-pid=NUM [default=-1]\n"
|
||||
<< " If non-negative, send signal 0 to the specified PID when not receiving any\n"
|
||||
<< " requests for one second; exit if signal sending fails.\n"
|
||||
<< "\n"
|
||||
<< " -t, --num-threads=NUM [default=1]\n"
|
||||
<< " Use this many threads to scan git workdir for unstaged and untracked files.\n"
|
||||
<< " Empirically, setting this parameter to twice the number of virtual CPU yields\n"
|
||||
<< " maximum performance.\n"
|
||||
<< "\n"
|
||||
<< " -v, --log-level=STR [default=INFO]\n"
|
||||
<< " Don't write entires to log whose log level is below this. Log levels in\n"
|
||||
<< " increasing order: DEBUG, INFO, WARN, ERROR, FATAL.\n"
|
||||
<< "\n"
|
||||
<< " -r, --repo-ttl-seconds=NUM [default=3600]\n"
|
||||
<< " Close git repositories that haven't been used for this long. This is meant to\n"
|
||||
<< " release resources such as memory and file descriptors. The next request for a\n"
|
||||
<< " repo that's been closed is much slower than for a repo that hasn't been.\n"
|
||||
<< " Negative value means infinity.\n"
|
||||
<< "\n"
|
||||
<< " -z, --max-commit-summary-length=NUM [default=256]\n"
|
||||
<< " Truncate commit summary if it's longer than this many bytes.\n"
|
||||
<< "\n"
|
||||
<< " -s, --max-num-staged=NUM [default=1]\n"
|
||||
<< " Report at most this many staged changes; negative value means infinity.\n"
|
||||
<< "\n"
|
||||
<< " -u, --max-num-unstaged=NUM [default=1]\n"
|
||||
<< " Report at most this many unstaged changes; negative value means infinity.\n"
|
||||
<< "\n"
|
||||
<< " -c, --max-num-conflicted=NUM [default=1]\n"
|
||||
<< " Report at most this many conflicted changes; negative value means infinity.\n"
|
||||
<< "\n"
|
||||
<< " -d, --max-num-untracked=NUM [default=1]\n"
|
||||
<< " Report at most this many untracked files; negative value means infinity.\n"
|
||||
<< "\n"
|
||||
<< " -m, --dirty-max-index-size=NUM [default=-1]\n"
|
||||
<< " If a repo has more files in its index than this, override --max-num-unstaged\n"
|
||||
<< " and --max-num-untracked (but not --max-num-staged) with zeros; negative value\n"
|
||||
<< " means infinity.\n"
|
||||
<< "\n"
|
||||
<< " -e, --recurse-untracked-dirs\n"
|
||||
<< " Count files within untracked directories like `git status --untracked-files`.\n"
|
||||
<< "\n"
|
||||
<< " -U, --ignore-status-show-untracked-files\n"
|
||||
<< " Unless this option is specified, report zero untracked files for repositories\n"
|
||||
<< " with status.showUntrackedFiles = false.\n"
|
||||
<< "\n"
|
||||
<< " -W, --ignore-bash-show-untracked-files\n"
|
||||
<< " Unless this option is specified, report zero untracked files for repositories\n"
|
||||
<< " with bash.showUntrackedFiles = false.\n"
|
||||
<< "\n"
|
||||
<< " -D, --ignore-bash-show-dirty-state\n"
|
||||
<< " Unless this option is specified, report zero staged, unstaged and conflicted\n"
|
||||
<< " changes for repositories with bash.showDirtyState = false.\n"
|
||||
<< "\n"
|
||||
<< " -V, --version\n"
|
||||
<< " Print gitstatusd version and exit.\n"
|
||||
<< "\n"
|
||||
<< " -G, --version-glob=STR [default=*]\n"
|
||||
<< " Immediately exit with code 11 if gitstatusd version (see --version) doesn't\n"
|
||||
<< " does not match the specified pattern. Matching is done with fnmatch(3)\n"
|
||||
<< " without flags.\n"
|
||||
<< "\n"
|
||||
<< " -h, --help\n"
|
||||
<< " Display this help and exit.\n"
|
||||
<< "\n"
|
||||
<< "INPUT\n"
|
||||
<< "\n"
|
||||
<< " Requests are read from stdin, separated by ascii 30 (record separator). Each\n"
|
||||
<< " request is made of the following fields, in the specified order, separated by\n"
|
||||
<< " ascii 31 (unit separator):\n"
|
||||
<< "\n"
|
||||
<< " 1. Request ID. Any string. Can be empty.\n"
|
||||
<< " 2. Path to the directory for which git stats are being requested.\n"
|
||||
<< " If the first character is ':', it is removed and the remaning path\n"
|
||||
<< " is treated as GIT_DIR.\n"
|
||||
<< " 3. (Optional) '1' to disable computation of anything that requires reading\n"
|
||||
<< " git index; '0' for the default behavior of computing everything.\n"
|
||||
<< "\n"
|
||||
<< "OUTPUT\n"
|
||||
<< "\n"
|
||||
<< " For every request read from stdin there is response written to stdout.\n"
|
||||
<< " Responses are separated by ascii 30 (record separator). Each response is made\n"
|
||||
<< " of the following fields, in the specified order, separated by ascii 31\n"
|
||||
<< " (unit separator):\n"
|
||||
<< "\n"
|
||||
<< " 1. Request id. The same as the first field in the request.\n"
|
||||
<< " 2. 0 if the directory isn't a git repo, 1 otherwise. If 0, all the\n"
|
||||
<< " following fields are missing.\n"
|
||||
<< " 3. Absolute path to the git repository workdir.\n"
|
||||
<< " 4. Commit hash that HEAD is pointing to. 40 hex digits.\n"
|
||||
<< " 5. Local branch name or empty if not on a branch.\n"
|
||||
<< " 6. Upstream branch name. Can be empty.\n"
|
||||
<< " 7. The remote name, e.g. \"upstream\" or \"origin\".\n"
|
||||
<< " 8. Remote URL. Can be empty.\n"
|
||||
<< " 9. Repository state, A.K.A. action. Can be empty.\n"
|
||||
<< " 10. The number of files in the index.\n"
|
||||
<< " 11. The number of staged changes.\n"
|
||||
<< " 12. The number of unstaged changes.\n"
|
||||
<< " 13. The number of conflicted changes.\n"
|
||||
<< " 14. The number of untracked files.\n"
|
||||
<< " 15. Number of commits the current branch is ahead of upstream.\n"
|
||||
<< " 16. Number of commits the current branch is behind upstream.\n"
|
||||
<< " 17. The number of stashes.\n"
|
||||
<< " 18. The last tag (in lexicographical order) that points to the same\n"
|
||||
<< " commit as HEAD.\n"
|
||||
<< " 19. The number of unstaged deleted files.\n"
|
||||
<< " 20. The number of staged new files.\n"
|
||||
<< " 21. The number of staged deleted files.\n"
|
||||
<< " 22. The push remote name, e.g. \"upstream\" or \"origin\".\n"
|
||||
<< " 23. Push remote URL. Can be empty.\n"
|
||||
<< " 24. Number of commits the current branch is ahead of push remote.\n"
|
||||
<< " 25. Number of commits the current branch is behind push remote.\n"
|
||||
<< " 26. Number of files in the index with skip-worktree bit set.\n"
|
||||
<< " 27. Number of files in the index with assume-unchanged bit set.\n"
|
||||
<< " 28. Encoding of the HEAD's commit message. Empty value means UTF-8.\n"
|
||||
<< " 29. The first paragraph of the HEAD's commit message as one line.\n"
|
||||
<< "\n"
|
||||
<< "Note: Renamed files are reported as deleted plus new.\n"
|
||||
<< "\n"
|
||||
<< "EXAMPLE\n"
|
||||
<< "\n"
|
||||
<< " Send a single request and print response (zsh syntax):\n"
|
||||
<< "\n"
|
||||
<< " local req_id=id\n"
|
||||
<< " local dir=$PWD\n"
|
||||
<< " echo -nE $req_id$'\\x1f'$dir$'\\x1e' | ./gitstatusd | {\n"
|
||||
<< " local resp\n"
|
||||
<< " IFS=$'\\x1f' read -rd $'\\x1e' -A resp && print -lr -- \"${(@qq)resp}\"\n"
|
||||
<< " }\n"
|
||||
<< "\n"
|
||||
<< " Output:"
|
||||
<< "\n"
|
||||
<< " 'id'\n"
|
||||
<< " '1'\n"
|
||||
<< " '/home/romka/gitstatus'\n"
|
||||
<< " 'bf46bf03dbab7108801b53f8a720caee8464c9c3'\n"
|
||||
<< " 'master'\n"
|
||||
<< " 'master'\n"
|
||||
<< " 'origin'\n"
|
||||
<< " 'git@github.com:romkatv/gitstatus.git'\n"
|
||||
<< " ''\n"
|
||||
<< " '70'\n"
|
||||
<< " '1'\n"
|
||||
<< " '0'\n"
|
||||
<< " '0'\n"
|
||||
<< " '2'\n"
|
||||
<< " '0'\n"
|
||||
<< " '0'\n"
|
||||
<< " ''\n"
|
||||
<< " '0'\n"
|
||||
<< " '0'\n"
|
||||
<< " '0'\n"
|
||||
<< " ''\n"
|
||||
<< " ''\n"
|
||||
<< " '0'\n"
|
||||
<< " '0'\n"
|
||||
<< " '0'\n"
|
||||
<< " '0'\n"
|
||||
<< " ''\n"
|
||||
<< " 'add a build server for darwin-arm64'\n"
|
||||
<< "\n"
|
||||
<< "EXIT STATUS\n"
|
||||
<< "\n"
|
||||
<< " The command returns zero on success (when printing help or on EOF),\n"
|
||||
<< " non-zero on failure. In the latter case the output is unspecified.\n"
|
||||
<< "\n"
|
||||
<< "COPYRIGHT\n"
|
||||
<< "\n"
|
||||
<< " Copyright 2019 Roman Perepelitsa\n"
|
||||
<< " This is free software; see https://github.com/romkatv/gitstatus for copying\n"
|
||||
<< " conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR\n"
|
||||
<< " A PARTICULAR PURPOSE." << std::endl;
|
||||
}
|
||||
|
||||
const char* Version() {
|
||||
#define _INTERNAL_GITSTATUS_STRINGIZE(x) _INTERNAL_GITSTATUS_STRINGIZE_I(x)
|
||||
#define _INTERNAL_GITSTATUS_STRINGIZE_I(x) #x
|
||||
return _INTERNAL_GITSTATUS_STRINGIZE(GITSTATUS_VERSION);
|
||||
#undef _INTERNAL_GITSTATUS_STRINGIZE_I
|
||||
#undef _INTERNAL_GITSTATUS_STRINGIZE
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Options ParseOptions(int argc, char** argv) {
|
||||
const struct option opts[] = {{"help", no_argument, nullptr, 'h'},
|
||||
{"version", no_argument, nullptr, 'V'},
|
||||
{"version-glob", required_argument, nullptr, 'G'},
|
||||
{"lock-fd", required_argument, nullptr, 'l'},
|
||||
{"parent-pid", required_argument, nullptr, 'p'},
|
||||
{"num-threads", required_argument, nullptr, 't'},
|
||||
{"log-level", required_argument, nullptr, 'v'},
|
||||
{"repo-ttl-seconds", required_argument, nullptr, 'r'},
|
||||
{"max-commit-summary-length", required_argument, nullptr, 'z'},
|
||||
{"max-num-staged", required_argument, nullptr, 's'},
|
||||
{"max-num-unstaged", required_argument, nullptr, 'u'},
|
||||
{"max-num-conflicted", required_argument, nullptr, 'c'},
|
||||
{"max-num-untracked", required_argument, nullptr, 'd'},
|
||||
{"dirty-max-index-size", required_argument, nullptr, 'm'},
|
||||
{"recurse-untracked-dirs", no_argument, nullptr, 'e'},
|
||||
{"ignore-status-show-untracked-files", no_argument, nullptr, 'U'},
|
||||
{"ignore-bash-show-untracked-files", no_argument, nullptr, 'W'},
|
||||
{"ignore-bash-show-dirty-state", no_argument, nullptr, 'D'},
|
||||
{}};
|
||||
Options res;
|
||||
while (true) {
|
||||
switch (getopt_long(argc, argv, "hVG:l:p:t:v:r:z:s:u:c:d:m:eUWD", opts, nullptr)) {
|
||||
case -1:
|
||||
if (optind != argc) {
|
||||
std::cerr << "unexpected positional argument: " << argv[optind] << std::endl;
|
||||
std::exit(10);
|
||||
}
|
||||
return res;
|
||||
case 'h':
|
||||
PrintUsage();
|
||||
std::exit(0);
|
||||
case 'V':
|
||||
std::cout << Version() << std::endl;
|
||||
std::exit(0);
|
||||
case 'G':
|
||||
if (int err = fnmatch(optarg, Version(), 0)) {
|
||||
if (err != FNM_NOMATCH) {
|
||||
std::cerr << "Cannot match " << Print(Version()) << " against pattern "
|
||||
<< Print(optarg) << ": error " << err;
|
||||
std::exit(10);
|
||||
}
|
||||
std::cerr << "Version mismatch. Wanted (pattern): " << Print(optarg)
|
||||
<< ". Actual: " << Print(Version()) << "." << std::endl;
|
||||
std::exit(11);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
res.lock_fd = ParseInt(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
res.parent_pid = ParseInt(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
if (!ParseLogLevel(optarg, res.log_level)) {
|
||||
std::cerr << "invalid log level: " << optarg << std::endl;
|
||||
std::exit(10);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
res.repo_ttl = std::chrono::seconds(ParseLong(optarg));
|
||||
break;
|
||||
case 't': {
|
||||
long n = ParseLong(optarg);
|
||||
if (n <= 0) {
|
||||
std::cerr << "invalid number of threads: " << n << std::endl;
|
||||
std::exit(10);
|
||||
}
|
||||
res.num_threads = n;
|
||||
break;
|
||||
}
|
||||
case 'z':
|
||||
res.max_commit_summary_length = ParseSizeT(optarg);
|
||||
break;
|
||||
case 's':
|
||||
res.max_num_staged = ParseSizeT(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
res.max_num_unstaged = ParseSizeT(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
res.max_num_conflicted = ParseSizeT(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
res.max_num_untracked = ParseSizeT(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
res.dirty_max_index_size = ParseSizeT(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
res.recurse_untracked_dirs = true;
|
||||
break;
|
||||
case 'U':
|
||||
res.ignore_status_show_untracked_files = true;
|
||||
break;
|
||||
case 'W':
|
||||
res.ignore_bash_show_untracked_files = true;
|
||||
break;
|
||||
case 'D':
|
||||
res.ignore_bash_show_dirty_state = true;
|
||||
break;
|
||||
default:
|
||||
std::exit(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,78 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_OPTIONS_H_
|
||||
#define ROMKATV_GITSTATUS_OPTIONS_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include "logging.h"
|
||||
#include "time.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
struct Limits {
|
||||
// Truncate commit summary if it's longer than this many bytes.
|
||||
size_t max_commit_summary_length = 256;
|
||||
// Report at most this many staged changes.
|
||||
size_t max_num_staged = 1;
|
||||
// Report at most this many unstaged changes.
|
||||
size_t max_num_unstaged = 1;
|
||||
// Report at most this many conflicted changes.
|
||||
size_t max_num_conflicted = 1;
|
||||
// Report at most this many untracked files.
|
||||
size_t max_num_untracked = 1;
|
||||
// If a repo has more files in its index than this, override max_num_unstaged and
|
||||
// max_num_untracked (but not max_num_staged) with zeros.
|
||||
size_t dirty_max_index_size = -1;
|
||||
// If true, report untracked files like `git status --untracked-files`.
|
||||
bool recurse_untracked_dirs = false;
|
||||
// Unless true, report zero untracked files for repositories with
|
||||
// status.showUntrackedFiles = false.
|
||||
bool ignore_status_show_untracked_files = false;
|
||||
// Unless true, report zero untracked files for repositories with
|
||||
// bash.showUntrackedFiles = false.
|
||||
bool ignore_bash_show_untracked_files = false;
|
||||
// Unless true, report zero staged, unstaged and conflicted changes for repositories with
|
||||
// bash.showDirtyState = false.
|
||||
bool ignore_bash_show_dirty_state = false;
|
||||
};
|
||||
|
||||
struct Options : Limits {
|
||||
// Use this many threads to scan git workdir for unstaged and untracked files. Must be positive.
|
||||
size_t num_threads = 1;
|
||||
// If non-negative, check whether the specified file descriptor is locked when not receiving any
|
||||
// requests for one second; exit if it isn't locked.
|
||||
int lock_fd = -1;
|
||||
// If non-negative, send signal 0 to the specified PID when not receiving any requests for one
|
||||
// second; exit if signal sending fails.
|
||||
int parent_pid = -1;
|
||||
// Don't write entires to log whose log level is below this. Log levels in increasing order:
|
||||
// DEBUG, INFO, WARN, ERROR, FATAL.
|
||||
LogLevel log_level = INFO;
|
||||
// Close git repositories that haven't been used for this long. This is meant to release resources
|
||||
// such as memory and file descriptors. The next request for a repo that's been closed is much
|
||||
// slower than for a repo that hasn't been. Negative value means infinity.
|
||||
Duration repo_ttl = std::chrono::seconds(3600);
|
||||
};
|
||||
|
||||
Options ParseOptions(int argc, char** argv);
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_OPTIONS_H_
|
101
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/print.h
Normal file
101
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/print.h
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_PRINT_H_
|
||||
#define ROMKATV_GITSTATUS_PRINT_H_
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include "string_view.h"
|
||||
#include "strings.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
template <class T>
|
||||
struct Printable {
|
||||
const T& value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
Printable<T> Print(const T& val) {
|
||||
return {val};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::ostream& operator<<(std::ostream& strm, const Printable<T>& p) {
|
||||
static_assert(!std::is_pointer<std::decay_t<T>>(), "");
|
||||
return strm << p.value;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& strm, const Printable<StringView>& p) {
|
||||
Quote(strm, p.value.ptr, p.value.ptr + p.value.len);
|
||||
return strm;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& strm, const Printable<std::string>& p) {
|
||||
Quote(strm, p.value.data(), p.value.data() + p.value.size());
|
||||
return strm;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& strm, const Printable<const char*>& p) {
|
||||
Quote(strm, p.value, p.value ? p.value + std::strlen(p.value) : nullptr);
|
||||
return strm;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& strm, const Printable<char*>& p) {
|
||||
Quote(strm, p.value, p.value ? p.value + std::strlen(p.value) : nullptr);
|
||||
return strm;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
std::ostream& operator<<(std::ostream& strm, const Printable<std::pair<T, U>>& p) {
|
||||
return strm << '{' << Print(p.value.first) << ", " << Print(p.value.second) << '}';
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::ostream& operator<<(std::ostream& strm, const Printable<std::vector<T>>& p) {
|
||||
strm << '[';
|
||||
for (size_t i = 0; i != p.value.size(); ++i) {
|
||||
if (i) strm << ", ";
|
||||
strm << Print(p.value[i]);
|
||||
}
|
||||
strm << ']';
|
||||
return strm;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& strm, const Printable<struct timespec>& p) {
|
||||
strm << p.value.tv_sec << '.' << std::setw(9) << std::setfill('0') << p.value.tv_nsec;
|
||||
return strm;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& strm, const Printable<git_index_time>& p) {
|
||||
strm << p.value.seconds << '.' << std::setw(9) << std::setfill('0') << p.value.nanoseconds;
|
||||
return strm;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_PRINT_H_
|
503
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/repo.cc
Normal file
503
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/repo.cc
Normal file
@ -0,0 +1,503 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "repo.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "arena.h"
|
||||
#include "check.h"
|
||||
#include "check_dir_mtime.h"
|
||||
#include "dir.h"
|
||||
#include "git.h"
|
||||
#include "print.h"
|
||||
#include "scope_guard.h"
|
||||
#include "stat.h"
|
||||
#include "string_cmp.h"
|
||||
#include "thread_pool.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
template <class T>
|
||||
T Load(const std::atomic<T>& x) {
|
||||
return x.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Store(std::atomic<T>& x, T v) {
|
||||
x.store(v, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Inc(std::atomic<T>& x, T by = 1) {
|
||||
return x.fetch_add(by, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Dec(std::atomic<T>& x) {
|
||||
return x.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Exchange(std::atomic<T>& x, T v) {
|
||||
return x.exchange(v, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
const char* DeltaStr(git_delta_t t) {
|
||||
switch (t) {
|
||||
case GIT_DELTA_UNMODIFIED: return "unmodified";
|
||||
case GIT_DELTA_ADDED: return "added";
|
||||
case GIT_DELTA_DELETED: return "deleted";
|
||||
case GIT_DELTA_MODIFIED: return "modified";
|
||||
case GIT_DELTA_RENAMED: return "renamed";
|
||||
case GIT_DELTA_COPIED: return "copied";
|
||||
case GIT_DELTA_IGNORED: return "ignored";
|
||||
case GIT_DELTA_UNTRACKED: return "untracked";
|
||||
case GIT_DELTA_TYPECHANGE: return "typechange";
|
||||
case GIT_DELTA_UNREADABLE: return "unreadable";
|
||||
case GIT_DELTA_CONFLICTED: return "conflicted";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Repo::Shard::Contains(Str<> str, StringView path) const {
|
||||
if (str.Lt(path, start_s)) return false;
|
||||
if (end_s.empty()) return true;
|
||||
path.len = std::min(path.len, end_s.size());
|
||||
return !str.Lt(end_s, path);
|
||||
}
|
||||
|
||||
Repo::Repo(git_repository* repo, Limits lim) : lim_(std::move(lim)), repo_(repo), tag_db_(repo) {
|
||||
if (lim_.max_num_untracked) {
|
||||
GlobalThreadPool()->Schedule([this] {
|
||||
bool check = CheckDirMtime(git_repository_path(repo_));
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
CHECK(Load(untracked_cache_) == Tribool::kUnknown);
|
||||
Store(untracked_cache_, check ? Tribool::kTrue : Tribool::kFalse);
|
||||
cv_.notify_one();
|
||||
});
|
||||
} else {
|
||||
untracked_cache_ = Tribool::kFalse;
|
||||
}
|
||||
}
|
||||
|
||||
Repo::~Repo() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
while (untracked_cache_ == Tribool::kUnknown) cv_.wait(lock);
|
||||
}
|
||||
if (git_index_) git_index_free(git_index_);
|
||||
git_repository_free(repo_);
|
||||
}
|
||||
|
||||
IndexStats Repo::GetIndexStats(const git_oid* head, git_config* cfg) {
|
||||
ON_SCOPE_EXIT(this, orig_lim = lim_) { lim_ = orig_lim; };
|
||||
auto Off = [&](const char* name) {
|
||||
int val;
|
||||
if (git_config_get_bool(&val, cfg, name) || val) return false;
|
||||
LOG(INFO) << "Honoring git config option: " << name << " = false";
|
||||
return true;
|
||||
};
|
||||
if (!lim_.ignore_status_show_untracked_files && Off("status.showUntrackedFiles")) {
|
||||
lim_.max_num_untracked = 0;
|
||||
}
|
||||
if (!lim_.ignore_bash_show_untracked_files && Off("bash.showUntrackedFiles")) {
|
||||
lim_.max_num_untracked = 0;
|
||||
}
|
||||
if (!lim_.ignore_bash_show_dirty_state && Off("bash.showDirtyState")) {
|
||||
lim_.max_num_staged = 0;
|
||||
lim_.max_num_unstaged = 0;
|
||||
lim_.max_num_conflicted = 0;
|
||||
}
|
||||
|
||||
if (git_index_) {
|
||||
int new_index;
|
||||
VERIFY(!git_index_read_ex(git_index_, 0, &new_index)) << GitError();
|
||||
if (new_index) {
|
||||
head_ = {};
|
||||
index_.reset();
|
||||
}
|
||||
} else {
|
||||
VERIFY(!git_repository_index(&git_index_, repo_)) << GitError();
|
||||
// Query an attribute (doesn't matter which) to initialize repo's attribute
|
||||
// cache. It's a workaround for synchronization bugs (data races) in libgit2
|
||||
// that result from lazy cache initialization without synchrnonization.
|
||||
// Thankfully, subsequent cache reads and writes are properly synchronized.
|
||||
const char* attr;
|
||||
VERIFY(!git_attr_get(&attr, repo_, 0, "x", "x")) << GitError();
|
||||
}
|
||||
|
||||
UpdateShards();
|
||||
Store(error_, false);
|
||||
Store(unstaged_, {});
|
||||
Store(untracked_, {});
|
||||
Store(unstaged_deleted_, {});
|
||||
|
||||
std::vector<const char*> dirty_candidates;
|
||||
const size_t index_size = git_index_entrycount(git_index_);
|
||||
|
||||
if (!lim_.max_num_staged && !lim_.max_num_conflicted) {
|
||||
head_ = {};
|
||||
Store(staged_, {});
|
||||
Store(conflicted_, {});
|
||||
Store(staged_new_, {});
|
||||
Store(staged_deleted_, {});
|
||||
Store(skip_worktree_, {});
|
||||
Store(assume_unchanged_, {});
|
||||
} else if (head) {
|
||||
if (git_oid_equal(head, &head_)) {
|
||||
LOG(INFO) << "Index and HEAD unchanged; staged = " << Load(staged_)
|
||||
<< ", conflicted = " << Load(conflicted_);
|
||||
} else {
|
||||
head_ = *head;
|
||||
Store(staged_, {});
|
||||
Store(conflicted_, {});
|
||||
Store(staged_new_, {});
|
||||
Store(staged_deleted_, {});
|
||||
Store(skip_worktree_, {});
|
||||
Store(assume_unchanged_, {});
|
||||
StartStagedScan(head);
|
||||
}
|
||||
} else {
|
||||
head_ = {};
|
||||
size_t staged = 0;
|
||||
size_t skip_worktree = 0;
|
||||
size_t assume_unchanged = 0;
|
||||
for (size_t i = 0; i != index_size; ++i) {
|
||||
const git_index_entry* entry = git_index_get_byindex_no_sort(git_index_, i);
|
||||
if (!(entry->flags_extended & GIT_INDEX_ENTRY_INTENT_TO_ADD)) ++staged;
|
||||
if (entry->flags_extended & GIT_INDEX_ENTRY_SKIP_WORKTREE) ++skip_worktree;
|
||||
if (entry->flags & GIT_INDEX_ENTRY_VALID) ++assume_unchanged;
|
||||
}
|
||||
Store(staged_, staged);
|
||||
Store(conflicted_, {});
|
||||
Store(staged_new_, staged);
|
||||
Store(staged_deleted_, {});
|
||||
Store(skip_worktree_, skip_worktree);
|
||||
Store(assume_unchanged_, assume_unchanged);
|
||||
}
|
||||
|
||||
if (index_size <= lim_.dirty_max_index_size &&
|
||||
(lim_.max_num_unstaged || lim_.max_num_untracked)) {
|
||||
if (!index_) index_ = std::make_unique<Index>(repo_, git_index_);
|
||||
dirty_candidates = index_->GetDirtyCandidates({.include_untracked = lim_.max_num_untracked > 0,
|
||||
.untracked_cache = Load(untracked_cache_)});
|
||||
if (dirty_candidates.empty()) {
|
||||
LOG(INFO) << "Clean repo: no dirty candidates";
|
||||
} else {
|
||||
LOG(INFO) << "Found " << dirty_candidates.size() << " dirty candidate(s) spanning from "
|
||||
<< Print(dirty_candidates.front()) << " to " << Print(dirty_candidates.back());
|
||||
}
|
||||
StartDirtyScan(dirty_candidates);
|
||||
}
|
||||
|
||||
Wait();
|
||||
VERIFY(!Load(error_));
|
||||
|
||||
size_t num_staged = std::min(Load(staged_), lim_.max_num_staged);
|
||||
size_t num_unstaged = std::min(Load(unstaged_), lim_.max_num_unstaged);
|
||||
return {.index_size = index_size,
|
||||
.num_staged = num_staged,
|
||||
.num_unstaged = num_unstaged,
|
||||
.num_conflicted = std::min(Load(conflicted_), lim_.max_num_conflicted),
|
||||
.num_untracked = std::min(Load(untracked_), lim_.max_num_untracked),
|
||||
.num_staged_new = std::min(Load(staged_new_), num_staged),
|
||||
.num_staged_deleted = std::min(Load(staged_deleted_), num_staged),
|
||||
.num_unstaged_deleted = std::min(Load(unstaged_deleted_), num_unstaged),
|
||||
.num_skip_worktree = Load(skip_worktree_),
|
||||
.num_assume_unchanged = Load(assume_unchanged_)};
|
||||
}
|
||||
|
||||
int Repo::OnDelta(const char* type, const git_diff_delta& d, std::atomic<size_t>& c1, size_t m1,
|
||||
const std::atomic<size_t>& c2, size_t m2) {
|
||||
auto Msg = [&]() {
|
||||
const char* status = DeltaStr(d.status);
|
||||
std::ostringstream strm;
|
||||
strm << "Found " << type << " file";
|
||||
if (strcmp(status, type)) strm << " (" << status << ")";
|
||||
strm << ": " << Print(d.new_file.path);
|
||||
return strm.str();
|
||||
};
|
||||
|
||||
size_t v = Inc(c1);
|
||||
if (v) {
|
||||
LOG(DEBUG) << Msg();
|
||||
} else {
|
||||
LOG(INFO) << Msg();
|
||||
}
|
||||
if (v + 1 < m1) return GIT_DIFF_DELTA_DO_NOT_INSERT;
|
||||
if (Load(c2) < m2) return GIT_DIFF_DELTA_DO_NOT_INSERT | GIT_DIFF_DELTA_SKIP_TYPE;
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
void Repo::StartDirtyScan(const std::vector<const char*>& paths) {
|
||||
if (paths.empty()) return;
|
||||
|
||||
git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
|
||||
opt.payload = this;
|
||||
opt.flags = GIT_DIFF_INCLUDE_TYPECHANGE_TREES | GIT_DIFF_SKIP_BINARY_CHECK |
|
||||
GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_EXEMPLARS;
|
||||
if (lim_.max_num_untracked) {
|
||||
opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
if (lim_.recurse_untracked_dirs) opt.flags |= GIT_DIFF_RECURSE_UNTRACKED_DIRS;
|
||||
} else {
|
||||
opt.flags |= GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
|
||||
}
|
||||
opt.ignore_submodules = GIT_SUBMODULE_IGNORE_DIRTY;
|
||||
opt.notify_cb = +[](const git_diff* diff, const git_diff_delta* delta,
|
||||
const char* matched_pathspec, void* payload) -> int {
|
||||
if (delta->status == GIT_DELTA_CONFLICTED) return GIT_DIFF_DELTA_DO_NOT_INSERT;
|
||||
Repo* repo = static_cast<Repo*>(payload);
|
||||
if (Load(repo->error_)) return GIT_EUSER;
|
||||
if (delta->status == GIT_DELTA_UNTRACKED) {
|
||||
return repo->OnDelta("untracked", *delta, repo->untracked_, repo->lim_.max_num_untracked,
|
||||
repo->unstaged_, repo->lim_.max_num_unstaged);
|
||||
} else {
|
||||
if (delta->status == GIT_DELTA_DELETED) Inc(repo->unstaged_deleted_);
|
||||
return repo->OnDelta("unstaged", *delta, repo->unstaged_, repo->lim_.max_num_unstaged,
|
||||
repo->untracked_, repo->lim_.max_num_untracked);
|
||||
}
|
||||
};
|
||||
|
||||
const Str<> str(git_index_is_case_sensitive(git_index_));
|
||||
auto shard = shards_.begin();
|
||||
for (auto p = paths.begin(); p != paths.end();) {
|
||||
opt.range_start = *p;
|
||||
opt.range_end = *p;
|
||||
opt.pathspec.strings = const_cast<char**>(&*p);
|
||||
opt.pathspec.count = 1;
|
||||
while (!shard->Contains(str, StringView(*p))) ++shard;
|
||||
while (++p != paths.end() && shard->Contains(str, StringView(*p))) {
|
||||
opt.range_end = *p;
|
||||
++opt.pathspec.count;
|
||||
}
|
||||
RunAsync([this, opt]() {
|
||||
git_diff* diff = nullptr;
|
||||
LOG(DEBUG) << "git_diff_index_to_workdir from " << Print(opt.range_start) << " to "
|
||||
<< Print(opt.range_end);
|
||||
switch (git_diff_index_to_workdir(&diff, repo_, git_index_, &opt)) {
|
||||
case 0:
|
||||
git_diff_free(diff);
|
||||
break;
|
||||
case GIT_EUSER:
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "git_diff_index_to_workdir: " << GitError();
|
||||
throw Exception();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Repo::StartStagedScan(const git_oid* head) {
|
||||
git_commit* commit = nullptr;
|
||||
VERIFY(!git_commit_lookup(&commit, repo_, head)) << GitError();
|
||||
ON_SCOPE_EXIT(=) { git_commit_free(commit); };
|
||||
git_tree* tree = nullptr;
|
||||
VERIFY(!git_commit_tree(&tree, commit)) << GitError();
|
||||
|
||||
git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
|
||||
opt.flags = GIT_DIFF_EXEMPLARS | GIT_DIFF_INCLUDE_TYPECHANGE_TREES;
|
||||
opt.payload = this;
|
||||
opt.notify_cb = +[](const git_diff* diff, const git_diff_delta* delta,
|
||||
const char* matched_pathspec, void* payload) -> int {
|
||||
Repo* repo = static_cast<Repo*>(payload);
|
||||
if (Load(repo->error_)) return GIT_EUSER;
|
||||
if (delta->status == GIT_DELTA_CONFLICTED) {
|
||||
return repo->OnDelta("conflicted", *delta, repo->conflicted_, repo->lim_.max_num_conflicted,
|
||||
repo->staged_, repo->lim_.max_num_staged);
|
||||
} else {
|
||||
if (delta->status == GIT_DELTA_ADDED) Inc(repo->staged_new_);
|
||||
if (delta->status == GIT_DELTA_DELETED) Inc(repo->staged_deleted_);
|
||||
return repo->OnDelta("staged", *delta, repo->staged_, repo->lim_.max_num_staged,
|
||||
repo->conflicted_, repo->lim_.max_num_conflicted);
|
||||
}
|
||||
};
|
||||
|
||||
for (const Shard& shard : shards_) {
|
||||
RunAsync([this, tree, opt, shard]() mutable {
|
||||
size_t skip_worktree = 0;
|
||||
size_t assume_unchanged = 0;
|
||||
for (size_t i = shard.start_i; i != shard.end_i; ++i) {
|
||||
const git_index_entry* entry = git_index_get_byindex_no_sort(git_index_, i);
|
||||
if (entry->flags_extended & GIT_INDEX_ENTRY_SKIP_WORKTREE) ++skip_worktree;
|
||||
if (entry->flags & GIT_INDEX_ENTRY_VALID) ++assume_unchanged;
|
||||
}
|
||||
Inc(skip_worktree_, skip_worktree);
|
||||
Inc(assume_unchanged_, assume_unchanged);
|
||||
opt.range_start = shard.start_s.c_str();
|
||||
opt.range_end = shard.end_s.c_str();
|
||||
git_diff* diff = nullptr;
|
||||
LOG(DEBUG) << "git_diff_tree_to_index from " << Print(opt.range_start) << " to "
|
||||
<< Print(opt.range_end);
|
||||
switch (git_diff_tree_to_index(&diff, repo_, tree, git_index_, &opt)) {
|
||||
case 0:
|
||||
git_diff_free(diff);
|
||||
break;
|
||||
case GIT_EUSER:
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "git_diff_tree_to_index: " << GitError();
|
||||
throw Exception();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Repo::UpdateShards() {
|
||||
constexpr size_t kEntriesPerShard = 512;
|
||||
|
||||
const Str<> str(git_index_is_case_sensitive(git_index_));
|
||||
size_t index_size = git_index_entrycount(git_index_);
|
||||
ON_SCOPE_EXIT(&) {
|
||||
LOG(INFO) << "Splitting " << index_size << " object(s) into " << shards_.size() << " shard(s)";
|
||||
};
|
||||
|
||||
if (index_size <= kEntriesPerShard || GlobalThreadPool()->num_threads() < 2) {
|
||||
shards_ = {{
|
||||
.start_s = "",
|
||||
.end_s = "",
|
||||
.start_i = 0,
|
||||
.end_i = index_size}};
|
||||
return;
|
||||
}
|
||||
|
||||
size_t shards =
|
||||
std::min(index_size / kEntriesPerShard + 1, 2 * GlobalThreadPool()->num_threads());
|
||||
shards_.clear();
|
||||
shards_.reserve(shards);
|
||||
std::string last_s;
|
||||
size_t last_i = 0;
|
||||
|
||||
for (size_t i = 0; i != shards - 1; ++i) {
|
||||
size_t idx = (i + 1) * index_size / shards;
|
||||
std::string split = git_index_get_byindex_no_sort(git_index_, idx)->path;
|
||||
auto pos = split.find_last_of('/');
|
||||
if (pos == std::string::npos) continue;
|
||||
split = split.substr(0, pos + 1);
|
||||
Shard shard;
|
||||
shard.end_s = split;
|
||||
--shard.end_s.back();
|
||||
if (!str.Lt(last_s, shard.end_s)) continue;
|
||||
shard.start_s = std::move(last_s);
|
||||
last_s = std::move(split);
|
||||
shard.start_i = last_i;
|
||||
shard.end_i = idx;
|
||||
last_i = idx;
|
||||
shards_.push_back(std::move(shard));
|
||||
}
|
||||
shards_.push_back({
|
||||
.start_s = std::move(last_s),
|
||||
.end_s = "",
|
||||
.start_i = last_i,
|
||||
.end_i = index_size});
|
||||
|
||||
CHECK(!shards_.empty());
|
||||
CHECK(shards_.size() <= shards);
|
||||
CHECK(shards_.front().start_s.empty());
|
||||
CHECK(shards_.front().start_i == 0);
|
||||
CHECK(shards_.back().end_s.empty());
|
||||
CHECK(shards_.back().end_i == index_size);
|
||||
for (size_t i = 0; i != shards_.size(); ++i) {
|
||||
if (i) {
|
||||
const git_index_entry* entry = git_index_get_byindex_no_sort(git_index_, shards_[i].start_i);
|
||||
CHECK(!std::memcmp(shards_[i].start_s.c_str(), entry->path, shards_[i].start_s.size()));
|
||||
CHECK(str.Lt(shards_[i - 1].end_s, shards_[i].start_s));
|
||||
CHECK(shards_[i - 1].end_i == shards_[i].start_i);
|
||||
}
|
||||
if (i != shards_.size() - 1) {
|
||||
CHECK(shards_[i].start_i < shards_[i].end_i);
|
||||
CHECK(str.Lt(shards_[i].start_s, shards_[i].end_s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Repo::DecInflight() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
CHECK(Load(inflight_) > 0);
|
||||
if (Dec(inflight_) == 1) cv_.notify_one();
|
||||
}
|
||||
|
||||
void Repo::RunAsync(std::function<void()> f) {
|
||||
Inc(inflight_);
|
||||
try {
|
||||
GlobalThreadPool()->Schedule([this, f = std::move(f)] {
|
||||
try {
|
||||
ON_SCOPE_EXIT(&) { DecInflight(); };
|
||||
f();
|
||||
} catch (const Exception&) {
|
||||
if (!Load(error_)) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (!Load(error_)) {
|
||||
Store(error_, true);
|
||||
cv_.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (...) {
|
||||
DecInflight();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void Repo::Wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
while (inflight_) cv_.wait(lock);
|
||||
}
|
||||
|
||||
std::future<std::string> Repo::GetTagName(const git_oid* target) {
|
||||
auto* promise = new std::promise<std::string>;
|
||||
std::future<std::string> res = promise->get_future();
|
||||
|
||||
GlobalThreadPool()->Schedule([=] {
|
||||
ON_SCOPE_EXIT(&) { delete promise; };
|
||||
if (!target) {
|
||||
promise->set_value("");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
promise->set_value(tag_db_.TagForCommit(*target));
|
||||
} catch (const Exception&) {
|
||||
promise->set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
126
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/repo.h
Normal file
126
zsh/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/src/repo.h
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_REPO_H_
|
||||
#define ROMKATV_GITSTATUS_REPO_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "check.h"
|
||||
#include "index.h"
|
||||
#include "options.h"
|
||||
#include "string_cmp.h"
|
||||
#include "tag_db.h"
|
||||
#include "time.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
struct IndexStats {
|
||||
size_t index_size = 0;
|
||||
size_t num_staged = 0;
|
||||
size_t num_unstaged = 0;
|
||||
size_t num_conflicted = 0;
|
||||
size_t num_untracked = 0;
|
||||
size_t num_staged_new = 0;
|
||||
size_t num_staged_deleted = 0;
|
||||
size_t num_unstaged_deleted = 0;
|
||||
size_t num_skip_worktree = 0;
|
||||
size_t num_assume_unchanged = 0;
|
||||
};
|
||||
|
||||
class Repo {
|
||||
public:
|
||||
explicit Repo(git_repository* repo, Limits lim);
|
||||
Repo(Repo&& other) = delete;
|
||||
~Repo();
|
||||
|
||||
git_repository* repo() const { return repo_; }
|
||||
|
||||
// Head can be null, in which case has_staged will be false.
|
||||
IndexStats GetIndexStats(const git_oid* head, git_config* cfg);
|
||||
|
||||
// Returns the last tag in lexicographical order whose target is equal to the given, or an
|
||||
// empty string. Target can be null, in which case the tag is empty.
|
||||
std::future<std::string> GetTagName(const git_oid* target);
|
||||
|
||||
private:
|
||||
struct Shard {
|
||||
bool Contains(Str<> str, StringView path) const;
|
||||
std::string start_s;
|
||||
std::string end_s;
|
||||
size_t start_i;
|
||||
size_t end_i;
|
||||
};
|
||||
|
||||
void UpdateShards();
|
||||
|
||||
int OnDelta(const char* type, const git_diff_delta& d, std::atomic<size_t>& c1, size_t m1,
|
||||
const std::atomic<size_t>& c2, size_t m2);
|
||||
|
||||
void StartStagedScan(const git_oid* head);
|
||||
void StartDirtyScan(const std::vector<const char*>& paths);
|
||||
|
||||
void DecInflight();
|
||||
void RunAsync(std::function<void()> f);
|
||||
void Wait();
|
||||
|
||||
Limits lim_;
|
||||
git_repository* const repo_;
|
||||
git_index* git_index_ = nullptr;
|
||||
std::vector<Shard> shards_;
|
||||
git_oid head_ = {};
|
||||
TagDb tag_db_;
|
||||
|
||||
std::unique_ptr<Index> index_;
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
std::atomic<size_t> inflight_{0};
|
||||
std::atomic<bool> error_{false};
|
||||
std::atomic<size_t> staged_{0};
|
||||
std::atomic<size_t> unstaged_{0};
|
||||
std::atomic<size_t> conflicted_{0};
|
||||
std::atomic<size_t> untracked_{0};
|
||||
std::atomic<size_t> staged_new_{0};
|
||||
std::atomic<size_t> staged_deleted_{0};
|
||||
std::atomic<size_t> unstaged_deleted_{0};
|
||||
std::atomic<size_t> skip_worktree_{0};
|
||||
std::atomic<size_t> assume_unchanged_{0};
|
||||
std::atomic<Tribool> untracked_cache_{Tribool::kUnknown};
|
||||
};
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_REPO_H_
|
@ -0,0 +1,167 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "repo_cache.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "check.h"
|
||||
#include "git.h"
|
||||
#include "print.h"
|
||||
#include "scope_guard.h"
|
||||
#include "string_view.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
void GitDirs(const char* dir, bool from_dotgit, std::string& gitdir, std::string& workdir) {
|
||||
git_buf gitdir_buf = {};
|
||||
git_buf workdir_buf = {};
|
||||
ON_SCOPE_EXIT(&) {
|
||||
git_buf_free(&gitdir_buf);
|
||||
git_buf_free(&workdir_buf);
|
||||
};
|
||||
int flags = from_dotgit ? GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_NO_DOTGIT : 0;
|
||||
switch (git_repository_discover_ex(&gitdir_buf, &workdir_buf, NULL, NULL, dir, flags, nullptr)) {
|
||||
case 0:
|
||||
gitdir.assign(gitdir_buf.ptr, gitdir_buf.size);
|
||||
workdir.assign(workdir_buf.ptr, workdir_buf.size);
|
||||
VERIFY(!gitdir.empty() && gitdir.front() == '/' && gitdir.back() == '/');
|
||||
VERIFY(!workdir.empty() && workdir.front() == '/' && workdir.back() == '/');
|
||||
break;
|
||||
case GIT_ENOTFOUND:
|
||||
gitdir.clear();
|
||||
workdir.clear();
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "git_repository_open_ext: " << Print(dir) << ": " << GitError();
|
||||
throw Exception();
|
||||
}
|
||||
}
|
||||
|
||||
git_repository* OpenRepo(const std::string& dir, bool from_dotgit) {
|
||||
git_repository* repo = nullptr;
|
||||
int flags = from_dotgit ? GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_NO_DOTGIT : 0;
|
||||
switch (git_repository_open_ext(&repo, dir.c_str(), flags, nullptr)) {
|
||||
case 0:
|
||||
return repo;
|
||||
case GIT_ENOTFOUND:
|
||||
return nullptr;
|
||||
default:
|
||||
LOG(ERROR) << "git_repository_open_ext: " << Print(dir) << ": " << GitError();
|
||||
throw Exception();
|
||||
}
|
||||
}
|
||||
|
||||
std::string DirName(std::string path) {
|
||||
if (path.empty()) return "";
|
||||
while (path.back() == '/') {
|
||||
path.pop_back();
|
||||
if (path.empty()) return "";
|
||||
}
|
||||
do {
|
||||
path.pop_back();
|
||||
if (path.empty()) return "";
|
||||
} while (path.back() != '/');
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Repo* RepoCache::Open(const std::string& dir, bool from_dotgit) {
|
||||
if (dir.empty() || dir.front() != '/') return nullptr;
|
||||
|
||||
std::string gitdir, workdir;
|
||||
GitDirs(dir.c_str(), from_dotgit, gitdir, workdir);
|
||||
if (gitdir.empty()) {
|
||||
// This isn't quite correct because of differences in canonicalization, .git files and GIT_DIR.
|
||||
// A proper solution would require tracking the "discovery dir" for every repository and
|
||||
// performing path canonicalization.
|
||||
if (from_dotgit) {
|
||||
Erase(cache_.find(dir.back() == '/' ? dir : dir + '/'));
|
||||
} else {
|
||||
std::string path = dir;
|
||||
if (path.back() != '/') path += '/';
|
||||
do {
|
||||
Erase(cache_.find(path + ".git/"));
|
||||
path = DirName(path);
|
||||
} while (!path.empty());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = cache_.find(gitdir);
|
||||
if (it != cache_.end()) {
|
||||
lru_.erase(it->second->lru);
|
||||
it->second->lru = lru_.insert({Clock::now(), it});
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// Opening from gitdir is faster but we cannot use it when gitdir came from a .git file.
|
||||
git_repository* repo =
|
||||
DirName(gitdir) == workdir ? OpenRepo(gitdir, true) : OpenRepo(dir, from_dotgit);
|
||||
if (!repo) return nullptr;
|
||||
ON_SCOPE_EXIT(&) {
|
||||
if (repo) git_repository_free(repo);
|
||||
};
|
||||
if (git_repository_is_bare(repo)) return nullptr;
|
||||
workdir = git_repository_workdir(repo) ?: "";
|
||||
if (workdir.empty()) return nullptr;
|
||||
VERIFY(workdir.front() == '/' && workdir.back() == '/') << Print(workdir);
|
||||
|
||||
auto x = cache_.emplace(gitdir, nullptr);
|
||||
std::unique_ptr<Entry>& elem = x.first->second;
|
||||
if (elem) {
|
||||
lru_.erase(elem->lru);
|
||||
} else {
|
||||
LOG(INFO) << "Initializing new repository: " << Print(gitdir);
|
||||
|
||||
// Libgit2 initializes odb and refdb lazily with double-locking. To avoid useless work
|
||||
// when multiple threads attempt to initialize the same db at the same time, we trigger
|
||||
// initialization manually before threads are in play.
|
||||
git_odb* odb;
|
||||
VERIFY(!git_repository_odb(&odb, repo)) << GitError();
|
||||
git_odb_free(odb);
|
||||
|
||||
git_refdb* refdb;
|
||||
VERIFY(!git_repository_refdb(&refdb, repo)) << GitError();
|
||||
git_refdb_free(refdb);
|
||||
|
||||
elem = std::make_unique<Entry>(std::exchange(repo, nullptr), lim_);
|
||||
}
|
||||
elem->lru = lru_.insert({Clock::now(), x.first});
|
||||
return elem.get();
|
||||
}
|
||||
|
||||
void RepoCache::Free(Time cutoff) {
|
||||
while (true) {
|
||||
if (lru_.empty()) break;
|
||||
auto it = lru_.begin();
|
||||
if (it->first > cutoff) break;
|
||||
Erase(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void RepoCache::Erase(Cache::iterator it) {
|
||||
if (it == cache_.end()) return;
|
||||
LOG(INFO) << "Closing repository: " << Print(it->first);
|
||||
lru_.erase(it->second->lru);
|
||||
cache_.erase(it);
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,60 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_REPO_CACHE_H_
|
||||
#define ROMKATV_GITSTATUS_REPO_CACHE_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include "options.h"
|
||||
#include "repo.h"
|
||||
#include "time.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
class RepoCache {
|
||||
public:
|
||||
explicit RepoCache(Limits lim) : lim_(std::move(lim)) {}
|
||||
Repo* Open(const std::string& dir, bool from_dotgit);
|
||||
void Free(Time cutoff);
|
||||
|
||||
private:
|
||||
struct Entry;
|
||||
using Cache = std::unordered_map<std::string, std::unique_ptr<Entry>>;
|
||||
using LRU = std::multimap<Time, Cache::iterator>;
|
||||
|
||||
void Erase(Cache::iterator it);
|
||||
|
||||
Limits lim_;
|
||||
Cache cache_;
|
||||
LRU lru_;
|
||||
|
||||
struct Entry : Repo {
|
||||
using Repo::Repo;
|
||||
LRU::iterator lru;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_REPO_CACHE_H_
|
@ -0,0 +1,130 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "request.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include "check.h"
|
||||
#include "logging.h"
|
||||
#include "print.h"
|
||||
#include "serialization.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
Request ParseRequest(const std::string& s) {
|
||||
Request res;
|
||||
auto begin = s.begin(), end = s.end(), sep = std::find(begin, end, kFieldSep);
|
||||
VERIFY(sep != end) << "Malformed request: " << s;
|
||||
res.id.assign(begin, sep);
|
||||
|
||||
begin = sep + 1;
|
||||
if (*begin == ':') {
|
||||
res.from_dotgit = true;
|
||||
++begin;
|
||||
}
|
||||
sep = std::find(begin, end, kFieldSep);
|
||||
res.dir.assign(begin, sep);
|
||||
if (sep == end) return res;
|
||||
|
||||
begin = sep + 1;
|
||||
VERIFY(begin + 1 == end && (*begin == '0' || *begin == '1')) << "Malformed request: " << s;
|
||||
res.diff = *begin == '0';
|
||||
return res;
|
||||
}
|
||||
|
||||
bool IsLockedFd(int fd) {
|
||||
CHECK(fd >= 0);
|
||||
struct flock flock = {};
|
||||
flock.l_type = F_RDLCK;
|
||||
flock.l_whence = SEEK_SET;
|
||||
CHECK(fcntl(fd, F_GETLK, &flock) != -1) << Errno();
|
||||
return flock.l_type != F_UNLCK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const Request& req) {
|
||||
strm << Print(req.id) << " for " << Print(req.dir);
|
||||
if (req.from_dotgit) strm << " [from-dotgit]";
|
||||
if (!req.diff) strm << " [no-diff]";
|
||||
return strm;
|
||||
}
|
||||
|
||||
RequestReader::RequestReader(int fd, int lock_fd, int parent_pid)
|
||||
: fd_(fd), lock_fd_(lock_fd), parent_pid_(parent_pid) {
|
||||
CHECK(fd != lock_fd);
|
||||
}
|
||||
|
||||
bool RequestReader::ReadRequest(Request& req) {
|
||||
auto eol = std::find(read_.begin(), read_.end(), kMsgSep);
|
||||
if (eol != read_.end()) {
|
||||
std::string msg(read_.begin(), eol);
|
||||
read_.erase(read_.begin(), eol + 1);
|
||||
req = ParseRequest(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
char buf[256];
|
||||
while (true) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd_, &fds);
|
||||
struct timeval timeout = {.tv_sec = 1};
|
||||
|
||||
int n;
|
||||
CHECK((n = select(fd_ + 1, &fds, NULL, NULL, &timeout)) >= 0) << Errno();
|
||||
if (n == 0) {
|
||||
if (lock_fd_ >= 0 && !IsLockedFd(lock_fd_)) {
|
||||
LOG(INFO) << "Lock on fd " << lock_fd_ << " is gone. Exiting.";
|
||||
std::exit(0);
|
||||
}
|
||||
if (parent_pid_ >= 0 && kill(parent_pid_, 0)) {
|
||||
LOG(INFO) << "Unable to send signal 0 to " << parent_pid_ << ". Exiting.";
|
||||
std::exit(0);
|
||||
}
|
||||
req = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK((n = read(fd_, buf, sizeof(buf))) >= 0) << Errno();
|
||||
if (n == 0) {
|
||||
LOG(INFO) << "EOF. Exiting.";
|
||||
std::exit(0);
|
||||
}
|
||||
read_.insert(read_.end(), buf, buf + n);
|
||||
int eol = std::find(buf, buf + n, kMsgSep) - buf;
|
||||
if (eol != n) {
|
||||
std::string msg(read_.begin(), read_.end() - (n - eol));
|
||||
read_.erase(read_.begin(), read_.begin() + msg.size() + 1);
|
||||
req = ParseRequest(msg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,50 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_REQUEST_H_
|
||||
#define ROMKATV_GITSTATUS_REQUEST_H_
|
||||
|
||||
#include <deque>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
struct Request {
|
||||
std::string id;
|
||||
std::string dir;
|
||||
bool from_dotgit = false;
|
||||
bool diff = true;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const Request& req);
|
||||
|
||||
class RequestReader {
|
||||
public:
|
||||
RequestReader(int fd, int lock_fd, int parent_pid);
|
||||
bool ReadRequest(Request& req);
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
int lock_fd_;
|
||||
int parent_pid_;
|
||||
std::deque<char> read_;
|
||||
};
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_REQUEST_H_
|
@ -0,0 +1,73 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "response.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "check.h"
|
||||
#include "serialization.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kUnreadable = '?';
|
||||
|
||||
void SafePrint(std::ostream& strm, StringView s) {
|
||||
for (size_t i = 0; i != s.len; ++i) {
|
||||
char c = s.ptr[i];
|
||||
strm << (c > 127 || std::isprint(c) ? c : kUnreadable);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ResponseWriter::ResponseWriter(std::string request_id) : request_id_(std::move(request_id)) {
|
||||
SafePrint(strm_, request_id_);
|
||||
Print(1);
|
||||
}
|
||||
|
||||
ResponseWriter::~ResponseWriter() {
|
||||
if (!done_) {
|
||||
strm_.str("");
|
||||
SafePrint(strm_, request_id_);
|
||||
Print("0");
|
||||
Dump("without git status");
|
||||
}
|
||||
}
|
||||
|
||||
void ResponseWriter::Print(ssize_t val) {
|
||||
strm_ << kFieldSep;
|
||||
strm_ << val;
|
||||
}
|
||||
|
||||
void ResponseWriter::Print(StringView val) {
|
||||
strm_ << kFieldSep;
|
||||
SafePrint(strm_, val);
|
||||
}
|
||||
|
||||
void ResponseWriter::Dump(const char* log) {
|
||||
CHECK(!done_);
|
||||
done_ = true;
|
||||
LOG(INFO) << "Replying " << log;
|
||||
std::cout << strm_.str() << kMsgSep << std::flush;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,50 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_RESPONSE_H_
|
||||
#define ROMKATV_GITSTATUS_RESPONSE_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "string_view.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
class ResponseWriter {
|
||||
public:
|
||||
ResponseWriter(std::string request_id);
|
||||
ResponseWriter(ResponseWriter&&) = delete;
|
||||
~ResponseWriter();
|
||||
|
||||
void Print(ssize_t val);
|
||||
void Print(StringView val);
|
||||
void Print(const char* val) { Print(StringView(val)); }
|
||||
|
||||
void Dump(const char* log);
|
||||
|
||||
private:
|
||||
bool done_ = false;
|
||||
std::string request_id_;
|
||||
std::ostringstream strm_;
|
||||
};
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_RESPONSE_H_
|
@ -0,0 +1,56 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_SCOPE_GUARD_H_
|
||||
#define ROMKATV_GITSTATUS_SCOPE_GUARD_H_
|
||||
|
||||
#include <utility>
|
||||
|
||||
#define ON_SCOPE_EXIT(capture...) \
|
||||
auto GITSTATUS_INTERNAL_CAT(_gitstatus_scope_guard_, __COUNTER__) = \
|
||||
::gitstatus::internal_scope_guard::ScopeGuardGenerator() = [capture]()
|
||||
|
||||
#define GITSTATUS_INTERNAL_CAT_I(x, y) x##y
|
||||
#define GITSTATUS_INTERNAL_CAT(x, y) GITSTATUS_INTERNAL_CAT_I(x, y)
|
||||
|
||||
namespace gitstatus {
|
||||
namespace internal_scope_guard {
|
||||
|
||||
void Undefined();
|
||||
|
||||
template <class F>
|
||||
class ScopeGuard {
|
||||
public:
|
||||
explicit ScopeGuard(F f) : f_(std::move(f)) {}
|
||||
~ScopeGuard() { std::move(f_)(); }
|
||||
ScopeGuard(ScopeGuard&& other) : f_(std::move(other.f_)) { Undefined(); }
|
||||
|
||||
private:
|
||||
F f_;
|
||||
};
|
||||
|
||||
struct ScopeGuardGenerator {
|
||||
template <class F>
|
||||
ScopeGuard<F> operator=(F f) const {
|
||||
return ScopeGuard<F>(std::move(f));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal_scope_guard
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_SCOPE_GUARD_H_
|
@ -0,0 +1,28 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_SERIALIZATION_H_
|
||||
#define ROMKATV_GITSTATUS_SERIALIZATION_H_
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
constexpr char kFieldSep = 31; // ascii 31 is unit separator
|
||||
constexpr char kMsgSep = 30; // ascii 30 is record separator
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_SERIALIZATION_H_
|
@ -0,0 +1,23 @@
|
||||
#ifndef ROMKATV_GITSTATUS_STAT_H_
|
||||
#define ROMKATV_GITSTATUS_STAT_H_
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
inline const struct timespec& MTim(const struct stat& s) {
|
||||
#ifdef __APPLE__
|
||||
return s.st_mtimespec;
|
||||
#else
|
||||
return s.st_mtim;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool StatEq(const struct stat& x, const struct stat& y) {
|
||||
return MTim(x).tv_sec == MTim(y).tv_sec && MTim(x).tv_nsec == MTim(y).tv_nsec &&
|
||||
x.st_size == y.st_size && x.st_ino == y.st_ino && x.st_mode == y.st_mode;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_STAT_H_
|
@ -0,0 +1,151 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_STRING_CMP_H_
|
||||
#define ROMKATV_GITSTATUS_STRING_CMP_H_
|
||||
|
||||
#include <string.h> // because there is no std::strcasecmp in C++
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "string_view.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// WARNING: These routines assume no embedded null characters in StringView. Violations cause UB.
|
||||
|
||||
template <int kCaseSensitive = -1>
|
||||
struct StrCmp;
|
||||
|
||||
template <>
|
||||
struct StrCmp<0> {
|
||||
int operator()(StringView x, StringView y) const {
|
||||
size_t n = std::min(x.len, y.len);
|
||||
int cmp = strncasecmp(x.ptr, y.ptr, n);
|
||||
if (cmp) return cmp;
|
||||
return static_cast<ssize_t>(x.len) - static_cast<ssize_t>(y.len);
|
||||
}
|
||||
|
||||
int operator()(StringView x, const char* y) const {
|
||||
for (const char *p = x.ptr, *e = p + x.len; p != e; ++p, ++y) {
|
||||
if (int cmp = std::tolower(*p) - std::tolower(*y)) return cmp;
|
||||
}
|
||||
return 0 - *y;
|
||||
}
|
||||
|
||||
int operator()(char x, char y) const { return std::tolower(x) - std::tolower(y); }
|
||||
int operator()(const char* x, const char* y) const { return strcasecmp(x, y); }
|
||||
int operator()(const char* x, StringView y) const { return -operator()(y, x); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct StrCmp<1> {
|
||||
int operator()(StringView x, StringView y) const {
|
||||
size_t n = std::min(x.len, y.len);
|
||||
int cmp = std::memcmp(x.ptr, y.ptr, n);
|
||||
if (cmp) return cmp;
|
||||
return static_cast<ssize_t>(x.len) - static_cast<ssize_t>(y.len);
|
||||
}
|
||||
|
||||
int operator()(StringView x, const char* y) const {
|
||||
for (const char *p = x.ptr, *e = p + x.len; p != e; ++p, ++y) {
|
||||
if (int cmp = *p - *y) return cmp;
|
||||
}
|
||||
return 0 - *y;
|
||||
}
|
||||
|
||||
int operator()(char x, char y) const { return x - y; }
|
||||
int operator()(const char* x, const char* y) const { return std::strcmp(x, y); }
|
||||
int operator()(const char* x, StringView y) const { return -operator()(y, x); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct StrCmp<-1> {
|
||||
explicit StrCmp(bool case_sensitive) : case_sensitive(case_sensitive) {}
|
||||
|
||||
template <class X, class Y>
|
||||
int operator()(const X& x, const Y& y) const {
|
||||
return case_sensitive ? StrCmp<1>()(x, y) : StrCmp<0>()(x, y);
|
||||
}
|
||||
|
||||
bool case_sensitive;
|
||||
};
|
||||
|
||||
template <int kCaseSensitive = -1>
|
||||
struct StrLt : private StrCmp<kCaseSensitive> {
|
||||
using StrCmp<kCaseSensitive>::StrCmp;
|
||||
|
||||
template <class X, class Y>
|
||||
bool operator()(const X& x, const Y& y) const {
|
||||
return StrCmp<kCaseSensitive>::operator()(x, y) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <int kCaseSensitive = -1>
|
||||
struct StrEq : private StrCmp<kCaseSensitive> {
|
||||
using StrCmp<kCaseSensitive>::StrCmp;
|
||||
|
||||
template <class X, class Y>
|
||||
bool operator()(const X& x, const Y& y) const {
|
||||
return StrCmp<kCaseSensitive>::operator()(x, y) == 0;
|
||||
}
|
||||
|
||||
bool operator()(const StringView& x, const StringView& y) const {
|
||||
return x.len == y.len && StrCmp<kCaseSensitive>::operator()(x, y) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <int kCaseSensitive = -1>
|
||||
struct Str {
|
||||
static_assert(kCaseSensitive == 0 || kCaseSensitive == 1, "");
|
||||
|
||||
static const bool case_sensitive = kCaseSensitive;
|
||||
|
||||
StrCmp<kCaseSensitive> Cmp;
|
||||
StrLt<kCaseSensitive> Lt;
|
||||
StrEq<kCaseSensitive> Eq;
|
||||
};
|
||||
|
||||
template <int kCaseSensitive>
|
||||
const bool Str<kCaseSensitive>::case_sensitive;
|
||||
|
||||
template <>
|
||||
struct Str<-1> {
|
||||
explicit Str(bool case_sensitive)
|
||||
: case_sensitive(case_sensitive),
|
||||
Cmp(case_sensitive),
|
||||
Lt(case_sensitive),
|
||||
Eq(case_sensitive) {}
|
||||
|
||||
bool case_sensitive;
|
||||
|
||||
StrCmp<-1> Cmp;
|
||||
StrLt<-1> Lt;
|
||||
StrEq<-1> Eq;
|
||||
};
|
||||
|
||||
template <class Iter>
|
||||
void StrSort(Iter begin, Iter end, bool case_sensitive) {
|
||||
case_sensitive ? std::sort(begin, end, StrLt<true>()) : std::sort(begin, end, StrLt<false>());
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_STRING_CMP_H_
|
@ -0,0 +1,77 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_STRING_VIEW_H_
|
||||
#define ROMKATV_GITSTATUS_STRING_VIEW_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// WARNING: StringView must not have embedded null characters. Violations cause UB.
|
||||
struct StringView {
|
||||
StringView() : StringView("") {}
|
||||
|
||||
// Requires: !memchr(s.data(), 0, s.size()).
|
||||
//
|
||||
// WARNING: The existence of this requirement and the fact that this constructor is implicit
|
||||
// means it's dangerous to have std::string instances with embedded null characters anywhere
|
||||
// in the program. If you have an std::string `s` with embedded nulls, an innocent-looking
|
||||
// `F(s)` might perform an implicit conversion to StringView and land you squarely in the
|
||||
// Undefined Behavior land.
|
||||
StringView(const std::string& s) : StringView(s.c_str(), s.size()) {}
|
||||
|
||||
// Requires: !memchr(ptr, 0, len).
|
||||
StringView(const char* ptr, size_t len) : ptr(ptr), len(len) {}
|
||||
|
||||
// Requires: end >= begin && !memchr(begin, 0, end - begin).
|
||||
StringView(const char* begin, const char* end) : StringView(begin, end - begin) {}
|
||||
|
||||
// Requires: strchr(s, 0) == s + N.
|
||||
template <size_t N>
|
||||
StringView(const char (&s)[N]) : StringView(s, N - 1) {
|
||||
static_assert(N, "");
|
||||
}
|
||||
|
||||
// Explicit because it's the only constructor that isn't O(1).
|
||||
// Are you sure you don't already known the strings's length?
|
||||
explicit StringView(const char* ptr) : StringView(ptr, ptr ? std::strlen(ptr) : 0) {}
|
||||
|
||||
bool StartsWith(StringView prefix) const {
|
||||
return len >= prefix.len && !std::memcmp(ptr, prefix.ptr, prefix.len);
|
||||
}
|
||||
|
||||
bool EndsWith(StringView suffix) const {
|
||||
return len >= suffix.len && !std::memcmp(ptr + (len - suffix.len), suffix.ptr, suffix.len);
|
||||
}
|
||||
|
||||
const char* ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& strm, StringView s) {
|
||||
if (s.ptr) strm.write(s.ptr, s.len);
|
||||
return strm;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_STRING_VIEW_H_
|
@ -0,0 +1,71 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "strings.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
void CEscape(std::ostream& strm, const char* begin, const char* end) {
|
||||
assert(!begin == !end);
|
||||
if (!begin) return;
|
||||
for (; begin != end; ++begin) {
|
||||
const unsigned char c = *begin;
|
||||
switch (c) {
|
||||
case '\t':
|
||||
strm << "\\t";
|
||||
continue;
|
||||
case '\n':
|
||||
strm << "\\n";
|
||||
continue;
|
||||
case '\r':
|
||||
strm << "\\r";
|
||||
continue;
|
||||
case '"':
|
||||
strm << "\\\"";
|
||||
continue;
|
||||
case '\'':
|
||||
strm << "\\'";
|
||||
continue;
|
||||
case '\\':
|
||||
strm << "\\\\";
|
||||
continue;
|
||||
}
|
||||
if (c > 31 && c < 127) {
|
||||
strm << c;
|
||||
continue;
|
||||
}
|
||||
strm << '\\';
|
||||
strm << static_cast<char>('0' + ((c >> 6) & 7));
|
||||
strm << static_cast<char>('0' + ((c >> 3) & 7));
|
||||
strm << static_cast<char>('0' + ((c >> 0) & 7));
|
||||
}
|
||||
}
|
||||
|
||||
void Quote(std::ostream& strm, const char* begin, const char* end) {
|
||||
assert(!begin == !end);
|
||||
if (!begin) {
|
||||
strm << "null";
|
||||
return;
|
||||
}
|
||||
strm << '"';
|
||||
CEscape(strm, begin, end);
|
||||
strm << '"';
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,37 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_STRINGS_H_
|
||||
#define ROMKATV_GITSTATUS_STRINGS_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
// If the pointers are null, prints nothing.
|
||||
//
|
||||
// Requires: !begin == !end.
|
||||
void CEscape(std::ostream& strm, const char* begin, const char* end);
|
||||
|
||||
// If the pointers are null, prints null without quotes.
|
||||
//
|
||||
// Requires: !begin == !end.
|
||||
void Quote(std::ostream& strm, const char* begin, const char* end);
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_STRING_VIEW_H_
|
@ -0,0 +1,332 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "tag_db.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "check.h"
|
||||
#include "dir.h"
|
||||
#include "git.h"
|
||||
#include "print.h"
|
||||
#include "scope_guard.h"
|
||||
#include "stat.h"
|
||||
#include "string_cmp.h"
|
||||
#include "thread_pool.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
static constexpr char kTagPrefix[] = "refs/tags/";
|
||||
|
||||
constexpr int8_t kUnhex[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 3
|
||||
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
|
||||
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 6
|
||||
};
|
||||
|
||||
struct {
|
||||
bool operator()(const Tag* x, const git_oid& y) const {
|
||||
return std::memcmp(x->id.id, y.id, GIT_OID_RAWSZ) < 0;
|
||||
}
|
||||
bool operator()(const git_oid& x, const Tag* y) const {
|
||||
return std::memcmp(x.id, y->id.id, GIT_OID_RAWSZ) < 0;
|
||||
}
|
||||
bool operator()(const Tag* x, const Tag* y) const {
|
||||
return std::memcmp(x->id.id, y->id.id, GIT_OID_RAWSZ) < 0;
|
||||
}
|
||||
} constexpr ById = {};
|
||||
|
||||
struct {
|
||||
bool operator()(const Tag* x, const char* y) const {
|
||||
return std::strcmp(x->name, y) < 0;
|
||||
}
|
||||
bool operator()(const char* x, const Tag* y) const {
|
||||
return std::strcmp(x, y->name) < 0;
|
||||
}
|
||||
bool operator()(const Tag* x, const Tag* y) const {
|
||||
return std::strcmp(x->name, y->name) < 0;
|
||||
}
|
||||
} constexpr ByName = {};
|
||||
|
||||
void ParseOid(unsigned char* oid, const char* begin, const char* end) {
|
||||
VERIFY(end >= begin + GIT_OID_HEXSZ);
|
||||
for (size_t i = 0; i != GIT_OID_HEXSZ; i += 2) {
|
||||
*oid++ = kUnhex[+begin[i]] << 4 | kUnhex[+begin[i + 1]];
|
||||
}
|
||||
}
|
||||
|
||||
const char* StripTag(const char* ref) {
|
||||
for (size_t i = 0; i != sizeof(kTagPrefix) - 1; ++i) {
|
||||
if (*ref++ != kTagPrefix[i]) return nullptr;
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
git_refdb* RefDb(git_repository* repo) {
|
||||
git_refdb* res;
|
||||
VERIFY(!git_repository_refdb(&res, repo)) << GitError();
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TagDb::TagDb(git_repository* repo)
|
||||
: repo_(repo),
|
||||
refdb_(RefDb(repo)),
|
||||
pack_(&pack_arena_),
|
||||
name2id_(&pack_arena_),
|
||||
id2name_(&pack_arena_) {
|
||||
CHECK(repo_ && refdb_);
|
||||
}
|
||||
|
||||
TagDb::~TagDb() {
|
||||
Wait();
|
||||
git_refdb_free(refdb_);
|
||||
}
|
||||
|
||||
std::string TagDb::TagForCommit(const git_oid& oid) {
|
||||
ReadLooseTags();
|
||||
UpdatePack();
|
||||
|
||||
std::string res;
|
||||
|
||||
std::string ref = "refs/tags/";
|
||||
size_t prefix_len = ref.size();
|
||||
for (const char* tag : loose_tags_) {
|
||||
ref.resize(prefix_len);
|
||||
ref += tag;
|
||||
if (res < tag && TagHasTarget(ref.c_str(), &oid)) res = tag;
|
||||
}
|
||||
|
||||
if ((std::unique_lock<std::mutex>(mutex_), id2name_dirty_)) {
|
||||
for (auto it = name2id_.rbegin(); it != name2id_.rend(); ++it) {
|
||||
if (!memcmp((*it)->id.id, oid.id, GIT_OID_RAWSZ) && !IsLooseTag((*it)->name)) {
|
||||
if (res < (*it)->name) res = (*it)->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto r = std::equal_range(id2name_.begin(), id2name_.end(), oid, ById);
|
||||
for (auto it = r.first; it != r.second; ++it) {
|
||||
if (!IsLooseTag((*it)->name) && res < (*it)->name) res = (*it)->name;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TagDb::ReadLooseTags() {
|
||||
loose_tags_.clear();
|
||||
loose_arena_.Reuse();
|
||||
|
||||
std::string dirname = git_repository_path(repo_) + "refs/tags"s;
|
||||
int dir_fd = open(dirname.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
if (dir_fd < 0) return;
|
||||
ON_SCOPE_EXIT(&) { CHECK(!close(dir_fd)) << Errno(); };
|
||||
// TODO: recursively traverse directories so that the file refs/tags/foo/bar gets interpreted
|
||||
// as the tag foo/bar. See https://github.com/romkatv/gitstatus/issues/254.
|
||||
(void)ListDir(dir_fd, loose_arena_, loose_tags_, /* precompose_unicode = */ false,
|
||||
/* case_sensitive = */ true);
|
||||
}
|
||||
|
||||
void TagDb::UpdatePack() {
|
||||
auto Reset = [&] {
|
||||
auto Wipe = [](auto& x) {
|
||||
x.clear();
|
||||
x.shrink_to_fit();
|
||||
};
|
||||
Wait();
|
||||
Wipe(pack_);
|
||||
Wipe(name2id_);
|
||||
Wipe(id2name_);
|
||||
pack_arena_.Reuse();
|
||||
std::memset(&pack_stat_, 0, sizeof(pack_stat_));
|
||||
};
|
||||
|
||||
std::string pack_path = git_repository_path(repo_) + "packed-refs"s;
|
||||
struct stat st;
|
||||
if (stat(pack_path.c_str(), &st)) {
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
if (StatEq(pack_stat_, st)) return;
|
||||
|
||||
Reset();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
LOG(INFO) << "Parsing " << Print(pack_path);
|
||||
int fd = open(pack_path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
VERIFY(fd >= 0);
|
||||
ON_SCOPE_EXIT(&) { CHECK(!close(fd)) << Errno(); };
|
||||
pack_.resize(st.st_size + 1);
|
||||
ssize_t n = read(fd, &pack_[0], st.st_size + 1);
|
||||
VERIFY(n >= 0) << Errno();
|
||||
VERIFY(!fstat(fd, &pack_stat_)) << Errno();
|
||||
if (!StatEq(st, pack_stat_)) {
|
||||
st = pack_stat_;
|
||||
continue;
|
||||
}
|
||||
VERIFY(n == st.st_size);
|
||||
pack_.pop_back();
|
||||
break;
|
||||
}
|
||||
ParsePack();
|
||||
} catch (const Exception&) {
|
||||
Reset();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void TagDb::ParsePack() {
|
||||
char* p = &pack_[0];
|
||||
char* e = p + pack_.size();
|
||||
|
||||
// Usually packed-refs starts with the following line:
|
||||
//
|
||||
// # pack-refs with: peeled fully-peeled sorted
|
||||
//
|
||||
// However, some users can produce pack-refs without this line.
|
||||
// See https://github.com/romkatv/powerlevel10k/issues/1428.
|
||||
// I don't know how they do it. Without the header line we cannot
|
||||
// assume that refs are sorted, which isn't a big deal because we
|
||||
// can just sort them. What's worse is that refs cannot be assumed
|
||||
// to be fully-peeled. We don't want to peel them, so we just drop
|
||||
// all tags.
|
||||
if (*p != '#') {
|
||||
LOG(WARN) << "packed-refs doesn't have a header. Won't resolve tags.";
|
||||
return;
|
||||
}
|
||||
|
||||
char* eol = std::strchr(p, '\n');
|
||||
if (!eol) return;
|
||||
*eol = 0;
|
||||
if (!std::strstr(p, " fully-peeled") || !std::strstr(p, " sorted")) {
|
||||
LOG(WARN) << "packed-refs has unexpected header. Won't resolve tags.";
|
||||
}
|
||||
p = eol + 1;
|
||||
|
||||
name2id_.reserve(pack_.size() / 128);
|
||||
id2name_.reserve(pack_.size() / 128);
|
||||
|
||||
std::vector<Tag*> idx;
|
||||
idx.reserve(pack_.size() / 128);
|
||||
|
||||
while (p != e) {
|
||||
Tag* tag = pack_arena_.Allocate<Tag>();
|
||||
ParseOid(tag->id.id, p, e);
|
||||
p += GIT_OID_HEXSZ;
|
||||
VERIFY(*p++ == ' ');
|
||||
const char* ref = p;
|
||||
VERIFY(p = std::strchr(p, '\n'));
|
||||
p[p[-1] == '\r' ? -1 : 0] = 0;
|
||||
++p;
|
||||
if (*p == '^') {
|
||||
ParseOid(tag->id.id, p + 1, e);
|
||||
p += GIT_OID_HEXSZ + 1;
|
||||
if (p != e) {
|
||||
VERIFY((p = std::strchr(p, '\n')));
|
||||
++p;
|
||||
}
|
||||
}
|
||||
tag->name = StripTag(ref);
|
||||
if (!tag->name) continue;
|
||||
name2id_.push_back(tag);
|
||||
id2name_.push_back(tag);
|
||||
}
|
||||
|
||||
if (!std::is_sorted(name2id_.begin(), name2id_.end(), ByName)) {
|
||||
// "sorted" in the header of packed-refs promisses that this won't trigger.
|
||||
std::sort(name2id_.begin(), name2id_.end(), ByName);
|
||||
}
|
||||
|
||||
id2name_dirty_ = true;
|
||||
GlobalThreadPool()->Schedule([this] {
|
||||
std::sort(id2name_.begin(), id2name_.end(), ById);
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
CHECK(id2name_dirty_);
|
||||
id2name_dirty_ = false;
|
||||
cv_.notify_one();
|
||||
});
|
||||
}
|
||||
|
||||
void TagDb::Wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
while (id2name_dirty_) cv_.wait(lock);
|
||||
}
|
||||
|
||||
bool TagDb::IsLooseTag(const char* name) const {
|
||||
return std::binary_search(loose_tags_.begin(), loose_tags_.end(), name,
|
||||
[](const char* a, const char* b) { return std::strcmp(a, b) < 0; });
|
||||
}
|
||||
|
||||
bool TagDb::TagHasTarget(const char* name, const git_oid* target) const {
|
||||
static constexpr size_t kMaxDerefCount = 10;
|
||||
|
||||
git_reference* ref;
|
||||
if (git_refdb_lookup(&ref, refdb_, name)) return false;
|
||||
ON_SCOPE_EXIT(&) { git_reference_free(ref); };
|
||||
|
||||
for (int i = 0; i != kMaxDerefCount && git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC; ++i) {
|
||||
git_reference* dst;
|
||||
const char* ref_name = git_reference_name(ref);
|
||||
if (git_refdb_lookup(&dst, refdb_, ref_name)) {
|
||||
const char* tag_name = StripTag(ref_name);
|
||||
auto it = std::lower_bound(name2id_.begin(), name2id_.end(), tag_name, ByName);
|
||||
return it != name2id_.end() && !strcmp((*it)->name, tag_name) && !IsLooseTag(tag_name) &&
|
||||
git_oid_equal(&(*it)->id, target);
|
||||
}
|
||||
git_reference_free(ref);
|
||||
ref = dst;
|
||||
}
|
||||
|
||||
if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) return false;
|
||||
const git_oid* oid = git_reference_target_peel(ref) ?: git_reference_target(ref);
|
||||
if (git_oid_equal(oid, target)) return true;
|
||||
|
||||
for (int i = 0; i != kMaxDerefCount; ++i) {
|
||||
git_tag* tag;
|
||||
if (git_tag_lookup(&tag, repo_, oid)) return false;
|
||||
ON_SCOPE_EXIT(&) { git_tag_free(tag); };
|
||||
if (git_tag_target_type(tag) == GIT_OBJECT_COMMIT) {
|
||||
return git_oid_equal(git_tag_target_id(tag), target);
|
||||
}
|
||||
oid = git_tag_target_id(tag);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,79 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_TAG_DB_H_
|
||||
#define ROMKATV_GITSTATUS_TAG_DB_H_
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "arena.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
struct Tag {
|
||||
const char* name;
|
||||
git_oid id;
|
||||
};
|
||||
|
||||
class TagDb {
|
||||
public:
|
||||
explicit TagDb(git_repository* repo);
|
||||
TagDb(TagDb&&) = delete;
|
||||
~TagDb();
|
||||
|
||||
std::string TagForCommit(const git_oid& oid);
|
||||
|
||||
private:
|
||||
void ReadLooseTags();
|
||||
void UpdatePack();
|
||||
void ParsePack();
|
||||
void Wait();
|
||||
|
||||
bool IsLooseTag(const char* name) const;
|
||||
|
||||
bool TagHasTarget(const char* name, const git_oid* target) const;
|
||||
|
||||
git_repository* const repo_;
|
||||
git_refdb* const refdb_;
|
||||
|
||||
Arena pack_arena_;
|
||||
struct stat pack_stat_ = {};
|
||||
WithArena<std::string> pack_;
|
||||
WithArena<std::vector<const Tag*>> name2id_;
|
||||
WithArena<std::vector<const Tag*>> id2name_;
|
||||
|
||||
Arena loose_arena_;
|
||||
std::vector<char*> loose_tags_;
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
bool id2name_dirty_ = false;
|
||||
};
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_TAG_DB_H_
|
@ -0,0 +1,87 @@
|
||||
#include "thread_pool.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
#include "check.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
ThreadPool::ThreadPool(size_t num_threads) : num_inflight_(num_threads) {
|
||||
for (size_t i = 0; i != num_threads; ++i) {
|
||||
threads_.emplace_back([=]() { Loop(i + 1); });
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
exit_ = true;
|
||||
}
|
||||
cv_.notify_all();
|
||||
sleeper_cv_.notify_one();
|
||||
for (std::thread& t : threads_) t.join();
|
||||
}
|
||||
|
||||
void ThreadPool::Schedule(Time t, std::function<void()> f) {
|
||||
std::condition_variable* wake = nullptr;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
work_.push(Work{std::move(t), ++last_idx_, std::move(f)});
|
||||
if (work_.top().idx == last_idx_) wake = have_sleeper_ ? &sleeper_cv_ : &cv_;
|
||||
}
|
||||
if (wake) wake->notify_one();
|
||||
}
|
||||
|
||||
void ThreadPool::Loop(size_t tid) {
|
||||
auto Next = [&]() -> std::function<void()> {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
--num_inflight_;
|
||||
if (work_.empty() && num_inflight_ == 0) idle_cv_.notify_all();
|
||||
while (true) {
|
||||
if (exit_) return nullptr;
|
||||
if (work_.empty()) {
|
||||
cv_.wait(lock);
|
||||
continue;
|
||||
}
|
||||
Time now = Clock::now();
|
||||
const Work& top = work_.top();
|
||||
if (top.t <= now) {
|
||||
std::function<void()> res = std::move(top.f);
|
||||
work_.pop();
|
||||
++num_inflight_;
|
||||
bool notify = !work_.empty() && !have_sleeper_;
|
||||
lock.unlock();
|
||||
if (notify) cv_.notify_one();
|
||||
return res;
|
||||
}
|
||||
if (have_sleeper_) {
|
||||
cv_.wait(lock);
|
||||
continue;
|
||||
}
|
||||
have_sleeper_ = true;
|
||||
sleeper_cv_.wait_until(lock, top.t);
|
||||
assert(have_sleeper_);
|
||||
have_sleeper_ = false;
|
||||
}
|
||||
};
|
||||
while (std::function<void()> f = Next()) f();
|
||||
}
|
||||
|
||||
void ThreadPool::Wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
idle_cv_.wait(lock, [&] { return work_.empty() && num_inflight_ == 0; });
|
||||
}
|
||||
|
||||
static ThreadPool* g_thread_pool = nullptr;
|
||||
|
||||
void InitGlobalThreadPool(size_t num_threads) {
|
||||
CHECK(!g_thread_pool);
|
||||
LOG(INFO) << "Spawning " << num_threads << " thread(s)";
|
||||
g_thread_pool = new ThreadPool(num_threads);
|
||||
}
|
||||
|
||||
ThreadPool* GlobalThreadPool() { return g_thread_pool; }
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,74 @@
|
||||
#ifndef ROMKATV_GITSTATUS_THREAD_POOL_H_
|
||||
#define ROMKATV_GITSTATUS_THREAD_POOL_H_
|
||||
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "time.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
class ThreadPool {
|
||||
public:
|
||||
explicit ThreadPool(size_t num_threads);
|
||||
ThreadPool(ThreadPool&&) = delete;
|
||||
|
||||
// Waits for the currently running functions to finish.
|
||||
// Does NOT wait for the queue of functions to drain.
|
||||
// If you want the latter, call Wait() manually.
|
||||
~ThreadPool();
|
||||
|
||||
// Runs `f` on one of the threads at or after time `t`. Can be called
|
||||
// from any thread. Can be called concurrently.
|
||||
//
|
||||
// Does not block.
|
||||
void Schedule(Time t, std::function<void()> f);
|
||||
|
||||
void Schedule(std::function<void()> f) { Schedule(Clock::now(), std::move(f)); }
|
||||
|
||||
// Blocks until the work queue is empty and there are no currently
|
||||
// running functions.
|
||||
void Wait();
|
||||
|
||||
size_t num_threads() const { return threads_.size(); }
|
||||
|
||||
private:
|
||||
struct Work {
|
||||
bool operator<(const Work& w) const { return std::tie(w.t, w.idx) < std::tie(t, idx); }
|
||||
Time t;
|
||||
int64_t idx;
|
||||
mutable std::function<void()> f;
|
||||
};
|
||||
|
||||
void Loop(size_t tid);
|
||||
|
||||
int64_t last_idx_ = 0;
|
||||
int64_t num_inflight_;
|
||||
bool exit_ = false;
|
||||
// Do we have a thread waiting on sleeper_cv_?
|
||||
bool have_sleeper_ = false;
|
||||
std::mutex mutex_;
|
||||
// Any number of threads can wait on this condvar. Always without a timeout.
|
||||
std::condition_variable cv_;
|
||||
// At most one thread can wait on this condvar at a time. Always with a timeout.
|
||||
std::condition_variable sleeper_cv_;
|
||||
// Signalled when the work queue is empty and there is nothing inflight.
|
||||
std::condition_variable idle_cv_;
|
||||
std::priority_queue<Work> work_;
|
||||
std::vector<std::thread> threads_;
|
||||
};
|
||||
|
||||
void InitGlobalThreadPool(size_t num_threads);
|
||||
|
||||
ThreadPool* GlobalThreadPool();
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_THREAD_POOL_H_
|
@ -0,0 +1,14 @@
|
||||
#ifndef ROMKATV_GITSTATUS_TIME_H_
|
||||
#define ROMKATV_GITSTATUS_TIME_H_
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using Time = Clock::time_point;
|
||||
using Duration = Clock::duration;
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_TIME_H_
|
@ -0,0 +1,72 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "check.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
namespace {
|
||||
|
||||
double CpuTimeMs() {
|
||||
auto ToMs = [](const timeval& tv) { return 1e3 * tv.tv_sec + 1e-3 * tv.tv_usec; };
|
||||
rusage usage = {};
|
||||
CHECK(getrusage(RUSAGE_SELF, &usage) == 0) << Errno();
|
||||
return ToMs(usage.ru_utime) + ToMs(usage.ru_stime);
|
||||
}
|
||||
|
||||
double WallTimeMs() {
|
||||
// An attempt to call clock_gettime on an ancient version of MacOS fails at runtime.
|
||||
// It's possible to detect the presence of clock_gettime at runtime but I don't have
|
||||
// an ancient MacOS to test the code. Hence this.
|
||||
#ifdef __APPLE__
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
#else
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return 1e3 * ts.tv_sec + 1e-6 * ts.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Timer::Start() {
|
||||
cpu_ = CpuTimeMs();
|
||||
wall_ = WallTimeMs();
|
||||
}
|
||||
|
||||
void Timer::Report(const char* msg) {
|
||||
double cpu = CpuTimeMs() - cpu_;
|
||||
if (std::isnan(wall_)) {
|
||||
LOG(INFO) << "Timing for: " << msg << ": " << cpu << "ms cpu";
|
||||
} else {
|
||||
double wall = WallTimeMs() - wall_;
|
||||
LOG(INFO) << "Timing for: " << msg << ": " << cpu << "ms cpu, " << wall << "ms wall";
|
||||
}
|
||||
Start();
|
||||
}
|
||||
|
||||
} // namespace gitstatus
|
@ -0,0 +1,36 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_TIMER_H_
|
||||
#define ROMKATV_GITSTATUS_TIMER_H_
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer() { Start(); }
|
||||
void Start();
|
||||
void Report(const char* msg);
|
||||
|
||||
private:
|
||||
double cpu_;
|
||||
double wall_;
|
||||
};
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_TIMER_H_
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2019 Roman Perepelitsa.
|
||||
//
|
||||
// This file is part of GitStatus.
|
||||
//
|
||||
// GitStatus is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// GitStatus is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GitStatus. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROMKATV_GITSTATUS_TRIBOOL_H_
|
||||
#define ROMKATV_GITSTATUS_TRIBOOL_H_
|
||||
|
||||
namespace gitstatus {
|
||||
|
||||
enum class Tribool : int { kFalse = 0, kTrue = 1, kUnknown = -1 };
|
||||
|
||||
} // namespace gitstatus
|
||||
|
||||
#endif // ROMKATV_GITSTATUS_TRIBOOL_H_
|
@ -0,0 +1,82 @@
|
||||
# Fewer than 47 columns will probably work. Haven't tried it.
|
||||
typeset -gr __p9k_wizard_columns=47
|
||||
# The bottleneck is ask_tails with nerd fonts. Everything else works fine with 12 lines.
|
||||
typeset -gr __p9k_wizard_lines=14
|
||||
typeset -gr __p9k_zd=${ZDOTDIR:-$HOME}
|
||||
typeset -gr __p9k_zd_u=${${${(q)__p9k_zd}/#(#b)${(q)HOME}(|\/*)/'~'$match[1]}//\%/%%}
|
||||
typeset -gr __p9k_zshrc=${${:-$__p9k_zd/.zshrc}:A}
|
||||
typeset -gr __p9k_zshrc_u=$__p9k_zd_u/.zshrc
|
||||
typeset -gr __p9k_root_dir_u=${${${(q)__p9k_root_dir}/#(#b)${(q)HOME}(|\/*)/'~'$match[1]}//\%/%%}
|
||||
|
||||
function _p9k_can_configure() {
|
||||
[[ $1 == '-q' ]] && local -i q=1 || local -i q=0
|
||||
function $0_error() {
|
||||
(( q )) || print -rP "%1F[ERROR]%f %Bp10k configure%b: $1" >&2
|
||||
}
|
||||
typeset -g __p9k_cfg_path_o=${POWERLEVEL9K_CONFIG_FILE:=${ZDOTDIR:-~}/.p10k.zsh}
|
||||
typeset -g __p9k_cfg_basename=${__p9k_cfg_path_o:t}
|
||||
typeset -g __p9k_cfg_path=${__p9k_cfg_path_o:A}
|
||||
typeset -g __p9k_cfg_path_u=${${${(q)__p9k_cfg_path_o}/#(#b)${(q)HOME}(|\/*)/'~'$match[1]}//\%/%%}
|
||||
{
|
||||
[[ -o multibyte ]] || { $0_error "multibyte option is not set"; return 1 }
|
||||
[[ -e $__p9k_zd ]] || { $0_error "$__p9k_zd_u does not exist"; return 1 }
|
||||
[[ -d $__p9k_zd ]] || { $0_error "$__p9k_zd_u is not a directory"; return 1 }
|
||||
[[ ! -d $__p9k_cfg_path ]] || { $0_error "$__p9k_cfg_path_u is a directory"; return 1 }
|
||||
[[ ! -d $__p9k_zshrc ]] || { $0_error "$__p9k_zshrc_u is a directory"; return 1 }
|
||||
|
||||
local dir=${__p9k_cfg_path:h}
|
||||
while [[ ! -e $dir && $dir != ${dir:h} ]]; do dir=${dir:h}; done
|
||||
if [[ ! -d $dir ]]; then
|
||||
$0_error "cannot create $__p9k_cfg_path_u because ${dir//\%/%%} is not a directory"
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -w $dir ]]; then
|
||||
$0_error "cannot create $__p9k_cfg_path_u because ${dir//\%/%%} is readonly"
|
||||
return 1
|
||||
fi
|
||||
|
||||
[[ ! -e $__p9k_cfg_path || -f $__p9k_cfg_path || -h $__p9k_cfg_path ]] || {
|
||||
$0_error "$__p9k_cfg_path_u is a special file"
|
||||
return 1
|
||||
}
|
||||
[[ ! -e $__p9k_zshrc || -f $__p9k_zshrc || -h $__p9k_zshrc ]] || {
|
||||
$0_error "$__p9k_zshrc_u a special file"
|
||||
return 1
|
||||
}
|
||||
[[ ! -e $__p9k_zshrc || -r $__p9k_zshrc ]] || {
|
||||
$0_error "$__p9k_zshrc_u is not readable"
|
||||
return 1
|
||||
}
|
||||
local style
|
||||
for style in lean lean-8colors classic rainbow pure; do
|
||||
[[ -r $__p9k_root_dir/config/p10k-$style.zsh ]] || {
|
||||
$0_error "$__p9k_root_dir_u/config/p10k-$style.zsh is not readable"
|
||||
return 1
|
||||
}
|
||||
done
|
||||
|
||||
(( LINES >= __p9k_wizard_lines && COLUMNS >= __p9k_wizard_columns )) || {
|
||||
$0_error "terminal size too small; must be at least $__p9k_wizard_columns columns by $__p9k_wizard_lines lines"
|
||||
return 1
|
||||
}
|
||||
[[ -t 0 && -t 1 ]] || { $0_error "no TTY"; return 2 }
|
||||
return 0
|
||||
} always {
|
||||
unfunction $0_error
|
||||
}
|
||||
}
|
||||
|
||||
function p9k_configure() {
|
||||
eval "$__p9k_intro"
|
||||
_p9k_can_configure || return
|
||||
(
|
||||
set -- -f
|
||||
builtin source $__p9k_root_dir/internal/wizard.zsh
|
||||
)
|
||||
local ret=$?
|
||||
case $ret in
|
||||
0) builtin source $__p9k_cfg_path; _p9k__force_must_init=1;;
|
||||
69) return 0;;
|
||||
*) return $ret;;
|
||||
esac
|
||||
}
|
869
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/icons.zsh
Normal file
869
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/icons.zsh
Normal file
@ -0,0 +1,869 @@
|
||||
typeset -gA icons
|
||||
|
||||
function _p9k_init_icons() {
|
||||
[[ -n ${POWERLEVEL9K_MODE-} || ${langinfo[CODESET]} == (utf|UTF)(-|)8 ]] || local POWERLEVEL9K_MODE=ascii
|
||||
[[ $_p9k__icon_mode == $POWERLEVEL9K_MODE/$POWERLEVEL9K_LEGACY_ICON_SPACING/$POWERLEVEL9K_ICON_PADDING ]] && return
|
||||
typeset -g _p9k__icon_mode=$POWERLEVEL9K_MODE/$POWERLEVEL9K_LEGACY_ICON_SPACING/$POWERLEVEL9K_ICON_PADDING
|
||||
|
||||
if [[ $POWERLEVEL9K_LEGACY_ICON_SPACING == true ]]; then
|
||||
local s=
|
||||
local q=' '
|
||||
else
|
||||
local s=' '
|
||||
local q=
|
||||
fi
|
||||
|
||||
case $POWERLEVEL9K_MODE in
|
||||
'flat'|'awesome-patched')
|
||||
# Awesome-Patched Font required! See:
|
||||
# https://github.com/gabrielelana/awesome-terminal-fonts/tree/patching-strategy/patched
|
||||
icons=(
|
||||
RULER_CHAR '\u2500' # ─
|
||||
LEFT_SEGMENT_SEPARATOR '\uE0B0' #
|
||||
RIGHT_SEGMENT_SEPARATOR '\uE0B2' #
|
||||
LEFT_SEGMENT_END_SEPARATOR ' ' # Whitespace
|
||||
LEFT_SUBSEGMENT_SEPARATOR '\uE0B1' #
|
||||
RIGHT_SUBSEGMENT_SEPARATOR '\uE0B3' #
|
||||
CARRIAGE_RETURN_ICON '\u21B5'$s # ↵
|
||||
ROOT_ICON '\uE801' #
|
||||
SUDO_ICON '\uE0A2' #
|
||||
RUBY_ICON '\uE847 ' #
|
||||
AWS_ICON '\uE895'$s #
|
||||
AWS_EB_ICON '\U1F331'$q # 🌱
|
||||
BACKGROUND_JOBS_ICON '\uE82F ' #
|
||||
TEST_ICON '\uE891'$s #
|
||||
TODO_ICON '\u2611' # ☑
|
||||
BATTERY_ICON '\uE894'$s #
|
||||
DISK_ICON '\uE1AE ' #
|
||||
OK_ICON '\u2714' # ✔
|
||||
FAIL_ICON '\u2718' # ✘
|
||||
SYMFONY_ICON 'SF'
|
||||
NODE_ICON '\u2B22'$s # ⬢
|
||||
NODEJS_ICON '\u2B22'$s # ⬢
|
||||
MULTILINE_FIRST_PROMPT_PREFIX '\u256D\U2500' # ╭─
|
||||
MULTILINE_NEWLINE_PROMPT_PREFIX '\u251C\U2500' # ├─
|
||||
MULTILINE_LAST_PROMPT_PREFIX '\u2570\U2500 ' # ╰─
|
||||
APPLE_ICON '\uE26E'$s #
|
||||
WINDOWS_ICON '\uE26F'$s #
|
||||
FREEBSD_ICON '\U1F608'$q # 😈
|
||||
ANDROID_ICON '\uE270'$s #
|
||||
LINUX_ICON '\uE271'$s #
|
||||
LINUX_ARCH_ICON '\uE271'$s #
|
||||
LINUX_DEBIAN_ICON '\uE271'$s #
|
||||
LINUX_RASPBIAN_ICON '\uE271'$s #
|
||||
LINUX_UBUNTU_ICON '\uE271'$s #
|
||||
LINUX_CENTOS_ICON '\uE271'$s #
|
||||
LINUX_COREOS_ICON '\uE271'$s #
|
||||
LINUX_ELEMENTARY_ICON '\uE271'$s #
|
||||
LINUX_MINT_ICON '\uE271'$s #
|
||||
LINUX_FEDORA_ICON '\uE271'$s #
|
||||
LINUX_GENTOO_ICON '\uE271'$s #
|
||||
LINUX_MAGEIA_ICON '\uE271'$s #
|
||||
LINUX_NIXOS_ICON '\uE271'$s #
|
||||
LINUX_MANJARO_ICON '\uE271'$s #
|
||||
LINUX_DEVUAN_ICON '\uE271'$s #
|
||||
LINUX_ALPINE_ICON '\uE271'$s #
|
||||
LINUX_AOSC_ICON '\uE271'$s #
|
||||
LINUX_OPENSUSE_ICON '\uE271'$s #
|
||||
LINUX_SABAYON_ICON '\uE271'$s #
|
||||
LINUX_SLACKWARE_ICON '\uE271'$s #
|
||||
LINUX_VOID_ICON '\uE271'$s #
|
||||
LINUX_ARTIX_ICON '\uE271'$s #
|
||||
LINUX_RHEL_ICON '\uE271'$s #
|
||||
SUNOS_ICON '\U1F31E'$q # 🌞
|
||||
HOME_ICON '\uE12C'$s #
|
||||
HOME_SUB_ICON '\uE18D'$s #
|
||||
FOLDER_ICON '\uE818'$s #
|
||||
NETWORK_ICON '\uE1AD'$s #
|
||||
ETC_ICON '\uE82F'$s #
|
||||
LOAD_ICON '\uE190 ' #
|
||||
SWAP_ICON '\uE87D'$s #
|
||||
RAM_ICON '\uE1E2 ' #
|
||||
SERVER_ICON '\uE895'$s #
|
||||
VCS_UNTRACKED_ICON '\uE16C'$s #
|
||||
VCS_UNSTAGED_ICON '\uE17C'$s #
|
||||
VCS_STAGED_ICON '\uE168'$s #
|
||||
VCS_STASH_ICON '\uE133 ' #
|
||||
#VCS_INCOMING_CHANGES_ICON '\uE1EB ' #
|
||||
#VCS_INCOMING_CHANGES_ICON '\uE80D ' #
|
||||
VCS_INCOMING_CHANGES_ICON '\uE131 ' #
|
||||
#VCS_OUTGOING_CHANGES_ICON '\uE1EC ' #
|
||||
#VCS_OUTGOING_CHANGES_ICON '\uE80E ' #
|
||||
VCS_OUTGOING_CHANGES_ICON '\uE132 ' #
|
||||
VCS_TAG_ICON '\uE817 ' #
|
||||
VCS_BOOKMARK_ICON '\uE87B' #
|
||||
VCS_COMMIT_ICON '\uE821 ' #
|
||||
VCS_BRANCH_ICON '\uE220 ' #
|
||||
VCS_REMOTE_BRANCH_ICON '\u2192' # →
|
||||
VCS_LOADING_ICON ''
|
||||
VCS_GIT_ICON '\uE20E ' #
|
||||
VCS_GIT_GITHUB_ICON '\uE20E ' #
|
||||
VCS_GIT_BITBUCKET_ICON '\uE20E ' #
|
||||
VCS_GIT_GITLAB_ICON '\uE20E ' #
|
||||
VCS_HG_ICON '\uE1C3 ' #
|
||||
VCS_SVN_ICON 'svn'$q
|
||||
RUST_ICON 'R'
|
||||
PYTHON_ICON '\uE63C'$s # (doesn't always work)
|
||||
SWIFT_ICON 'Swift'
|
||||
GO_ICON 'Go'
|
||||
GOLANG_ICON 'Go'
|
||||
PUBLIC_IP_ICON 'IP'
|
||||
LOCK_ICON '\UE138' #
|
||||
NORDVPN_ICON '\UE138' #
|
||||
EXECUTION_TIME_ICON '\UE89C'$s #
|
||||
SSH_ICON 'ssh'
|
||||
VPN_ICON '\UE138'
|
||||
KUBERNETES_ICON '\U2388'$s # ⎈
|
||||
DROPBOX_ICON '\UF16B'$s # (doesn't always work)
|
||||
DATE_ICON '\uE184'$s #
|
||||
TIME_ICON '\uE12E'$s #
|
||||
JAVA_ICON '\U2615' # ☕︎
|
||||
LARAVEL_ICON ''
|
||||
RANGER_ICON '\u2B50' # ⭐
|
||||
MIDNIGHT_COMMANDER_ICON 'mc'
|
||||
VIM_ICON 'vim'
|
||||
TERRAFORM_ICON 'tf'
|
||||
PROXY_ICON '\u2194' # ↔
|
||||
DOTNET_ICON '.NET'
|
||||
DOTNET_CORE_ICON '.NET'
|
||||
AZURE_ICON '\u2601' # ☁
|
||||
DIRENV_ICON '\u25BC' # ▼
|
||||
FLUTTER_ICON 'F'
|
||||
GCLOUD_ICON 'G'
|
||||
LUA_ICON 'lua'
|
||||
PERL_ICON 'perl'
|
||||
NNN_ICON 'nnn'
|
||||
XPLR_ICON 'xplr'
|
||||
TIMEWARRIOR_ICON 'tw'
|
||||
TASKWARRIOR_ICON 'task'
|
||||
NIX_SHELL_ICON 'nix'
|
||||
WIFI_ICON 'WiFi'
|
||||
ERLANG_ICON 'erl'
|
||||
ELIXIR_ICON 'elixir'
|
||||
POSTGRES_ICON 'postgres'
|
||||
PHP_ICON 'php'
|
||||
HASKELL_ICON 'hs'
|
||||
PACKAGE_ICON 'pkg'
|
||||
JULIA_ICON 'jl'
|
||||
SCALA_ICON 'scala'
|
||||
TOOLBOX_ICON '\u2B22' # ⬢
|
||||
)
|
||||
;;
|
||||
'awesome-fontconfig')
|
||||
# fontconfig with awesome-font required! See
|
||||
# https://github.com/gabrielelana/awesome-terminal-fonts
|
||||
icons=(
|
||||
RULER_CHAR '\u2500' # ─
|
||||
LEFT_SEGMENT_SEPARATOR '\uE0B0' #
|
||||
RIGHT_SEGMENT_SEPARATOR '\uE0B2' #
|
||||
LEFT_SEGMENT_END_SEPARATOR ' ' # Whitespace
|
||||
LEFT_SUBSEGMENT_SEPARATOR '\uE0B1' #
|
||||
RIGHT_SUBSEGMENT_SEPARATOR '\uE0B3' #
|
||||
CARRIAGE_RETURN_ICON '\u21B5' # ↵
|
||||
ROOT_ICON '\uF201'$s #
|
||||
SUDO_ICON '\uF09C'$s #
|
||||
RUBY_ICON '\uF219 ' #
|
||||
AWS_ICON '\uF270'$s #
|
||||
AWS_EB_ICON '\U1F331'$q # 🌱
|
||||
BACKGROUND_JOBS_ICON '\uF013 ' #
|
||||
TEST_ICON '\uF291'$s #
|
||||
TODO_ICON '\u2611' # ☑
|
||||
BATTERY_ICON '\U1F50B' # 🔋
|
||||
DISK_ICON '\uF0A0 ' #
|
||||
OK_ICON '\u2714' # ✔
|
||||
FAIL_ICON '\u2718' # ✘
|
||||
SYMFONY_ICON 'SF'
|
||||
NODE_ICON '\u2B22' # ⬢
|
||||
NODEJS_ICON '\u2B22' # ⬢
|
||||
MULTILINE_FIRST_PROMPT_PREFIX '\u256D\U2500' # ╭─
|
||||
MULTILINE_NEWLINE_PROMPT_PREFIX '\u251C\U2500' # ├─
|
||||
MULTILINE_LAST_PROMPT_PREFIX '\u2570\U2500 ' # ╰─
|
||||
APPLE_ICON '\uF179'$s #
|
||||
WINDOWS_ICON '\uF17A'$s #
|
||||
FREEBSD_ICON '\U1F608'$q # 😈
|
||||
ANDROID_ICON '\uE17B'$s # (doesn't always work)
|
||||
LINUX_ICON '\uF17C'$s #
|
||||
LINUX_ARCH_ICON '\uF17C'$s #
|
||||
LINUX_DEBIAN_ICON '\uF17C'$s #
|
||||
LINUX_RASPBIAN_ICON '\uF17C'$s #
|
||||
LINUX_UBUNTU_ICON '\uF17C'$s #
|
||||
LINUX_CENTOS_ICON '\uF17C'$s #
|
||||
LINUX_COREOS_ICON '\uF17C'$s #
|
||||
LINUX_ELEMENTARY_ICON '\uF17C'$s #
|
||||
LINUX_MINT_ICON '\uF17C'$s #
|
||||
LINUX_FEDORA_ICON '\uF17C'$s #
|
||||
LINUX_GENTOO_ICON '\uF17C'$s #
|
||||
LINUX_MAGEIA_ICON '\uF17C'$s #
|
||||
LINUX_NIXOS_ICON '\uF17C'$s #
|
||||
LINUX_MANJARO_ICON '\uF17C'$s #
|
||||
LINUX_DEVUAN_ICON '\uF17C'$s #
|
||||
LINUX_ALPINE_ICON '\uF17C'$s #
|
||||
LINUX_AOSC_ICON '\uF17C'$s #
|
||||
LINUX_OPENSUSE_ICON '\uF17C'$s #
|
||||
LINUX_SABAYON_ICON '\uF17C'$s #
|
||||
LINUX_SLACKWARE_ICON '\uF17C'$s #
|
||||
LINUX_VOID_ICON '\uF17C'$s #
|
||||
LINUX_ARTIX_ICON '\uF17C'$s #
|
||||
LINUX_RHEL_ICON '\uF17C'$s #
|
||||
SUNOS_ICON '\uF185 ' #
|
||||
HOME_ICON '\uF015'$s #
|
||||
HOME_SUB_ICON '\uF07C'$s #
|
||||
FOLDER_ICON '\uF115'$s #
|
||||
ETC_ICON '\uF013 ' #
|
||||
NETWORK_ICON '\uF09E'$s #
|
||||
LOAD_ICON '\uF080 ' #
|
||||
SWAP_ICON '\uF0E4'$s #
|
||||
RAM_ICON '\uF0E4'$s #
|
||||
SERVER_ICON '\uF233'$s #
|
||||
VCS_UNTRACKED_ICON '\uF059'$s #
|
||||
VCS_UNSTAGED_ICON '\uF06A'$s #
|
||||
VCS_STAGED_ICON '\uF055'$s #
|
||||
VCS_STASH_ICON '\uF01C ' #
|
||||
VCS_INCOMING_CHANGES_ICON '\uF01A ' #
|
||||
VCS_OUTGOING_CHANGES_ICON '\uF01B ' #
|
||||
VCS_TAG_ICON '\uF217 ' #
|
||||
VCS_BOOKMARK_ICON '\uF27B ' #
|
||||
VCS_COMMIT_ICON '\uF221 ' #
|
||||
VCS_BRANCH_ICON '\uF126 ' #
|
||||
VCS_REMOTE_BRANCH_ICON '\u2192' # →
|
||||
VCS_LOADING_ICON ''
|
||||
VCS_GIT_ICON '\uF1D3 ' #
|
||||
VCS_GIT_GITHUB_ICON '\uF113 ' #
|
||||
VCS_GIT_BITBUCKET_ICON '\uF171 ' #
|
||||
VCS_GIT_GITLAB_ICON '\uF296 ' #
|
||||
VCS_HG_ICON '\uF0C3 ' #
|
||||
VCS_SVN_ICON 'svn'$q
|
||||
RUST_ICON '\uE6A8' #
|
||||
PYTHON_ICON '\uE63C'$s #
|
||||
SWIFT_ICON 'Swift'
|
||||
GO_ICON 'Go'
|
||||
GOLANG_ICON 'Go'
|
||||
PUBLIC_IP_ICON 'IP'
|
||||
LOCK_ICON '\UF023' #
|
||||
NORDVPN_ICON '\UF023' #
|
||||
EXECUTION_TIME_ICON '\uF253'$s #
|
||||
SSH_ICON 'ssh'
|
||||
VPN_ICON '\uF023'
|
||||
KUBERNETES_ICON '\U2388' # ⎈
|
||||
DROPBOX_ICON '\UF16B'$s #
|
||||
DATE_ICON '\uF073 ' #
|
||||
TIME_ICON '\uF017 ' #
|
||||
JAVA_ICON '\U2615' # ☕︎
|
||||
LARAVEL_ICON ''
|
||||
RANGER_ICON '\u2B50' # ⭐
|
||||
MIDNIGHT_COMMANDER_ICON 'mc'
|
||||
VIM_ICON 'vim'
|
||||
TERRAFORM_ICON 'tf'
|
||||
PROXY_ICON '\u2194' # ↔
|
||||
DOTNET_ICON '.NET'
|
||||
DOTNET_CORE_ICON '.NET'
|
||||
AZURE_ICON '\u2601' # ☁
|
||||
DIRENV_ICON '\u25BC' # ▼
|
||||
FLUTTER_ICON 'F'
|
||||
GCLOUD_ICON 'G'
|
||||
LUA_ICON 'lua'
|
||||
PERL_ICON 'perl'
|
||||
NNN_ICON 'nnn'
|
||||
XPLR_ICON 'xplr'
|
||||
TIMEWARRIOR_ICON 'tw'
|
||||
TASKWARRIOR_ICON 'task'
|
||||
NIX_SHELL_ICON 'nix'
|
||||
WIFI_ICON 'WiFi'
|
||||
ERLANG_ICON 'erl'
|
||||
ELIXIR_ICON 'elixir'
|
||||
POSTGRES_ICON 'postgres'
|
||||
PHP_ICON 'php'
|
||||
HASKELL_ICON 'hs'
|
||||
PACKAGE_ICON 'pkg'
|
||||
JULIA_ICON 'jl'
|
||||
SCALA_ICON 'scala'
|
||||
TOOLBOX_ICON '\u2B22' # ⬢
|
||||
)
|
||||
;;
|
||||
'awesome-mapped-fontconfig')
|
||||
# mapped fontconfig with awesome-font required! See
|
||||
# https://github.com/gabrielelana/awesome-terminal-fonts
|
||||
# don't forget to source the font maps in your startup script
|
||||
if [ -z "$AWESOME_GLYPHS_LOADED" ]; then
|
||||
echo "Powerlevel9k warning: Awesome-Font mappings have not been loaded.
|
||||
Source a font mapping in your shell config, per the Awesome-Font docs
|
||||
(https://github.com/gabrielelana/awesome-terminal-fonts),
|
||||
Or use a different Powerlevel9k font configuration.";
|
||||
fi
|
||||
icons=(
|
||||
RULER_CHAR '\u2500' # ─
|
||||
LEFT_SEGMENT_SEPARATOR '\uE0B0' #
|
||||
RIGHT_SEGMENT_SEPARATOR '\uE0B2' #
|
||||
LEFT_SEGMENT_END_SEPARATOR ' ' # Whitespace
|
||||
LEFT_SUBSEGMENT_SEPARATOR '\uE0B1' #
|
||||
RIGHT_SUBSEGMENT_SEPARATOR '\uE0B3' #
|
||||
CARRIAGE_RETURN_ICON '\u21B5' # ↵
|
||||
ROOT_ICON "${CODEPOINT_OF_OCTICONS_ZAP:+\\u$CODEPOINT_OF_OCTICONS_ZAP}"
|
||||
SUDO_ICON "${CODEPOINT_OF_AWESOME_UNLOCK:+\\u$CODEPOINT_OF_AWESOME_UNLOCK$s}"
|
||||
RUBY_ICON "${CODEPOINT_OF_OCTICONS_RUBY:+\\u$CODEPOINT_OF_OCTICONS_RUBY }"
|
||||
AWS_ICON "${CODEPOINT_OF_AWESOME_SERVER:+\\u$CODEPOINT_OF_AWESOME_SERVER$s}"
|
||||
AWS_EB_ICON '\U1F331'$q # 🌱
|
||||
BACKGROUND_JOBS_ICON "${CODEPOINT_OF_AWESOME_COG:+\\u$CODEPOINT_OF_AWESOME_COG }"
|
||||
TEST_ICON "${CODEPOINT_OF_AWESOME_BUG:+\\u$CODEPOINT_OF_AWESOME_BUG$s}"
|
||||
TODO_ICON "${CODEPOINT_OF_AWESOME_CHECK_SQUARE_O:+\\u$CODEPOINT_OF_AWESOME_CHECK_SQUARE_O$s}"
|
||||
BATTERY_ICON "${CODEPOINT_OF_AWESOME_BATTERY_FULL:+\\U$CODEPOINT_OF_AWESOME_BATTERY_FULL$s}"
|
||||
DISK_ICON "${CODEPOINT_OF_AWESOME_HDD_O:+\\u$CODEPOINT_OF_AWESOME_HDD_O }"
|
||||
OK_ICON "${CODEPOINT_OF_AWESOME_CHECK:+\\u$CODEPOINT_OF_AWESOME_CHECK$s}"
|
||||
FAIL_ICON "${CODEPOINT_OF_AWESOME_TIMES:+\\u$CODEPOINT_OF_AWESOME_TIMES}"
|
||||
SYMFONY_ICON 'SF'
|
||||
NODE_ICON '\u2B22' # ⬢
|
||||
NODEJS_ICON '\u2B22' # ⬢
|
||||
MULTILINE_FIRST_PROMPT_PREFIX '\u256D\U2500' # ╭─
|
||||
MULTILINE_NEWLINE_PROMPT_PREFIX '\u251C\U2500' # ├─
|
||||
MULTILINE_LAST_PROMPT_PREFIX '\u2570\U2500 ' # ╰─
|
||||
APPLE_ICON "${CODEPOINT_OF_AWESOME_APPLE:+\\u$CODEPOINT_OF_AWESOME_APPLE$s}"
|
||||
FREEBSD_ICON '\U1F608'$q # 😈
|
||||
LINUX_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_ARCH_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_DEBIAN_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_RASPBIAN_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_UBUNTU_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_CENTOS_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_COREOS_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_ELEMENTARY_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_MINT_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_FEDORA_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_GENTOO_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_MAGEIA_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_NIXOS_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_MANJARO_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_DEVUAN_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_ALPINE_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_AOSC_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_OPENSUSE_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_SABAYON_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_SLACKWARE_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_VOID_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_ARTIX_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
LINUX_RHEL_ICON "${CODEPOINT_OF_AWESOME_LINUX:+\\u$CODEPOINT_OF_AWESOME_LINUX$s}"
|
||||
SUNOS_ICON "${CODEPOINT_OF_AWESOME_SUN_O:+\\u$CODEPOINT_OF_AWESOME_SUN_O }"
|
||||
HOME_ICON "${CODEPOINT_OF_AWESOME_HOME:+\\u$CODEPOINT_OF_AWESOME_HOME$s}"
|
||||
HOME_SUB_ICON "${CODEPOINT_OF_AWESOME_FOLDER_OPEN:+\\u$CODEPOINT_OF_AWESOME_FOLDER_OPEN$s}"
|
||||
FOLDER_ICON "${CODEPOINT_OF_AWESOME_FOLDER_O:+\\u$CODEPOINT_OF_AWESOME_FOLDER_O$s}"
|
||||
ETC_ICON "${CODEPOINT_OF_AWESOME_COG:+\\u$CODEPOINT_OF_AWESOME_COG }"
|
||||
NETWORK_ICON "${CODEPOINT_OF_AWESOME_RSS:+\\u$CODEPOINT_OF_AWESOME_RSS$s}"
|
||||
LOAD_ICON "${CODEPOINT_OF_AWESOME_BAR_CHART:+\\u$CODEPOINT_OF_AWESOME_BAR_CHART }"
|
||||
SWAP_ICON "${CODEPOINT_OF_AWESOME_DASHBOARD:+\\u$CODEPOINT_OF_AWESOME_DASHBOARD$s}"
|
||||
RAM_ICON "${CODEPOINT_OF_AWESOME_DASHBOARD:+\\u$CODEPOINT_OF_AWESOME_DASHBOARD$s}"
|
||||
SERVER_ICON "${CODEPOINT_OF_AWESOME_SERVER:+\\u$CODEPOINT_OF_AWESOME_SERVER$s}"
|
||||
VCS_UNTRACKED_ICON "${CODEPOINT_OF_AWESOME_QUESTION_CIRCLE:+\\u$CODEPOINT_OF_AWESOME_QUESTION_CIRCLE$s}"
|
||||
VCS_UNSTAGED_ICON "${CODEPOINT_OF_AWESOME_EXCLAMATION_CIRCLE:+\\u$CODEPOINT_OF_AWESOME_EXCLAMATION_CIRCLE$s}"
|
||||
VCS_STAGED_ICON "${CODEPOINT_OF_AWESOME_PLUS_CIRCLE:+\\u$CODEPOINT_OF_AWESOME_PLUS_CIRCLE$s}"
|
||||
VCS_STASH_ICON "${CODEPOINT_OF_AWESOME_INBOX:+\\u$CODEPOINT_OF_AWESOME_INBOX }"
|
||||
VCS_INCOMING_CHANGES_ICON "${CODEPOINT_OF_AWESOME_ARROW_CIRCLE_DOWN:+\\u$CODEPOINT_OF_AWESOME_ARROW_CIRCLE_DOWN }"
|
||||
VCS_OUTGOING_CHANGES_ICON "${CODEPOINT_OF_AWESOME_ARROW_CIRCLE_UP:+\\u$CODEPOINT_OF_AWESOME_ARROW_CIRCLE_UP }"
|
||||
VCS_TAG_ICON "${CODEPOINT_OF_AWESOME_TAG:+\\u$CODEPOINT_OF_AWESOME_TAG }"
|
||||
VCS_BOOKMARK_ICON "${CODEPOINT_OF_OCTICONS_BOOKMARK:+\\u$CODEPOINT_OF_OCTICONS_BOOKMARK}"
|
||||
VCS_COMMIT_ICON "${CODEPOINT_OF_OCTICONS_GIT_COMMIT:+\\u$CODEPOINT_OF_OCTICONS_GIT_COMMIT }"
|
||||
VCS_BRANCH_ICON "${CODEPOINT_OF_OCTICONS_GIT_BRANCH:+\\u$CODEPOINT_OF_OCTICONS_GIT_BRANCH }"
|
||||
VCS_REMOTE_BRANCH_ICON "${CODEPOINT_OF_OCTICONS_REPO_PUSH:+\\u$CODEPOINT_OF_OCTICONS_REPO_PUSH$s}"
|
||||
VCS_LOADING_ICON ''
|
||||
VCS_GIT_ICON "${CODEPOINT_OF_AWESOME_GIT:+\\u$CODEPOINT_OF_AWESOME_GIT }"
|
||||
VCS_GIT_GITHUB_ICON "${CODEPOINT_OF_AWESOME_GITHUB_ALT:+\\u$CODEPOINT_OF_AWESOME_GITHUB_ALT }"
|
||||
VCS_GIT_BITBUCKET_ICON "${CODEPOINT_OF_AWESOME_BITBUCKET:+\\u$CODEPOINT_OF_AWESOME_BITBUCKET }"
|
||||
VCS_GIT_GITLAB_ICON "${CODEPOINT_OF_AWESOME_GITLAB:+\\u$CODEPOINT_OF_AWESOME_GITLAB }"
|
||||
VCS_HG_ICON "${CODEPOINT_OF_AWESOME_FLASK:+\\u$CODEPOINT_OF_AWESOME_FLASK }"
|
||||
VCS_SVN_ICON 'svn'$q
|
||||
RUST_ICON '\uE6A8' #
|
||||
PYTHON_ICON '\U1F40D' # 🐍
|
||||
SWIFT_ICON '\uE655'$s #
|
||||
PUBLIC_IP_ICON "${CODEPOINT_OF_AWESOME_GLOBE:+\\u$CODEPOINT_OF_AWESOME_GLOBE$s}"
|
||||
LOCK_ICON "${CODEPOINT_OF_AWESOME_LOCK:+\\u$CODEPOINT_OF_AWESOME_LOCK}"
|
||||
NORDVPN_ICON "${CODEPOINT_OF_AWESOME_LOCK:+\\u$CODEPOINT_OF_AWESOME_LOCK}"
|
||||
EXECUTION_TIME_ICON "${CODEPOINT_OF_AWESOME_HOURGLASS_END:+\\u$CODEPOINT_OF_AWESOME_HOURGLASS_END$s}"
|
||||
SSH_ICON 'ssh'
|
||||
VPN_ICON "${CODEPOINT_OF_AWESOME_LOCK:+\\u$CODEPOINT_OF_AWESOME_LOCK}"
|
||||
KUBERNETES_ICON '\U2388' # ⎈
|
||||
DROPBOX_ICON "${CODEPOINT_OF_AWESOME_DROPBOX:+\\u$CODEPOINT_OF_AWESOME_DROPBOX$s}"
|
||||
DATE_ICON '\uF073 ' #
|
||||
TIME_ICON '\uF017 ' #
|
||||
JAVA_ICON '\U2615' # ☕︎
|
||||
LARAVEL_ICON ''
|
||||
RANGER_ICON '\u2B50' # ⭐
|
||||
MIDNIGHT_COMMANDER_ICON 'mc'
|
||||
VIM_ICON 'vim'
|
||||
TERRAFORM_ICON 'tf'
|
||||
PROXY_ICON '\u2194' # ↔
|
||||
DOTNET_ICON '.NET'
|
||||
DOTNET_CORE_ICON '.NET'
|
||||
AZURE_ICON '\u2601' # ☁
|
||||
DIRENV_ICON '\u25BC' # ▼
|
||||
FLUTTER_ICON 'F'
|
||||
GCLOUD_ICON 'G'
|
||||
LUA_ICON 'lua'
|
||||
PERL_ICON 'perl'
|
||||
NNN_ICON 'nnn'
|
||||
XPLR_ICON 'xplr'
|
||||
TIMEWARRIOR_ICON 'tw'
|
||||
TASKWARRIOR_ICON 'task'
|
||||
NIX_SHELL_ICON 'nix'
|
||||
WIFI_ICON 'WiFi'
|
||||
ERLANG_ICON 'erl'
|
||||
ELIXIR_ICON 'elixir'
|
||||
POSTGRES_ICON 'postgres'
|
||||
PHP_ICON 'php'
|
||||
HASKELL_ICON 'hs'
|
||||
PACKAGE_ICON 'pkg'
|
||||
JULIA_ICON 'jl'
|
||||
SCALA_ICON 'scala'
|
||||
TOOLBOX_ICON '\u2B22' # ⬢
|
||||
)
|
||||
;;
|
||||
'nerdfont-complete'|'nerdfont-fontconfig')
|
||||
# nerd-font patched (complete) font required! See
|
||||
# https://github.com/ryanoasis/nerd-fonts
|
||||
# http://nerdfonts.com/#cheat-sheet
|
||||
icons=(
|
||||
RULER_CHAR '\u2500' # ─
|
||||
LEFT_SEGMENT_SEPARATOR '\uE0B0' #
|
||||
RIGHT_SEGMENT_SEPARATOR '\uE0B2' #
|
||||
LEFT_SEGMENT_END_SEPARATOR ' ' # Whitespace
|
||||
LEFT_SUBSEGMENT_SEPARATOR '\uE0B1' #
|
||||
RIGHT_SUBSEGMENT_SEPARATOR '\uE0B3' #
|
||||
CARRIAGE_RETURN_ICON '\u21B5' # ↵
|
||||
ROOT_ICON '\uE614'$q #
|
||||
SUDO_ICON '\uF09C'$s #
|
||||
RUBY_ICON '\uF219 ' #
|
||||
AWS_ICON '\uF270'$s #
|
||||
AWS_EB_ICON '\UF1BD'$q$q #
|
||||
BACKGROUND_JOBS_ICON '\uF013 ' #
|
||||
TEST_ICON '\uF188'$s #
|
||||
TODO_ICON '\u2611' # ☑
|
||||
BATTERY_ICON '\UF240 ' #
|
||||
DISK_ICON '\uF0A0'$s #
|
||||
OK_ICON '\uF00C'$s #
|
||||
FAIL_ICON '\uF00D' #
|
||||
SYMFONY_ICON '\uE757' #
|
||||
NODE_ICON '\uE617 ' #
|
||||
NODEJS_ICON '\uE617 ' #
|
||||
MULTILINE_FIRST_PROMPT_PREFIX '\u256D\U2500' # ╭─
|
||||
MULTILINE_NEWLINE_PROMPT_PREFIX '\u251C\U2500' # ├─
|
||||
MULTILINE_LAST_PROMPT_PREFIX '\u2570\U2500 ' # ╰─
|
||||
APPLE_ICON '\uF179' #
|
||||
WINDOWS_ICON '\uF17A'$s #
|
||||
FREEBSD_ICON '\UF30C ' #
|
||||
ANDROID_ICON '\uF17B' #
|
||||
LINUX_ARCH_ICON '\uF303' #
|
||||
LINUX_CENTOS_ICON '\uF304'$s #
|
||||
LINUX_COREOS_ICON '\uF305'$s #
|
||||
LINUX_DEBIAN_ICON '\uF306' #
|
||||
LINUX_RASPBIAN_ICON '\uF315' #
|
||||
LINUX_ELEMENTARY_ICON '\uF309'$s #
|
||||
LINUX_FEDORA_ICON '\uF30a'$s #
|
||||
LINUX_GENTOO_ICON '\uF30d'$s #
|
||||
LINUX_MAGEIA_ICON '\uF310' #
|
||||
LINUX_MINT_ICON '\uF30e'$s #
|
||||
LINUX_NIXOS_ICON '\uF313'$s #
|
||||
LINUX_MANJARO_ICON '\uF312'$s #
|
||||
LINUX_DEVUAN_ICON '\uF307'$s #
|
||||
LINUX_ALPINE_ICON '\uF300'$s #
|
||||
LINUX_AOSC_ICON '\uF301'$s #
|
||||
LINUX_OPENSUSE_ICON '\uF314'$s #
|
||||
LINUX_SABAYON_ICON '\uF317'$s #
|
||||
LINUX_SLACKWARE_ICON '\uF319'$s #
|
||||
LINUX_VOID_ICON '\uF17C' #
|
||||
LINUX_ARTIX_ICON '\uF17C' #
|
||||
LINUX_UBUNTU_ICON '\uF31b'$s #
|
||||
LINUX_RHEL_ICON '\uF316'$s #
|
||||
LINUX_ICON '\uF17C' #
|
||||
SUNOS_ICON '\uF185 ' #
|
||||
HOME_ICON '\uF015'$s #
|
||||
HOME_SUB_ICON '\uF07C'$s #
|
||||
FOLDER_ICON '\uF115'$s #
|
||||
ETC_ICON '\uF013'$s #
|
||||
NETWORK_ICON '\uF50D'$s #
|
||||
LOAD_ICON '\uF080 ' #
|
||||
SWAP_ICON '\uF464'$s #
|
||||
RAM_ICON '\uF0E4'$s #
|
||||
SERVER_ICON '\uF0AE'$s #
|
||||
VCS_UNTRACKED_ICON '\uF059'$s #
|
||||
VCS_UNSTAGED_ICON '\uF06A'$s #
|
||||
VCS_STAGED_ICON '\uF055'$s #
|
||||
VCS_STASH_ICON '\uF01C ' #
|
||||
VCS_INCOMING_CHANGES_ICON '\uF01A ' #
|
||||
VCS_OUTGOING_CHANGES_ICON '\uF01B ' #
|
||||
VCS_TAG_ICON '\uF02B ' #
|
||||
VCS_BOOKMARK_ICON '\uF461 ' #
|
||||
VCS_COMMIT_ICON '\uE729 ' #
|
||||
VCS_BRANCH_ICON '\uF126 ' #
|
||||
VCS_REMOTE_BRANCH_ICON '\uE728 ' #
|
||||
VCS_LOADING_ICON ''
|
||||
VCS_GIT_ICON '\uF1D3 ' #
|
||||
VCS_GIT_GITHUB_ICON '\uF113 ' #
|
||||
VCS_GIT_BITBUCKET_ICON '\uE703 ' #
|
||||
VCS_GIT_GITLAB_ICON '\uF296 ' #
|
||||
VCS_HG_ICON '\uF0C3 ' #
|
||||
VCS_SVN_ICON '\uE72D'$q #
|
||||
RUST_ICON '\uE7A8'$q #
|
||||
PYTHON_ICON '\UE73C ' #
|
||||
SWIFT_ICON '\uE755' #
|
||||
GO_ICON '\uE626' #
|
||||
GOLANG_ICON '\uE626' #
|
||||
PUBLIC_IP_ICON '\UF0AC'$s #
|
||||
LOCK_ICON '\UF023' #
|
||||
NORDVPN_ICON '\UF023' #
|
||||
EXECUTION_TIME_ICON '\uF252'$s #
|
||||
SSH_ICON '\uF489'$s #
|
||||
VPN_ICON '\UF023'
|
||||
KUBERNETES_ICON '\U2388' # ⎈
|
||||
DROPBOX_ICON '\UF16B'$s #
|
||||
DATE_ICON '\uF073 ' #
|
||||
TIME_ICON '\uF017 ' #
|
||||
JAVA_ICON '\uE738' #
|
||||
LARAVEL_ICON '\ue73f'$q #
|
||||
RANGER_ICON '\uF00b ' #
|
||||
MIDNIGHT_COMMANDER_ICON 'mc'
|
||||
VIM_ICON '\uE62B' #
|
||||
TERRAFORM_ICON '\uF1BB ' #
|
||||
PROXY_ICON '\u2194' # ↔
|
||||
DOTNET_ICON '\uE77F' #
|
||||
DOTNET_CORE_ICON '\uE77F' #
|
||||
AZURE_ICON '\uFD03' # ﴃ
|
||||
DIRENV_ICON '\u25BC' # ▼
|
||||
FLUTTER_ICON 'F'
|
||||
GCLOUD_ICON '\uF7B7' #
|
||||
LUA_ICON '\uE620' #
|
||||
PERL_ICON '\uE769' #
|
||||
NNN_ICON 'nnn'
|
||||
XPLR_ICON 'xplr'
|
||||
TIMEWARRIOR_ICON '\uF49B' #
|
||||
TASKWARRIOR_ICON '\uF4A0 ' #
|
||||
NIX_SHELL_ICON '\uF313 ' #
|
||||
WIFI_ICON '\uF1EB ' #
|
||||
ERLANG_ICON '\uE7B1 ' #
|
||||
ELIXIR_ICON '\uE62D' #
|
||||
POSTGRES_ICON '\uE76E' #
|
||||
PHP_ICON '\uE608' #
|
||||
HASKELL_ICON '\uE61F' #
|
||||
PACKAGE_ICON '\uF8D6' #
|
||||
JULIA_ICON '\uE624' #
|
||||
SCALA_ICON '\uE737' #
|
||||
TOOLBOX_ICON '\uE20F'$s #
|
||||
)
|
||||
;;
|
||||
ascii)
|
||||
icons=(
|
||||
RULER_CHAR '-'
|
||||
LEFT_SEGMENT_SEPARATOR ''
|
||||
RIGHT_SEGMENT_SEPARATOR ''
|
||||
LEFT_SEGMENT_END_SEPARATOR ' '
|
||||
LEFT_SUBSEGMENT_SEPARATOR '|'
|
||||
RIGHT_SUBSEGMENT_SEPARATOR '|'
|
||||
CARRIAGE_RETURN_ICON ''
|
||||
ROOT_ICON '#'
|
||||
SUDO_ICON ''
|
||||
RUBY_ICON 'rb'
|
||||
AWS_ICON 'aws'
|
||||
AWS_EB_ICON 'eb'
|
||||
BACKGROUND_JOBS_ICON '%%'
|
||||
TEST_ICON ''
|
||||
TODO_ICON 'todo'
|
||||
BATTERY_ICON 'battery'
|
||||
DISK_ICON 'disk'
|
||||
OK_ICON 'ok'
|
||||
FAIL_ICON 'err'
|
||||
SYMFONY_ICON 'symphony'
|
||||
NODE_ICON 'node'
|
||||
NODEJS_ICON 'node'
|
||||
MULTILINE_FIRST_PROMPT_PREFIX ''
|
||||
MULTILINE_NEWLINE_PROMPT_PREFIX ''
|
||||
MULTILINE_LAST_PROMPT_PREFIX ''
|
||||
APPLE_ICON 'mac'
|
||||
WINDOWS_ICON 'win'
|
||||
FREEBSD_ICON 'bsd'
|
||||
ANDROID_ICON 'android'
|
||||
LINUX_ICON 'linux'
|
||||
LINUX_ARCH_ICON 'arch'
|
||||
LINUX_DEBIAN_ICON 'debian'
|
||||
LINUX_RASPBIAN_ICON 'pi'
|
||||
LINUX_UBUNTU_ICON 'ubuntu'
|
||||
LINUX_CENTOS_ICON 'centos'
|
||||
LINUX_COREOS_ICON 'coreos'
|
||||
LINUX_ELEMENTARY_ICON 'elementary'
|
||||
LINUX_MINT_ICON 'mint'
|
||||
LINUX_FEDORA_ICON 'fedora'
|
||||
LINUX_GENTOO_ICON 'gentoo'
|
||||
LINUX_MAGEIA_ICON 'mageia'
|
||||
LINUX_NIXOS_ICON 'nixos'
|
||||
LINUX_MANJARO_ICON 'manjaro'
|
||||
LINUX_DEVUAN_ICON 'devuan'
|
||||
LINUX_ALPINE_ICON 'alpine'
|
||||
LINUX_AOSC_ICON 'aosc'
|
||||
LINUX_OPENSUSE_ICON 'suse'
|
||||
LINUX_SABAYON_ICON 'sabayon'
|
||||
LINUX_SLACKWARE_ICON 'slack'
|
||||
LINUX_VOID_ICON 'void'
|
||||
LINUX_ARTIX_ICON 'artix'
|
||||
LINUX_RHEL_ICON 'rhel'
|
||||
SUNOS_ICON 'sunos'
|
||||
HOME_ICON ''
|
||||
HOME_SUB_ICON ''
|
||||
FOLDER_ICON ''
|
||||
ETC_ICON ''
|
||||
NETWORK_ICON 'ip'
|
||||
LOAD_ICON 'cpu'
|
||||
SWAP_ICON 'swap'
|
||||
RAM_ICON 'ram'
|
||||
SERVER_ICON ''
|
||||
VCS_UNTRACKED_ICON '?'
|
||||
VCS_UNSTAGED_ICON '!'
|
||||
VCS_STAGED_ICON '+'
|
||||
VCS_STASH_ICON '#'
|
||||
VCS_INCOMING_CHANGES_ICON '<'
|
||||
VCS_OUTGOING_CHANGES_ICON '>'
|
||||
VCS_TAG_ICON ''
|
||||
VCS_BOOKMARK_ICON '^'
|
||||
VCS_COMMIT_ICON '@'
|
||||
VCS_BRANCH_ICON ''
|
||||
VCS_REMOTE_BRANCH_ICON ':'
|
||||
VCS_LOADING_ICON ''
|
||||
VCS_GIT_ICON ''
|
||||
VCS_GIT_GITHUB_ICON ''
|
||||
VCS_GIT_BITBUCKET_ICON ''
|
||||
VCS_GIT_GITLAB_ICON ''
|
||||
VCS_HG_ICON ''
|
||||
VCS_SVN_ICON ''
|
||||
RUST_ICON 'rust'
|
||||
PYTHON_ICON 'py'
|
||||
SWIFT_ICON 'swift'
|
||||
GO_ICON 'go'
|
||||
GOLANG_ICON 'go'
|
||||
PUBLIC_IP_ICON 'ip'
|
||||
LOCK_ICON '!w'
|
||||
NORDVPN_ICON 'nordvpn'
|
||||
EXECUTION_TIME_ICON ''
|
||||
SSH_ICON 'ssh'
|
||||
VPN_ICON 'vpn'
|
||||
KUBERNETES_ICON 'kube'
|
||||
DROPBOX_ICON 'dropbox'
|
||||
DATE_ICON ''
|
||||
TIME_ICON ''
|
||||
JAVA_ICON 'java'
|
||||
LARAVEL_ICON ''
|
||||
RANGER_ICON 'ranger'
|
||||
MIDNIGHT_COMMANDER_ICON 'mc'
|
||||
VIM_ICON 'vim'
|
||||
TERRAFORM_ICON 'tf'
|
||||
PROXY_ICON 'proxy'
|
||||
DOTNET_ICON '.net'
|
||||
DOTNET_CORE_ICON '.net'
|
||||
AZURE_ICON 'az'
|
||||
DIRENV_ICON 'direnv'
|
||||
FLUTTER_ICON 'flutter'
|
||||
GCLOUD_ICON 'gcloud'
|
||||
LUA_ICON 'lua'
|
||||
PERL_ICON 'perl'
|
||||
NNN_ICON 'nnn'
|
||||
XPLR_ICON 'xplr'
|
||||
TIMEWARRIOR_ICON 'tw'
|
||||
TASKWARRIOR_ICON 'task'
|
||||
NIX_SHELL_ICON 'nix'
|
||||
WIFI_ICON 'wifi'
|
||||
ERLANG_ICON 'erlang'
|
||||
ELIXIR_ICON 'elixir'
|
||||
POSTGRES_ICON 'postgres'
|
||||
PHP_ICON 'php'
|
||||
HASKELL_ICON 'hs'
|
||||
PACKAGE_ICON 'pkg'
|
||||
JULIA_ICON 'jl'
|
||||
SCALA_ICON 'scala'
|
||||
TOOLBOX_ICON 'toolbox'
|
||||
)
|
||||
;;
|
||||
*)
|
||||
# Powerline-Patched Font required!
|
||||
# See https://github.com/Lokaltog/powerline-fonts
|
||||
icons=(
|
||||
RULER_CHAR '\u2500' # ─
|
||||
LEFT_SEGMENT_SEPARATOR '\uE0B0' #
|
||||
RIGHT_SEGMENT_SEPARATOR '\uE0B2' #
|
||||
LEFT_SEGMENT_END_SEPARATOR ' ' # Whitespace
|
||||
LEFT_SUBSEGMENT_SEPARATOR '\uE0B1' #
|
||||
RIGHT_SUBSEGMENT_SEPARATOR '\uE0B3' #
|
||||
CARRIAGE_RETURN_ICON '\u21B5' # ↵
|
||||
ROOT_ICON '\u26A1' # ⚡
|
||||
SUDO_ICON ''
|
||||
RUBY_ICON 'Ruby'
|
||||
AWS_ICON 'AWS'
|
||||
AWS_EB_ICON '\U1F331'$q # 🌱
|
||||
BACKGROUND_JOBS_ICON '\u2699' # ⚙
|
||||
TEST_ICON ''
|
||||
TODO_ICON '\u2206' # ∆
|
||||
BATTERY_ICON '\U1F50B' # 🔋
|
||||
DISK_ICON 'hdd'
|
||||
OK_ICON '\u2714' # ✔
|
||||
FAIL_ICON '\u2718' # ✘
|
||||
SYMFONY_ICON 'SF'
|
||||
NODE_ICON 'Node'
|
||||
NODEJS_ICON 'Node'
|
||||
MULTILINE_FIRST_PROMPT_PREFIX '\u256D\U2500' # ╭─
|
||||
MULTILINE_NEWLINE_PROMPT_PREFIX '\u251C\U2500' # ├─
|
||||
MULTILINE_LAST_PROMPT_PREFIX '\u2570\U2500 ' # ╰─
|
||||
APPLE_ICON 'OSX'
|
||||
WINDOWS_ICON 'WIN'
|
||||
FREEBSD_ICON 'BSD'
|
||||
ANDROID_ICON 'And'
|
||||
LINUX_ICON 'Lx'
|
||||
LINUX_ARCH_ICON 'Arc'
|
||||
LINUX_DEBIAN_ICON 'Deb'
|
||||
LINUX_RASPBIAN_ICON 'RPi'
|
||||
LINUX_UBUNTU_ICON 'Ubu'
|
||||
LINUX_CENTOS_ICON 'Cen'
|
||||
LINUX_COREOS_ICON 'Cor'
|
||||
LINUX_ELEMENTARY_ICON 'Elm'
|
||||
LINUX_MINT_ICON 'LMi'
|
||||
LINUX_FEDORA_ICON 'Fed'
|
||||
LINUX_GENTOO_ICON 'Gen'
|
||||
LINUX_MAGEIA_ICON 'Mag'
|
||||
LINUX_NIXOS_ICON 'Nix'
|
||||
LINUX_MANJARO_ICON 'Man'
|
||||
LINUX_DEVUAN_ICON 'Dev'
|
||||
LINUX_ALPINE_ICON 'Alp'
|
||||
LINUX_AOSC_ICON 'Aos'
|
||||
LINUX_OPENSUSE_ICON 'OSu'
|
||||
LINUX_SABAYON_ICON 'Sab'
|
||||
LINUX_SLACKWARE_ICON 'Sla'
|
||||
LINUX_VOID_ICON 'Vo'
|
||||
LINUX_ARTIX_ICON 'Art'
|
||||
LINUX_RHEL_ICON 'RH'
|
||||
SUNOS_ICON 'Sun'
|
||||
HOME_ICON ''
|
||||
HOME_SUB_ICON ''
|
||||
FOLDER_ICON ''
|
||||
ETC_ICON '\u2699' # ⚙
|
||||
NETWORK_ICON 'IP'
|
||||
LOAD_ICON 'L'
|
||||
SWAP_ICON 'SWP'
|
||||
RAM_ICON 'RAM'
|
||||
SERVER_ICON ''
|
||||
VCS_UNTRACKED_ICON '?'
|
||||
VCS_UNSTAGED_ICON '\u25CF' # ●
|
||||
VCS_STAGED_ICON '\u271A' # ✚
|
||||
VCS_STASH_ICON '\u235F' # ⍟
|
||||
VCS_INCOMING_CHANGES_ICON '\u2193' # ↓
|
||||
VCS_OUTGOING_CHANGES_ICON '\u2191' # ↑
|
||||
VCS_TAG_ICON ''
|
||||
VCS_BOOKMARK_ICON '\u263F' # ☿
|
||||
VCS_COMMIT_ICON ''
|
||||
VCS_BRANCH_ICON '\uE0A0 ' #
|
||||
VCS_REMOTE_BRANCH_ICON '\u2192' # →
|
||||
VCS_LOADING_ICON ''
|
||||
VCS_GIT_ICON ''
|
||||
VCS_GIT_GITHUB_ICON ''
|
||||
VCS_GIT_BITBUCKET_ICON ''
|
||||
VCS_GIT_GITLAB_ICON ''
|
||||
VCS_HG_ICON ''
|
||||
VCS_SVN_ICON ''
|
||||
RUST_ICON 'R'
|
||||
PYTHON_ICON 'Py'
|
||||
SWIFT_ICON 'Swift'
|
||||
GO_ICON 'Go'
|
||||
GOLANG_ICON 'Go'
|
||||
PUBLIC_IP_ICON 'IP'
|
||||
LOCK_ICON '\UE0A2'
|
||||
NORDVPN_ICON '\UE0A2'
|
||||
EXECUTION_TIME_ICON ''
|
||||
SSH_ICON 'ssh'
|
||||
VPN_ICON 'vpn'
|
||||
KUBERNETES_ICON '\U2388' # ⎈
|
||||
DROPBOX_ICON 'Dropbox'
|
||||
DATE_ICON ''
|
||||
TIME_ICON ''
|
||||
JAVA_ICON '\U2615' # ☕︎
|
||||
LARAVEL_ICON ''
|
||||
RANGER_ICON '\u2B50' # ⭐
|
||||
MIDNIGHT_COMMANDER_ICON 'mc'
|
||||
VIM_ICON 'vim'
|
||||
TERRAFORM_ICON 'tf'
|
||||
PROXY_ICON '\u2194' # ↔
|
||||
DOTNET_ICON '.NET'
|
||||
DOTNET_CORE_ICON '.NET'
|
||||
AZURE_ICON '\u2601' # ☁
|
||||
DIRENV_ICON '\u25BC' # ▼
|
||||
FLUTTER_ICON 'F'
|
||||
GCLOUD_ICON 'G'
|
||||
LUA_ICON 'lua'
|
||||
PERL_ICON 'perl'
|
||||
NNN_ICON 'nnn'
|
||||
XPLR_ICON 'xplr'
|
||||
TIMEWARRIOR_ICON 'tw'
|
||||
TASKWARRIOR_ICON 'task'
|
||||
NIX_SHELL_ICON 'nix'
|
||||
WIFI_ICON 'WiFi'
|
||||
ERLANG_ICON 'erl'
|
||||
ELIXIR_ICON 'elixir'
|
||||
POSTGRES_ICON 'postgres'
|
||||
PHP_ICON 'php'
|
||||
HASKELL_ICON 'hs'
|
||||
PACKAGE_ICON 'pkg'
|
||||
JULIA_ICON 'jl'
|
||||
SCALA_ICON 'scala'
|
||||
TOOLBOX_ICON '\u2B22' # ⬢
|
||||
)
|
||||
;;
|
||||
esac
|
||||
|
||||
# Override the above icon settings with any user-defined variables.
|
||||
case $POWERLEVEL9K_MODE in
|
||||
'flat')
|
||||
icons[LEFT_SEGMENT_SEPARATOR]=''
|
||||
icons[RIGHT_SEGMENT_SEPARATOR]=''
|
||||
icons[LEFT_SUBSEGMENT_SEPARATOR]='|'
|
||||
icons[RIGHT_SUBSEGMENT_SEPARATOR]='|'
|
||||
;;
|
||||
'compatible')
|
||||
icons[LEFT_SEGMENT_SEPARATOR]='\u2B80' # ⮀
|
||||
icons[RIGHT_SEGMENT_SEPARATOR]='\u2B82' # ⮂
|
||||
icons[VCS_BRANCH_ICON]='@'
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $POWERLEVEL9K_ICON_PADDING == none && $POWERLEVEL9K_MODE != ascii ]]; then
|
||||
icons=("${(@kv)icons%% #}")
|
||||
icons[LEFT_SEGMENT_END_SEPARATOR]+=' '
|
||||
icons[MULTILINE_LAST_PROMPT_PREFIX]+=' '
|
||||
icons[VCS_TAG_ICON]+=' '
|
||||
icons[VCS_BOOKMARK_ICON]+=' '
|
||||
icons[VCS_COMMIT_ICON]+=' '
|
||||
icons[VCS_BRANCH_ICON]+=' '
|
||||
icons[VCS_REMOTE_BRANCH_ICON]+=' '
|
||||
fi
|
||||
}
|
||||
|
||||
# Sadly, this is a part of public API. Its use is emphatically discouraged.
|
||||
function print_icon() {
|
||||
eval "$__p9k_intro"
|
||||
_p9k_init_icons
|
||||
local var=POWERLEVEL9K_$1
|
||||
if (( $+parameters[$var] )); then
|
||||
echo -n - ${(P)var}
|
||||
else
|
||||
echo -n - $icons[$1]
|
||||
fi
|
||||
}
|
||||
|
||||
# Prints a list of configured icons.
|
||||
#
|
||||
# * $1 string - If "original", then the original icons are printed,
|
||||
# otherwise "print_icon" is used, which takes the users
|
||||
# overrides into account.
|
||||
function get_icon_names() {
|
||||
eval "$__p9k_intro"
|
||||
_p9k_init_icons
|
||||
local key
|
||||
for key in ${(@kon)icons}; do
|
||||
echo -n - "POWERLEVEL9K_$key: "
|
||||
print -nP "%K{red} %k"
|
||||
if [[ $1 == original ]]; then
|
||||
echo -n - $icons[$key]
|
||||
else
|
||||
print_icon $key
|
||||
fi
|
||||
print -P "%K{red} %k"
|
||||
done
|
||||
}
|
197
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/notes.md
Normal file
197
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/notes.md
Normal file
@ -0,0 +1,197 @@
|
||||
battery: use the same technique as in vpn_ip to avoid reset=2.
|
||||
|
||||
---
|
||||
|
||||
implement fake gitstatus api on top of vcs_info (or plain git?) + worker and use it if there is no
|
||||
gitstatus.
|
||||
|
||||
---
|
||||
|
||||
- call vcs_info on worker. the tricky question is what to display while "loading".
|
||||
|
||||
---
|
||||
|
||||
- add _SHOW_SYSTEM to all *env segments.
|
||||
|
||||
---
|
||||
|
||||
- support states in SHOW_ON_COMMAND: POWERLEVEL9K_SEGMENT_STATE_SHOW_ON_COMMAND='...'
|
||||
|
||||
---
|
||||
|
||||
add POWERLEVEL9K_${SEGMENT}_${STATE}_SHOW_IN_DIR='pwd_pattern'; implement the same way as
|
||||
SHOW_ON_UPGLOB. how should it interact with POWERLEVEL9K_${SEGMENT}_DISABLED_DIR_PATTERN?
|
||||
|
||||
---
|
||||
|
||||
add `p10k upglob`; returns 0 on match and sets REPLY to the directory where match was found.
|
||||
|
||||
---
|
||||
|
||||
when directory cannot be shortened any further, start chopping off segments from the left and
|
||||
replacing the chopped off part with `…`. e.g., `…/x/anchor/y/anchor`. the shortest dir
|
||||
representation is thus `…/last` or `…/last` depending on whether the last segment is an anchor.
|
||||
the replacement parameter's value is `…/` (with a slash) to allow for `x/anchor/y/anchor`.
|
||||
|
||||
---
|
||||
|
||||
- add to faq: how do i display an environment variable in prompt? link it from "extensible"
|
||||
|
||||
---
|
||||
|
||||
- add to faq: how do i display an icon in prompt? link it from "extensible"
|
||||
|
||||
---
|
||||
|
||||
- add root_indicator to config templates
|
||||
|
||||
---
|
||||
|
||||
- test chruby and add it to config templates
|
||||
|
||||
---
|
||||
|
||||
- add ssh to config templates
|
||||
|
||||
---
|
||||
|
||||
- add swift version to config templates; see if there is a good pattern for PROJECT_ONLY
|
||||
|
||||
---
|
||||
|
||||
- add swiftenv
|
||||
|
||||
---
|
||||
|
||||
- add faq: how to customize directory shortening? mention POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER,
|
||||
POWERLEVEL9K_DIR_MAX_LENGTH and co., and truncate_to_last.
|
||||
|
||||
---
|
||||
|
||||
fix a bug in zsh: https://github.com/romkatv/powerlevel10k/issues/502. to reproduce:
|
||||
|
||||
```zsh
|
||||
emulate zsh -o prompt_percent -c 'print -P "%F{#ff0000}red%F{green}%B bold green"'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
add `p10k explain` that prints something like this:
|
||||
|
||||
```text
|
||||
segment icons meaning
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
--
|
||||
status ✔ ✘ exit code of the last command
|
||||
```
|
||||
|
||||
implement it the hard way: for every enabled segment go over all its {state,icon} pairs, resolve
|
||||
the icon (if not absolute), apply VISUAL_IDENTIFIER_EXPANSION, remove leading and trailing
|
||||
whitespace and print without formatting (sort of like `print -P | cat`); print segment names in
|
||||
green and icons in bold; battery can have an unlimited number of icons, so `...` would be needed
|
||||
(based on total length of concatenated icons rather than the number of icons); user-defined
|
||||
segments would have "unknown" icons by default (yellow and not bold); can allow them to
|
||||
participate by defining `explainprompt_foo` that populates array `reply` with strings like this:
|
||||
'-s STATE -i LOCK_ICON +r'; the first element must be segment description.
|
||||
|
||||
---
|
||||
|
||||
add `docker_context` prompt segment; similar to `kubecontext`; the data should come from
|
||||
`currentContext` field in `~/.docker/config.json` (according to
|
||||
https://github.com/starship/starship/issues/995); there is also `DOCKER_CONTEXT`; more info:
|
||||
https://docs.docker.com/engine/reference/commandline/context_use; also
|
||||
https://github.com/starship/starship/pull/996.
|
||||
|
||||
---
|
||||
|
||||
support `env`, `ionice` and `strace` precommands in `parser.zsh`.
|
||||
|
||||
---
|
||||
|
||||
Add ruler to configuration wizard. Options: `─`, `·`, `╌`, `┄`, `▁`, `═`.
|
||||
|
||||
---
|
||||
|
||||
Add frame styles to the wizard.
|
||||
|
||||
```text
|
||||
╭─
|
||||
╰─
|
||||
|
||||
┌─
|
||||
└─
|
||||
|
||||
┏━
|
||||
┗━
|
||||
|
||||
╔═
|
||||
╚═
|
||||
|
||||
▛▀
|
||||
▙▄
|
||||
```
|
||||
|
||||
Prompt connection should have matching options.
|
||||
|
||||
---
|
||||
|
||||
Add `POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_MIRROR_SEPARATOR`. If set, left segments get separated with
|
||||
`POWERLEVEL9K_LEFT_SEGMENT_SEPARATOR` followed by `POWERLEVEL9K_LEFT_SEGMENT_MIRROR_SEPARATOR`.
|
||||
Each is drawn without background. The first with the foreground of left segment, the second with
|
||||
the background of right segment. To insert space in between, embed it in
|
||||
`POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_MIRROR_SEPARATOR`.
|
||||
`POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR` is unused.
|
||||
|
||||
---
|
||||
|
||||
Add *Segment Connection* screen to configuration wizard with options *Fused*, *Touching* and
|
||||
*Disjoint*. The last two differ by the absence/presence of space between `SEGMENT_SEPARATOR` and
|
||||
`SEGMENT_MIRROR_SEPARATOR`.
|
||||
|
||||
*Fused* requires line separator (there is already a screen for it) but the other two options require
|
||||
two filled separators similar to heads and tail. Figure out how to present this choice.
|
||||
|
||||
---
|
||||
|
||||
Optimize auto-wizard check.
|
||||
|
||||
```text
|
||||
time ( repeat 1000 [[ -z "${parameters[(I)POWERLEVEL9K_*~(POWERLEVEL9K_MODE|POWERLEVEL9K_CONFIG_FILE)]}" ]] )
|
||||
user=0.21s system=0.05s cpu=99% total=0.264
|
||||
|
||||
time ( repeat 1000 [[ -z "${parameters[(I)POWERLEVEL9K_*]}" ]] )
|
||||
user=0.17s system=0.00s cpu=99% total=0.175
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Add the equivalent of `P9K_PYTHON_VERSION` to all `*env` segments where it makes sense.
|
||||
|
||||
---
|
||||
|
||||
Define `P9K_ICON` on initialization. Fill it with `$icon`. Duplicate every key that ends in `_ICON`.
|
||||
Respect `POWERLEVEL9K_VCS_STASH_ICON` overrides but not anything with segment name or state.
|
||||
|
||||
Define `POWERLEVEL9K_VCS_*` parameters in config templates for all symbols used in
|
||||
`my_git_formatter`. Add missing entries to `icons`. Use `$P9K_ICON[...]` within `my_git_formatter`.
|
||||
Add a screen to the wizard to choose between clear and circled icons.
|
||||
|
||||
---
|
||||
|
||||
Add a screen to the wizard asking whether to set `POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~'`.
|
||||
Show it only if there is `$HOME/.git`. By default this parameter should be commented out.
|
9130
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/p10k.zsh
Normal file
9130
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/p10k.zsh
Normal file
File diff suppressed because it is too large
Load Diff
382
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/parser.zsh
Normal file
382
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/parser.zsh
Normal file
@ -0,0 +1,382 @@
|
||||
typeset -grA __p9k_pb_cmd_skip=(
|
||||
'}' 'always' # handled specially
|
||||
'{' ''
|
||||
'{' ''
|
||||
'|' ''
|
||||
'||' ''
|
||||
'&' ''
|
||||
'&&' ''
|
||||
'|&' ''
|
||||
'&!' ''
|
||||
'&|' ''
|
||||
')' ''
|
||||
'(' ''
|
||||
'()' ''
|
||||
'!' ''
|
||||
';' ''
|
||||
'if' ''
|
||||
'fi' ''
|
||||
'elif' ''
|
||||
'else' ''
|
||||
'then' ''
|
||||
'while' ''
|
||||
'until' ''
|
||||
'do' ''
|
||||
'done' ''
|
||||
'esac' ''
|
||||
'end' ''
|
||||
'coproc' ''
|
||||
'nocorrect' ''
|
||||
'noglob' ''
|
||||
'time' ''
|
||||
'[[' '\]\]'
|
||||
'((' '\)\)'
|
||||
'case' '\)|esac'
|
||||
';;' '\)|esac'
|
||||
';&' '\)|esac'
|
||||
';|' '\)|esac'
|
||||
'foreach' '\(*\)'
|
||||
)
|
||||
|
||||
typeset -grA __p9k_pb_precommand=(
|
||||
'-' ''
|
||||
'builtin' ''
|
||||
'command' ''
|
||||
'exec' '-[^a]#[a]'
|
||||
'nohup' ''
|
||||
'setsid' ''
|
||||
'eatmydata' ''
|
||||
'catchsegv' ''
|
||||
'pkexec' '--user'
|
||||
'doas' '-[^aCu]#[acU]'
|
||||
'nice' '-[^n]#[n]|--adjustment'
|
||||
'stdbuf' '-[^ioe]#[ioe]|--(input|output|error)'
|
||||
'sudo' '-[^aghpuUCcrtT]#[aghpuUCcrtT]|--(close-from|group|host|prompt|role|type|other-user|command-timeout|user)'
|
||||
'ssh-agent' '-[^aEPt]#[aEPt]'
|
||||
'tabbed' '-[^gnprtTuU]#[gnprtTuU]'
|
||||
'chronic' ''
|
||||
'ifne' ''
|
||||
)
|
||||
|
||||
typeset -grA __p9k_pb_redirect=(
|
||||
'&>' ''
|
||||
'>' ''
|
||||
'>&' ''
|
||||
'<' ''
|
||||
'<&' ''
|
||||
'<>' ''
|
||||
'&>|' ''
|
||||
'>|' ''
|
||||
'&>>' ''
|
||||
'>>' ''
|
||||
'>>&' ''
|
||||
'&>>|' ''
|
||||
'>>|' ''
|
||||
'<<<' ''
|
||||
)
|
||||
|
||||
typeset -grA __p9k_pb_term=(
|
||||
'|' ''
|
||||
'||' ''
|
||||
';' ''
|
||||
'&' ''
|
||||
'&&' ''
|
||||
'|&' ''
|
||||
'&!' ''
|
||||
'&|' ''
|
||||
';;' ''
|
||||
';&' ''
|
||||
';|' ''
|
||||
'(' ''
|
||||
')' ''
|
||||
'()' '' # handled specially
|
||||
'}' '' # handled specially
|
||||
)
|
||||
|
||||
typeset -grA __p9k_pb_term_skip=(
|
||||
'(' '\)'
|
||||
';;' '\)|esac'
|
||||
';&' '\)|esac'
|
||||
';|' '\)|esac'
|
||||
)
|
||||
|
||||
# Usage: _p9k_parse_buffer <buffer> [token-limit]
|
||||
#
|
||||
# Parses the specified command line buffer and pupulates array P9K_COMMANDS
|
||||
# with commands from it. Terminates early and returns 1 if there are more
|
||||
# tokens than the specified limit.
|
||||
#
|
||||
# Broken:
|
||||
#
|
||||
# ---------------
|
||||
# : $(x)
|
||||
# ---------------
|
||||
# : `x`
|
||||
# ---------------
|
||||
# ${x/}
|
||||
# ---------------
|
||||
# - -- x
|
||||
# ---------------
|
||||
# command -p -p x
|
||||
# ---------------
|
||||
# *
|
||||
# ---------------
|
||||
# x=$y; $x
|
||||
# ---------------
|
||||
# alias x=y; y
|
||||
# ---------------
|
||||
# x <<END
|
||||
# ; END
|
||||
# END
|
||||
# ---------------
|
||||
# Setup:
|
||||
# setopt interactive_comments
|
||||
# alias x='#'
|
||||
# Punchline:
|
||||
# x; y
|
||||
# ---------------
|
||||
#
|
||||
# More brokenness with non-standard options (ignore_braces, ignore_close_braces, etc.).
|
||||
function _p9k_parse_buffer() {
|
||||
[[ ${2:-0} == <-> ]] || return 2
|
||||
|
||||
local rcquotes
|
||||
[[ -o rcquotes ]] && rcquotes=rcquotes
|
||||
|
||||
eval "$__p9k_intro"
|
||||
setopt no_nomatch $rcquotes
|
||||
|
||||
typeset -ga P9K_COMMANDS=()
|
||||
|
||||
local -r id='(<->|[[:alpha:]_][[:IDENT:]]#)'
|
||||
local -r var="\$$id|\${$id}|\"\$$id\"|\"\${$id}\""
|
||||
|
||||
local -i e ic c=${2:-'1 << 62'}
|
||||
local skip n s r state token cmd prev
|
||||
local -a aln alp alf v
|
||||
|
||||
if [[ -o interactive_comments ]]; then
|
||||
ic=1
|
||||
local tokens=(${(Z+C+)1})
|
||||
else
|
||||
local tokens=(${(z)1})
|
||||
fi
|
||||
|
||||
{
|
||||
while (( $#tokens )); do
|
||||
(( e = $#state ))
|
||||
|
||||
while (( $#tokens == alp[-1] )); do
|
||||
aln[-1]=()
|
||||
alp[-1]=()
|
||||
if (( $#tokens == alf[-1] )); then
|
||||
alf[-1]=()
|
||||
(( e = 0 ))
|
||||
fi
|
||||
done
|
||||
|
||||
while (( c-- > 0 )) || return; do
|
||||
token=$tokens[1]
|
||||
tokens[1]=()
|
||||
if (( $+galiases[$token] )); then
|
||||
(( $aln[(eI)p$token] )) && break
|
||||
s=$galiases[$token]
|
||||
n=p$token
|
||||
elif (( e )); then
|
||||
break
|
||||
elif (( $+aliases[$token] )); then
|
||||
(( $aln[(eI)p$token] )) && break
|
||||
s=$aliases[$token]
|
||||
n=p$token
|
||||
elif [[ $token == ?*.?* ]] && (( $+saliases[${token##*.}] )); then
|
||||
r=${token##*.}
|
||||
(( $aln[(eI)s$r] )) && break
|
||||
s=${saliases[$r]%% #}
|
||||
n=s$r
|
||||
else
|
||||
break
|
||||
fi
|
||||
aln+=$n
|
||||
alp+=$#tokens
|
||||
[[ $s == *' ' ]] && alf+=$#tokens
|
||||
(( ic )) && tokens[1,0]=(${(Z+C+)s}) || tokens[1,0]=(${(z)s})
|
||||
done
|
||||
|
||||
case $token in
|
||||
'<<'(|-))
|
||||
state=h
|
||||
continue
|
||||
;;
|
||||
*('`'|['<>=$']'(')*)
|
||||
if [[ $token == ('`'[^'`']##'`'|'"`'[^'`']##'`"'|'$('[^')']##')'|'"$('[^')']##')"'|['<>=']'('[^')']##')') ]]; then
|
||||
s=${${token##('"'|)(['$<>']|)?}%%?('"'|)}
|
||||
(( ic )) && tokens+=(';' ${(Z+C+)s}) || tokens+=(';' ${(z)s})
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
case $state in
|
||||
*r)
|
||||
state[-1]=
|
||||
continue
|
||||
;;
|
||||
a)
|
||||
if [[ $token == $skip ]]; then
|
||||
if [[ $token == '{' ]]; then
|
||||
P9K_COMMANDS+=$cmd
|
||||
cmd=
|
||||
state=
|
||||
else
|
||||
skip='{'
|
||||
fi
|
||||
continue
|
||||
else
|
||||
state=t
|
||||
fi
|
||||
;& # fall through
|
||||
t|p*)
|
||||
if (( $+__p9k_pb_term[$token] )); then
|
||||
if [[ $token == '()' ]]; then
|
||||
state=
|
||||
else
|
||||
P9K_COMMANDS+=$cmd
|
||||
if [[ $token == '}' ]]; then
|
||||
state=a
|
||||
skip=always
|
||||
else
|
||||
skip=$__p9k_pb_term_skip[$token]
|
||||
state=${skip:+s}
|
||||
fi
|
||||
fi
|
||||
cmd=
|
||||
continue
|
||||
elif [[ $state == t ]]; then
|
||||
continue
|
||||
elif [[ $state == *x ]]; then
|
||||
if (( $+__p9k_pb_redirect[$token] )); then
|
||||
prev=
|
||||
state[-1]=r
|
||||
continue
|
||||
else
|
||||
state[-1]=
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
s)
|
||||
if [[ $token == $~skip ]]; then
|
||||
state=
|
||||
fi
|
||||
continue
|
||||
;;
|
||||
h)
|
||||
while (( $#tokens )); do
|
||||
(( e = ${tokens[(i)${(Q)token}]} ))
|
||||
if [[ $tokens[e-1] == ';' && $tokens[e+1] == ';' ]]; then
|
||||
tokens[1,e]=()
|
||||
break
|
||||
else
|
||||
tokens[1,e]=()
|
||||
fi
|
||||
done
|
||||
while (( $#alp && alp[-1] >= $#tokens )); do
|
||||
aln[-1]=()
|
||||
alp[-1]=()
|
||||
done
|
||||
state=t
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
if (( $+__p9k_pb_redirect[${token#<0-255>}] )); then
|
||||
state+=r
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ $token == *'$'* ]]; then
|
||||
if [[ $token == $~var ]]; then
|
||||
n=${${token##[^[:IDENT:]]}%%[^[:IDENT:]]}
|
||||
[[ $token == *'"' ]] && v=("${(P)n}") || v=(${(P)n})
|
||||
tokens[1,0]=(${(@qq)v})
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
case $state in
|
||||
'')
|
||||
if (( $+__p9k_pb_cmd_skip[$token] )); then
|
||||
skip=$__p9k_pb_cmd_skip[$token]
|
||||
[[ $token == '}' ]] && state=a || state=${skip:+s}
|
||||
continue
|
||||
fi
|
||||
if [[ $token == *=* ]]; then
|
||||
v=${(S)token/#(<->|([[:alpha:]_][[:IDENT:]]#(|'['*[^\\](\\\\)#']')))(|'+')=}
|
||||
if (( $#v < $#token )); then
|
||||
if [[ $v == '(' ]]; then
|
||||
state=s
|
||||
skip='\)'
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
: ${token::=${(Q)${~token}}}
|
||||
;;
|
||||
p2)
|
||||
if [[ -n $prev ]]; then
|
||||
prev=
|
||||
else
|
||||
: ${token::=${(Q)${~token}}}
|
||||
if [[ $token == '{'$~id'}' ]]; then
|
||||
state=p2x
|
||||
prev=$token
|
||||
else
|
||||
state=p
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
;& # fall through
|
||||
p)
|
||||
if [[ -n $prev ]]; then
|
||||
token=$prev
|
||||
prev=
|
||||
else
|
||||
: ${token::=${(Q)${~token}}}
|
||||
case $token in
|
||||
'{'$~id'}') prev=$token; state=px; continue;;
|
||||
[^-]*) ;;
|
||||
--) state=p1; continue;;
|
||||
$~skip) state=p2; continue;;
|
||||
*) continue;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
p1)
|
||||
if [[ -n $prev ]]; then
|
||||
token=$prev
|
||||
prev=
|
||||
else
|
||||
: ${token::=${(Q)${~token}}}
|
||||
if [[ $token == '{'$~id'}' ]]; then
|
||||
state=p1x
|
||||
prev=$token
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if (( $+__p9k_pb_precommand[$token] )); then
|
||||
prev=
|
||||
state=p
|
||||
skip=$__p9k_pb_precommand[$token]
|
||||
cmd+=$token$'\0'
|
||||
else
|
||||
state=t
|
||||
[[ $token == ('(('*'))'|'`'*'`'|'$'*|['<>=']'('*')'|*$'\0'*) ]] || cmd+=$token$'\0'
|
||||
fi
|
||||
done
|
||||
} always {
|
||||
[[ $state == (px|p1x) ]] && cmd+=$prev
|
||||
P9K_COMMANDS+=$cmd
|
||||
P9K_COMMANDS=(${(u)P9K_COMMANDS%$'\0'})
|
||||
}
|
||||
}
|
2122
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/wizard.zsh
Normal file
2122
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/wizard.zsh
Normal file
File diff suppressed because it is too large
Load Diff
213
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/worker.zsh
Normal file
213
zsh/.oh-my-zsh/custom/themes/powerlevel10k/internal/worker.zsh
Normal file
@ -0,0 +1,213 @@
|
||||
# invoked in worker: _p9k_worker_main <pgid>
|
||||
function _p9k_worker_main() {
|
||||
mkfifo -- $_p9k__worker_file_prefix.fifo || return
|
||||
echo -nE - s$_p9k_worker_pgid$'\x1e' || return
|
||||
exec <$_p9k__worker_file_prefix.fifo || return
|
||||
zf_rm -- $_p9k__worker_file_prefix.fifo || return
|
||||
|
||||
local -i reset
|
||||
local req fd
|
||||
local -a ready
|
||||
local _p9k_worker_request_id
|
||||
local -A _p9k_worker_fds # fd => id$'\x1f'callback
|
||||
local -A _p9k_worker_inflight # id => inflight count
|
||||
|
||||
function _p9k_worker_reply() {
|
||||
print -nr -- e${(pj:\n:)@}$'\x1e' || kill -- -$_p9k_worker_pgid
|
||||
}
|
||||
|
||||
# usage: _p9k_worker_async <work> <callback>
|
||||
function _p9k_worker_async() {
|
||||
local fd async=$1
|
||||
sysopen -r -o cloexec -u fd <(() { eval $async; } && print -n '\x1e') || return
|
||||
(( ++_p9k_worker_inflight[$_p9k_worker_request_id] ))
|
||||
_p9k_worker_fds[$fd]=$_p9k_worker_request_id$'\x1f'$2
|
||||
}
|
||||
|
||||
trap '' PIPE
|
||||
|
||||
{
|
||||
while zselect -a ready 0 ${(k)_p9k_worker_fds}; do
|
||||
[[ $ready[1] == -r ]] || return
|
||||
for fd in ${ready:1}; do
|
||||
if [[ $fd == 0 ]]; then
|
||||
local buf=
|
||||
[[ -t 0 ]] # https://www.zsh.org/mla/workers/2020/msg00207.html
|
||||
if sysread -t 0 'buf[$#buf+1]'; then
|
||||
while [[ $buf != *$'\x1e' ]]; do
|
||||
sysread 'buf[$#buf+1]' || return
|
||||
done
|
||||
else
|
||||
(( $? == 4 )) || return
|
||||
fi
|
||||
for req in ${(ps:\x1e:)buf}; do
|
||||
_p9k_worker_request_id=${req%%$'\x1f'*}
|
||||
() { eval $req[$#_p9k_worker_request_id+2,-1] }
|
||||
(( $+_p9k_worker_inflight[$_p9k_worker_request_id] )) && continue
|
||||
print -rn -- d$_p9k_worker_request_id$'\x1e' || return
|
||||
done
|
||||
else
|
||||
local REPLY=
|
||||
while true; do
|
||||
if sysread -i $fd 'REPLY[$#REPLY+1]'; then
|
||||
[[ $REPLY == *$'\x1e' ]] || continue
|
||||
else
|
||||
(( $? == 5 )) || return
|
||||
break
|
||||
fi
|
||||
done
|
||||
local cb=$_p9k_worker_fds[$fd]
|
||||
_p9k_worker_request_id=${cb%%$'\x1f'*}
|
||||
unset "_p9k_worker_fds[$fd]"
|
||||
exec {fd}>&-
|
||||
if [[ $REPLY == *$'\x1e' ]]; then
|
||||
REPLY[-1]=""
|
||||
() { eval $cb[$#_p9k_worker_request_id+2,-1] }
|
||||
fi
|
||||
if (( --_p9k_worker_inflight[$_p9k_worker_request_id] == 0 )); then
|
||||
unset "_p9k_worker_inflight[$_p9k_worker_request_id]"
|
||||
print -rn -- d$_p9k_worker_request_id$'\x1e' || return
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
} always {
|
||||
kill -- -$_p9k_worker_pgid
|
||||
}
|
||||
}
|
||||
|
||||
# invoked in master: _p9k_worker_invoke <request-id> <list>
|
||||
function _p9k_worker_invoke() {
|
||||
[[ -n $_p9k__worker_resp_fd ]] || return
|
||||
local req=$1$'\x1f'$2$'\x1e'
|
||||
if [[ -n $_p9k__worker_req_fd && $+_p9k__worker_request_map[$1] == 0 ]]; then
|
||||
_p9k__worker_request_map[$1]=
|
||||
print -rnu $_p9k__worker_req_fd -- $req
|
||||
else
|
||||
_p9k__worker_request_map[$1]=$req
|
||||
fi
|
||||
}
|
||||
|
||||
function _p9k_worker_cleanup() {
|
||||
# __p9k_intro bugs out here in some cases for some reason.
|
||||
emulate -L zsh
|
||||
[[ $_p9k__worker_shell_pid == $sysparams[pid] ]] && _p9k_worker_stop
|
||||
return 0
|
||||
}
|
||||
|
||||
function _p9k_worker_stop() {
|
||||
# See comments in _p9k_worker_cleanup.
|
||||
emulate -L zsh
|
||||
add-zsh-hook -D zshexit _p9k_worker_cleanup
|
||||
[[ -n $_p9k__worker_resp_fd ]] && zle -F $_p9k__worker_resp_fd
|
||||
[[ -n $_p9k__worker_resp_fd ]] && exec {_p9k__worker_resp_fd}>&-
|
||||
[[ -n $_p9k__worker_req_fd ]] && exec {_p9k__worker_req_fd}>&-
|
||||
[[ -n $_p9k__worker_pid ]] && kill -- -$_p9k__worker_pid 2>/dev/null
|
||||
[[ -n $_p9k__worker_file_prefix ]] && zf_rm -f -- $_p9k__worker_file_prefix.fifo
|
||||
_p9k__worker_pid=
|
||||
_p9k__worker_req_fd=
|
||||
_p9k__worker_resp_fd=
|
||||
_p9k__worker_shell_pid=
|
||||
_p9k__worker_request_map=()
|
||||
return 0
|
||||
}
|
||||
|
||||
function _p9k_worker_receive() {
|
||||
eval "$__p9k_intro"
|
||||
|
||||
[[ -z $_p9k__worker_resp_fd ]] && return
|
||||
|
||||
{
|
||||
(( $# <= 1 )) || return
|
||||
|
||||
local buf resp
|
||||
|
||||
[[ -t $_p9k__worker_resp_fd ]] # https://www.zsh.org/mla/workers/2020/msg00207.html
|
||||
if sysread -i $_p9k__worker_resp_fd -t 0 'buf[$#buf+1]'; then
|
||||
while [[ $buf == *[^$'\x05\x1e']$'\x05'# ]]; do
|
||||
sysread -i $_p9k__worker_resp_fd 'buf[$#buf+1]' || return
|
||||
done
|
||||
else
|
||||
(( $? == 4 )) || return
|
||||
fi
|
||||
|
||||
local -i reset max_reset
|
||||
for resp in ${(ps:\x1e:)${buf//$'\x05'}}; do
|
||||
local arg=$resp[2,-1]
|
||||
case $resp[1] in
|
||||
d)
|
||||
local req=$_p9k__worker_request_map[$arg]
|
||||
if [[ -n $req ]]; then
|
||||
_p9k__worker_request_map[$arg]=
|
||||
print -rnu $_p9k__worker_req_fd -- $req || return
|
||||
else
|
||||
unset "_p9k__worker_request_map[$arg]"
|
||||
fi
|
||||
;;
|
||||
e)
|
||||
() { eval $arg }
|
||||
(( reset > max_reset )) && max_reset=reset
|
||||
;;
|
||||
s)
|
||||
[[ -z $_p9k__worker_req_fd ]] || return
|
||||
[[ $arg == <1-> ]] || return
|
||||
_p9k__worker_pid=$arg
|
||||
sysopen -w -o cloexec -u _p9k__worker_req_fd $_p9k__worker_file_prefix.fifo || return
|
||||
local req=
|
||||
for req in $_p9k__worker_request_map; do
|
||||
print -rnu $_p9k__worker_req_fd -- $req || return
|
||||
done
|
||||
_p9k__worker_request_map=({${(k)^_p9k__worker_request_map},''})
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( max_reset == 2 )); then
|
||||
_p9k__refresh_reason=worker
|
||||
_p9k_set_prompt
|
||||
_p9k__refresh_reason=''
|
||||
fi
|
||||
(( max_reset )) && _p9k_reset_prompt
|
||||
return 0
|
||||
} always {
|
||||
(( $? )) && _p9k_worker_stop
|
||||
}
|
||||
}
|
||||
|
||||
function _p9k_worker_start() {
|
||||
setopt monitor || return
|
||||
{
|
||||
[[ -n $_p9k__worker_resp_fd ]] && return
|
||||
_p9k__worker_file_prefix=${TMPDIR:-/tmp}/p10k.worker.$EUID.$sysparams[pid].$EPOCHSECONDS
|
||||
|
||||
sysopen -r -o cloexec -u _p9k__worker_resp_fd <(
|
||||
exec 0</dev/null
|
||||
if [[ -n $_POWERLEVEL9K_WORKER_LOG_LEVEL ]]; then
|
||||
exec 2>$_p9k__worker_file_prefix.log
|
||||
setopt xtrace
|
||||
else
|
||||
exec 2>/dev/null
|
||||
fi
|
||||
builtin cd -q / || return
|
||||
zmodload zsh/zselect || return
|
||||
! { zselect -t0 || (( $? != 1 )) } || return
|
||||
local _p9k_worker_pgid=$sysparams[pid]
|
||||
_p9k_worker_main &
|
||||
{
|
||||
trap '' PIPE
|
||||
while syswrite $'\x05'; do zselect -t 1000; done
|
||||
zf_rm -f $_p9k__worker_file_prefix.fifo
|
||||
kill -- -$_p9k_worker_pgid
|
||||
} &
|
||||
exec =true) || return
|
||||
_p9k__worker_pid=$sysparams[procsubstpid]
|
||||
zle -F $_p9k__worker_resp_fd _p9k_worker_receive
|
||||
_p9k__worker_shell_pid=$sysparams[pid]
|
||||
add-zsh-hook zshexit _p9k_worker_cleanup
|
||||
} always {
|
||||
(( $? )) && _p9k_worker_stop
|
||||
}
|
||||
}
|
BIN
zsh/.oh-my-zsh/custom/themes/powerlevel10k/powerlevel10k.png
Normal file
BIN
zsh/.oh-my-zsh/custom/themes/powerlevel10k/powerlevel10k.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
@ -0,0 +1,83 @@
|
||||
# vim:ft=zsh ts=2 sw=2 sts=2 et fenc=utf-8
|
||||
################################################################
|
||||
# Powerlevel10k Theme
|
||||
# https://github.com/romkatv/powerlevel10k
|
||||
#
|
||||
# Forked from Powerlevel9k Theme
|
||||
# https://github.com/bhilburn/powerlevel9k
|
||||
#
|
||||
# Which in turn was forked from Agnoster Theme
|
||||
# https://github.com/robbyrussell/oh-my-zsh/blob/74177c5320b2a1b2f8c4c695c05984b57fd7c6ea/themes/agnoster.zsh-theme
|
||||
################################################################
|
||||
|
||||
# Temporarily change options.
|
||||
'builtin' 'local' '-a' '__p9k_src_opts'
|
||||
[[ ! -o 'aliases' ]] || __p9k_src_opts+=('aliases')
|
||||
[[ ! -o 'sh_glob' ]] || __p9k_src_opts+=('sh_glob')
|
||||
[[ ! -o 'no_brace_expand' ]] || __p9k_src_opts+=('no_brace_expand')
|
||||
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
|
||||
|
||||
(( $+__p9k_root_dir )) || typeset -gr __p9k_root_dir=${POWERLEVEL9K_INSTALLATION_DIR:-${${(%):-%x}:A:h}}
|
||||
(( $+__p9k_intro )) || {
|
||||
# Leading spaces before `local` are important. Otherwise Antigen will remove `local` (!!!).
|
||||
# __p9k_trapint is to work around bugs in zsh: https://www.zsh.org/mla/workers/2020/msg00612.html.
|
||||
# Likewise for `trap ":"` instead of the plain `trap ""`.
|
||||
typeset -gr __p9k_intro_base='emulate -L zsh -o no_hist_expand -o extended_glob -o no_prompt_bang -o prompt_percent -o no_prompt_subst -o no_aliases -o no_bg_nice -o typeset_silent -o no_rematch_pcre
|
||||
(( $+__p9k_trapped )) || { local -i __p9k_trapped; trap : INT; trap "trap ${(q)__p9k_trapint:--} INT" EXIT }
|
||||
local -a match mbegin mend
|
||||
local -i MBEGIN MEND OPTIND
|
||||
local MATCH OPTARG IFS=$'\'' \t\n\0'\'
|
||||
typeset -gr __p9k_intro_locale='[[ $langinfo[CODESET] != (utf|UTF)(-|)8 ]] && _p9k_init_locale && { [[ -n $LC_ALL ]] && local LC_ALL=$__p9k_locale || local LC_CTYPE=$__p9k_locale }'
|
||||
typeset -gr __p9k_intro_no_locale="${${__p9k_intro_base/ match / match reply }/ MATCH / MATCH REPLY }"
|
||||
typeset -gr __p9k_intro_no_reply="$__p9k_intro_base; $__p9k_intro_locale"
|
||||
typeset -gr __p9k_intro="$__p9k_intro_no_locale; $__p9k_intro_locale"
|
||||
}
|
||||
|
||||
zmodload zsh/langinfo
|
||||
|
||||
function _p9k_init_locale() {
|
||||
if (( ! $+__p9k_locale )); then
|
||||
typeset -g __p9k_locale=
|
||||
(( $+commands[locale] )) || return
|
||||
local -a loc
|
||||
loc=(${(@M)$(locale -a 2>/dev/null):#*.(utf|UTF)(-|)8}) || return
|
||||
(( $#loc )) || return
|
||||
typeset -g __p9k_locale=${loc[(r)(#i)C.UTF(-|)8]:-${loc[(r)(#i)en_US.UTF(-|)8]:-$loc[1]}}
|
||||
fi
|
||||
[[ -n $__p9k_locale ]]
|
||||
}
|
||||
|
||||
() {
|
||||
eval "$__p9k_intro"
|
||||
if (( $+__p9k_sourced )); then
|
||||
(( $+functions[_p9k_setup] )) && _p9k_setup
|
||||
return 0
|
||||
fi
|
||||
typeset -gr __p9k_dump_file=${XDG_CACHE_HOME:-~/.cache}/p10k-dump-${(%):-%n}.zsh
|
||||
if [[ $__p9k_dump_file != $__p9k_instant_prompt_dump_file ]] && (( ! $+functions[_p9k_preinit] )) && source $__p9k_dump_file 2>/dev/null && (( $+functions[_p9k_preinit] )); then
|
||||
_p9k_preinit
|
||||
fi
|
||||
typeset -gr __p9k_sourced=13
|
||||
if [[ $ZSH_VERSION == (5.<1->*|<6->.*) ]]; then
|
||||
if [[ -w $__p9k_root_dir && -w $__p9k_root_dir/internal && -w $__p9k_root_dir/gitstatus ]]; then
|
||||
local f
|
||||
for f in $__p9k_root_dir/{powerlevel9k.zsh-theme,powerlevel10k.zsh-theme,internal/p10k.zsh,internal/icons.zsh,internal/configure.zsh,internal/worker.zsh,internal/parser.zsh,gitstatus/gitstatus.plugin.zsh,gitstatus/install}; do
|
||||
[[ $f.zwc -nt $f ]] && continue
|
||||
zmodload -F zsh/files b:zf_mv b:zf_rm
|
||||
local tmp=$f.tmp.$$.zwc
|
||||
{
|
||||
# `zf_mv -f src dst` fails on NTFS if `dst` is not writable, hence `zf_rm`.
|
||||
zf_rm -f -- $f.zwc && zcompile -R -- $tmp $f && zf_mv -f -- $tmp $f.zwc
|
||||
} always {
|
||||
(( $? )) && zf_rm -f -- $tmp
|
||||
}
|
||||
done
|
||||
fi
|
||||
fi
|
||||
builtin source $__p9k_root_dir/internal/p10k.zsh || true
|
||||
}
|
||||
|
||||
(( $+__p9k_instant_prompt_active )) && unsetopt prompt_cr prompt_sp || setopt prompt_cr prompt_sp
|
||||
|
||||
(( ${#__p9k_src_opts} )) && setopt ${__p9k_src_opts[@]}
|
||||
'builtin' 'unset' '__p9k_src_opts'
|
@ -0,0 +1 @@
|
||||
'builtin' 'source' "${POWERLEVEL9K_INSTALLATION_DIR:-${${(%):-%x}:A:h}}/powerlevel10k.zsh-theme"
|
@ -0,0 +1 @@
|
||||
'builtin' 'source' "${POWERLEVEL9K_INSTALLATION_DIR:-${${(%):-%x}:A:h}}/powerlevel10k.zsh-theme"
|
@ -0,0 +1 @@
|
||||
'builtin' 'source' "${POWERLEVEL9K_INSTALLATION_DIR:-${${(%):-%x}:A:h}}/powerlevel10k.zsh-theme"
|
44
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md
vendored
Normal file
44
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Thanks for opening an issue! For a project that deals with as many different things as P9k, debugging problems can be difficult. Please follow the guide, below, to create a bug report that will help us help you!
|
||||
|
||||
### Before Opening a Bug
|
||||
P9k is lovingly maintained by volunteers, and we are happy to help you! You can help us by first making sure your issue hasn't already been solved before opening a new one. Please check the [Troubleshooting Guide](https://github.com/bhilburn/powerlevel9k/wiki/Troubleshooting) first. Many issues are actually local configuration problems, which may have previously been solved by another user - be sure to also [search the existing issues](https://github.com/bhilburn/powerlevel9k/issues?utf8=%E2%9C%93&q=is%3Aissue) before opening a new one.
|
||||
|
||||
Once you've done these things, you can delete this section and proceed `=)`
|
||||
|
||||
-----
|
||||
|
||||
#### Describe Your Issue
|
||||
What is happening?
|
||||
|
||||
Most issues are best explained with a screenshot. Please share one if you can!
|
||||
|
||||
#### Have you tried to debug or fix it?
|
||||
|
||||
Have you tinkered with your settings, and what happened when you did? Did you find a bit of code that you think might be the culprit? Let us know what you've done so far!
|
||||
|
||||
#### Environment Information
|
||||
|
||||
This information will help us understand your configuration.
|
||||
|
||||
- What version of ZSH are you using? You can use `zsh --version` to see this.
|
||||
- Do you use a ZSH framework (e.g., Oh-My-ZSH, Antigen)?
|
||||
- How did you install P9k (cloning the repo, by tarball, a package from your OS, etc.,)?
|
||||
- What version of P9k are you using?
|
||||
- Which terminal emulator do you use?
|
||||
|
||||
#### Issues with Fonts & Icons
|
||||
You may delete this section if your issue is not font / icon related.
|
||||
|
||||
- Which font do you use?
|
||||
- Which [font configuration mode](https://github.com/bhilburn/powerlevel9k/wiki/About-Fonts) are you using? You can check this with (`echo $POWERLEVEL9K_MODE`).
|
||||
- Please share the contents of `$P9k/debug/font-issues.zsh`.
|
||||
- If this is an icon problem, does the output of `$ get_icon_names` look correct?
|
38
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.github/ISSUE_TEMPLATE/PERFORMANCE_ISSUE_TEMPLATE.md
vendored
Normal file
38
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.github/ISSUE_TEMPLATE/PERFORMANCE_ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Performance Issue
|
||||
about: For performance Issues
|
||||
title: "[Performance]"
|
||||
labels: performance
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Sorry to hear that the performance of P9K is not adequate. To fix this, please provide us with some hints.
|
||||
|
||||
### Your Hardware
|
||||
|
||||
Disk I/O is critical for P9K, so do you use a spinning disk, or a SSD?
|
||||
|
||||
### Virtualization
|
||||
|
||||
Do you use P9K in some sort of virtualization? This is also the case, if you use WSL on Windows..
|
||||
|
||||
### How Fast is Fast
|
||||
|
||||
Could you quantify how fast the specific segment is, that you think is slow?
|
||||
For example, if you think the `vcs` segment is slow, could you execute this command in the directory, where the segment is slow:
|
||||
|
||||
```zsh
|
||||
time (repeat 10; do; prompt_vcs left 1 false >/dev/null; done;)
|
||||
```
|
||||
|
||||
Also, please provide us with some context around the segment. In the `vcs` example:
|
||||
|
||||
- How big is the repo?
|
||||
- Does it contain a lot of untracked files?
|
||||
- Does it contain a lot of git submodules?
|
||||
- Does it contain a lot of files in general?
|
||||
|
||||
Additionally, you could install [zsh-prompt-benchmark](https://github.com/romkatv/zsh-prompt-benchmark), to benchmark the general performance of ZSH and P9K.
|
||||
|
||||
If you don't know which segment is slow, could you remove one by one, and spot the one that made the greatest impact?
|
33
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
33
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
Thank you so much for opening a PR for P9k! Many of our best features and segments have come from the community, and we are excited to see your contribution.
|
||||
|
||||
To help you make the best PR, here are some guidelines:
|
||||
|
||||
- The `master` branch is our *stable* branch, and the `next` branch is our development branch. If you are submitting a bug fix, please file your PR against `master`. If it is a new feature, enhancement, segment, or something similar, please submit it against `next`. For more information, please see our [Developer's Guide](https://github.com/bhilburn/powerlevel9k/wiki/Developer's-Guide).
|
||||
- We maintain unit tests for segments and features in the `test` directory. Please add unit tests for anything new you have developed! If you aren't sure how to do this, go ahead and file your PR and ask for help!
|
||||
- For running manual tests in different environments, we have Vagrant and Docker configurations. Please see the [Test README](https://github.com/bhilburn/powerlevel9k/blob/next/TESTS.md) and make sure your new feature is working as expected!
|
||||
- If your PR requires user configuration, please make sure that it includes an update to the README describing this.
|
||||
- P9k maintains a lot of useful information in our [Wiki](https://github.com/bhilburn/powerlevel9k/wiki). Depending on the content of your PR, we might ask you to update the Wiki (or provide text for us to use) to document your work. Most PRs don't require this.
|
||||
- Please make your commit messages useful! Here is a [great short guide on useful commit messages](https://code.likeagirl.io/useful-tips-for-writing-better-git-commit-messages-808770609503).
|
||||
|
||||
Once you have submitted your PR, P9k core contributors will review the code and work with you to get it merged. During this process, we might request changes to your code and discuss different ways of doing things. This is all part of the open source process, and our goal is to help you create the best contribution possible for P9k `=)`.
|
||||
|
||||
Please follow this template for creating your PR:
|
||||
|
||||
#### Title
|
||||
|
||||
Please make the title of your PR descriptive! If appropriate, please prefix the title with one of these tags:
|
||||
|
||||
- [Bugfix]
|
||||
- [New Segment]
|
||||
- [Docs]
|
||||
- [Enhancement]
|
||||
|
||||
#### Description
|
||||
|
||||
Please describe the contribution your PR makes! Screenshots are especially helpful, here, if it's a new segment.
|
||||
|
||||
If your PR is addressing an issue, please reference the Issue number here.
|
||||
|
||||
#### Questions
|
||||
|
||||
Is there something in your PR you're not sure about or need help with? Is there a particular piece of code you would like feedback on? Let us know here!
|
3
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.gitignore
vendored
Normal file
3
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
test-vm/.vagrant
|
||||
*.swp
|
||||
.idea
|
3
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.gitmodules
vendored
Normal file
3
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "shunit2"]
|
||||
path = shunit2
|
||||
url = https://github.com/kward/shunit2.git
|
77
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.travis.yml
Normal file
77
zsh/.oh-my-zsh/custom/themes/powerlevel9k/.travis.yml
Normal file
@ -0,0 +1,77 @@
|
||||
language: sh
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
osx_image: xcode9.4
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- build-essential
|
||||
- git
|
||||
- mercurial
|
||||
- subversion
|
||||
- jq
|
||||
- node
|
||||
- golang
|
||||
- ruby
|
||||
- python
|
||||
- python-virtualenv
|
||||
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
|
||||
env:
|
||||
global:
|
||||
- ZSH_DIST=$HOME/.zshdist
|
||||
matrix:
|
||||
# Use _ZSH_VERSION since if ZSH_VERSION is present, travis cacher thinks it
|
||||
# is running in zsh and tries to use zsh specific functions.
|
||||
- _ZSH_VERSION=5.5.1
|
||||
- _ZSH_VERSION=5.5
|
||||
- _ZSH_VERSION=5.4.2
|
||||
- _ZSH_VERSION=5.4.1
|
||||
- _ZSH_VERSION=5.3.1
|
||||
- _ZSH_VERSION=5.3
|
||||
- _ZSH_VERSION=5.2
|
||||
- _ZSH_VERSION=5.1.1
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $ZSH_DIST
|
||||
|
||||
before_script:
|
||||
- >
|
||||
setup_zsh() {
|
||||
dest="$ZSH_DIST/$1"
|
||||
if [[ ! -d $dest/bin ]]; then
|
||||
coreutils_mktemp="mktemp"
|
||||
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
coreutils_mktemp="gmktemp"
|
||||
fi
|
||||
tmp="$(${coreutils_mktemp} --directory --tmpdir="${TMPDIR:/tmp}" zshbuild.XXXXXX)"
|
||||
(
|
||||
cd "$tmp" &&
|
||||
curl -L http://downloads.sourceforge.net/zsh/zsh-${1}.tar.gz | tar zx &&
|
||||
cd zsh-$1 &&
|
||||
./configure --prefix="$dest" &&
|
||||
make &&
|
||||
mkdir -p "$dest" &&
|
||||
make install ||
|
||||
echo "Failed to build zsh-${1}!"
|
||||
)
|
||||
fi
|
||||
export PATH="$dest/bin:$PATH"
|
||||
}
|
||||
- setup_zsh $_ZSH_VERSION
|
||||
# Show the git version being used to test.
|
||||
- "git --version"
|
||||
# Show the mercurial version being used to test.
|
||||
- "hg --version"
|
||||
# Show the zsh version being used to test.
|
||||
- "zsh --version"
|
||||
|
||||
script:
|
||||
- test/suite.spec
|
476
zsh/.oh-my-zsh/custom/themes/powerlevel9k/CHANGELOG.md
Normal file
476
zsh/.oh-my-zsh/custom/themes/powerlevel9k/CHANGELOG.md
Normal file
@ -0,0 +1,476 @@
|
||||
## v0.6.7
|
||||
|
||||
- PR #1175 - Fix home dir shortening when using package_name truncation strategy
|
||||
- PR #1158 - [Bugfix] dir: Fix package name path truncation inside home dir
|
||||
- PR #1157 - Hide stderr from git ls-files
|
||||
- PR #1154 - Fix issues with debug/fonts script in Konsole
|
||||
- PR #1151 - [Bugfix] Only abbreviate $HOME at the beginning of cwd
|
||||
- PR #1148 - Remove checking for NODEENV_DISABLE_PROMPT
|
||||
- PR #1147 - Fix newlines in ZSH 5.7
|
||||
- PR #1149 - Fix RVM
|
||||
- PR #1128 - [Bugfix] virtualenv prompt displaying
|
||||
- PR #981 - [Bugfix] Fix for #974
|
||||
- PR #1126 - Use ip command for VPN segment
|
||||
- PR #1079 - [Bugfix] Update VIRTUAL_ENV_DISABLE_PROMPT value
|
||||
- PR #1080 - [Bugfix] Port #1071 to `master` (Fix fatal errors emitted by untracked file check in vcs.zsh)
|
||||
- PR #1074 - Add vcs vulnerability tests master
|
||||
- PR #1070 - [Docs] Uniformly apply inline code formatting in README
|
||||
- PR #1065 - Protect locale
|
||||
- PR #1048 - Speedup Improvements in `vcs` segment
|
||||
- PR #1037 - Fix vpn_ip segment
|
||||
- PR #1036 - Make truncate with package name work without setting shorten length
|
||||
- PR #1020 - Fix context spec
|
||||
- PR #990 - [Docs] Add forgotten backtick
|
||||
- PR #981 - Avoid error if `/etc/os-release` does not exist
|
||||
- PR #966 - [Bugfix] Fix icons cut off in RPROMPT segments
|
||||
|
||||
## v0.6.6
|
||||
|
||||
- The `rbenv` segment is no longer a default segment in the LPROMPT.
|
||||
- PR #959 - Fixing issue in v0.6.5 where we changed some color codes.
|
||||
- PR #934 - Add Tests
|
||||
- PR #884 - test-in-docker: fix with newer ZSH versions
|
||||
- PR #928 - [Docs] Add etc state description in dir docs
|
||||
- PR #937 - Use SUDO_COMMAND to check for sudo
|
||||
- PR #925 - [Bugfix] Resolve #918 Transparent background
|
||||
- PR #923 - Fix font issue debugging script
|
||||
- PR #921 - Add missing colors to fix color comparison
|
||||
- PR #951 - Add fallback icon for missing linux distro icons
|
||||
- PR #956 - Fix broken link in readme
|
||||
- Fixed #936 - fallback icons for Linux distros
|
||||
- Fixed #926 - `etc` state for `dir` segment in docs
|
||||
- Fixed #852 - `sudo` detection got crazy, there. sorry, everyone.
|
||||
- Fixed #927 - more default color issues.
|
||||
|
||||
## v0.6.5
|
||||
|
||||
- Multiple PRs: General fixes to README, improved documentation.
|
||||
- Multiple PRs: Improvements to icons / glyphs.
|
||||
- PR #777: now possible to always show the Ruby env segment.
|
||||
- PR #773: Fixed issue with home abbreviation in directory segment.
|
||||
- PR #789: Now properly working around some odd ZSH status return codes.
|
||||
- PR #716: Now possible to configure the colors of the VCS segment in rebase mode.
|
||||
- PR #722: Removed dependency on `bc` for `load` segment.
|
||||
- PR #686: Fixed issue where whitespaces in path occasionally broke `dir` segment.
|
||||
- PR #685: No longer accidentally invoking user `grep` aliases.
|
||||
- PR #680: Using env variable for `PYENV` properly, now.
|
||||
- PR #676, #611: Fixes for Kubernetes segment.
|
||||
- PR #667: Supporting multiple AWS profiles.
|
||||
- PR #660: Fixing directory parsing issue with PYTHONPATH.
|
||||
- PR #663: Fixed silly issues causing ZSH warnings.
|
||||
- PR #647: Fixing `public_ip` segment for macOS.
|
||||
- PR #643: Fixing `vpn_ip` segment naming.
|
||||
- PR #636: `context` segment now grabs user with command rather than env.
|
||||
- PR #618: Fix issue where `su -` didn't change context segment.
|
||||
- PR #608: Load average selection in `load` segment.
|
||||
|
||||
### New Segment: `laravel_version`
|
||||
|
||||
Displays the current laravel version.
|
||||
|
||||
## v0.6.4
|
||||
|
||||
- `load` segment now has configurable averages.
|
||||
- Update to `dir` segment to add `dir_writable` feature.
|
||||
- `status` segment can now display POSIX signal name of exit code.
|
||||
- Added `teardown` command to turn off P9k prompt.
|
||||
- Fixes for P9k in Cygwin and 32-bit systems.
|
||||
- Better colors in virtualization segments.
|
||||
- Added 'Gopher' icon to the `go_version` segment.
|
||||
- Improved detection in `nvm`
|
||||
- Added option to support command status reading from piped command sequences.
|
||||
- Fixed issue with visual artifacts with quick consecutive commands.
|
||||
- Updated 'ananconda' segment for more uniform styling.
|
||||
- `rvm` segment can now support usernames with dashes.
|
||||
- Fixed Python icon reference in some font configurations.
|
||||
- Vi mode indicator fixed.
|
||||
- Fixes for Docker segment.
|
||||
- Added new Docker-based testing system.
|
||||
- Significant enhancements to the `battery` segment. Check out the README to
|
||||
read more!
|
||||
- New truncation strategy that truncates until the path becomes unique.
|
||||
|
||||
### New Segments: `host` and `user`
|
||||
|
||||
Provides two separate segments for `host` and `user` in case you don't wont both
|
||||
in one (per the `context` segment).
|
||||
|
||||
### New Segment: `newline`
|
||||
|
||||
Allows you to split segments across multiple lines.
|
||||
|
||||
### New Segment: `kubecontext`
|
||||
|
||||
Shows the current context of your `kubectl` configuration.
|
||||
|
||||
### New Segment: `vpn`
|
||||
|
||||
Shows current `vpn` interface.
|
||||
|
||||
## v0.6.3
|
||||
|
||||
- Fixed susceptibility to [pw3nage exploit](https://github.com/njhartwell/pw3nage).
|
||||
- Added support for Android
|
||||
- The abbreviation for $HOME is now configurable (doesn't have to be `~`).
|
||||
- Fixed colorization of VCS segment in Subversion repos.
|
||||
- Improved handling of symlinks in installation paths.
|
||||
|
||||
## v0.6.2
|
||||
|
||||
- Fixed some issues with the new `nerdfont-fontconfig` option.
|
||||
- Fixed typo in README.
|
||||
- The `get_icon_names` function can now print sorted output, and show which
|
||||
icons users have overridden.
|
||||
- Added a FreeBSD VM for testing.
|
||||
|
||||
### Add debug script for iTerm2 issues
|
||||
|
||||
A new script `debug/iterm.zsh` was added for easier spotting problems with your iTerm2 configuration.
|
||||
|
||||
### Add debug script for font issues
|
||||
|
||||
A new script `debug/font-issues.zsh` was added, so that problems with your font could be spotted easier.
|
||||
|
||||
### `ram` changes
|
||||
|
||||
The `ram` segment now shows the available ram instead of free.
|
||||
|
||||
### Add new segments `host` and `user`
|
||||
|
||||
The user and host segments allow you to have different icons and colors for both the user and host segments
|
||||
depending on their state.
|
||||
|
||||
## v0.6.0
|
||||
|
||||
- Fixed a bug where the tag display was broken on detached HEADs.
|
||||
- Fixed a bug where SVN detection sometimes failed.
|
||||
- Fixed the `load` and `ram` segments for BSD.
|
||||
- Fixed code-points that changed in Awesome fonts.
|
||||
- Fixed display of "OK_ICON" in `status` segment in non-verbose mode.
|
||||
- Fixed an issue where dir name truncation that was very short sometimes failed.
|
||||
- Speed & accuracy improvements to the battery segment.
|
||||
- Added Github syntax highlighting to README.
|
||||
- Various documentation cleanup.
|
||||
|
||||
### New Font Option: nerd-fonts
|
||||
|
||||
There is now an option to use [nerd-fonts](https://github.com/ryanoasis/nerd-fonts) with P9k. Simply configure the `nerdfont-fontconfig`, and you'll be set!
|
||||
|
||||
### `vcs` changes
|
||||
|
||||
The VCS segment can now display icons for remote repo hosting services, including Github, Gitlab, and 'other'.
|
||||
|
||||
### `dir` changes
|
||||
|
||||
Added an option to configure the path separator. If you want something
|
||||
else than an ordinary slash, you could set
|
||||
`POWERLEVEL9K_DIR_PATH_SEPARATOR` to whatever you want.
|
||||
|
||||
#### `truncate_with_package_name` now searches for `composer.json` as well
|
||||
|
||||
Now `composer.json` files are searched as well. By default `package.json` still takes
|
||||
precedence. If you want to change that, set `POWERLEVEL9K_DIR_PACKAGE_FILES=(composer.json package.json)`.
|
||||
|
||||
### New segment `command_execution_time` added
|
||||
|
||||
Shows the duration a command needed to run. By default only durations over 3 seconds
|
||||
are shown (can be adjusted by setting POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD).
|
||||
|
||||
### New segment `dir_writable` added
|
||||
|
||||
This segment displays a lock icon if your user has no write permissions in the current folder.
|
||||
|
||||
### New segment `disk_usage` added
|
||||
|
||||
This segment will show the usage level of your current partition.
|
||||
|
||||
### New segment `public_ip` added
|
||||
|
||||
Fetches your Public IP (using ident.me) and displays it in your prompt.
|
||||
|
||||
### New segment `swift_version` added
|
||||
|
||||
This segment displays the version of Swift that is installed / in your path.
|
||||
|
||||
### New segment `detect_virt` added
|
||||
|
||||
Detects and reports if you are in a virtualized session using `systemd`.
|
||||
|
||||
## v0.5.0
|
||||
|
||||
### `load` and `ram` changes
|
||||
|
||||
These two segments now support BSD.
|
||||
|
||||
### `vcs` changes
|
||||
|
||||
- We implemented a huge speed improvement for this segment.
|
||||
- Now this segment supports Subversion repositories.
|
||||
- Add ability to hide tags by setting `POWERLEVEL9K_VCS_HIDE_TAGS` to true.
|
||||
|
||||
## `anaconda` changes
|
||||
|
||||
Speed improvements for `anaconda` segment.
|
||||
|
||||
## v0.4.0
|
||||
|
||||
### Development changes
|
||||
|
||||
From now on, development makes use of a CI system "travis".
|
||||
|
||||
### `vcs` changes
|
||||
|
||||
The default state was renamed to `clean`. If you overrode foreground
|
||||
or background color in the past, you need to rename your variables to:
|
||||
|
||||
```zsh
|
||||
POWERLEVEL9K_VCS_CLEAN_FOREGROUND='cyan'
|
||||
POWERLEVEL9K_VCS_CLEAN_BACKGROUND='white'
|
||||
```
|
||||
|
||||
Additionaly the vcs segment now has an `untracked` state which
|
||||
indicates that you have untracked files in your repository.
|
||||
|
||||
The foreground color of actionformat is now configurable via:
|
||||
```zsh
|
||||
POWERLEVEL9K_VCS_ACTIONFORMAT_FOREGROUND='green'
|
||||
```
|
||||
|
||||
Also, the vcs segment uses the foreground color it was configured to.
|
||||
That said, the variables `POWERLEVEL9K_VCS_FOREGROUND` and
|
||||
`POWERLEVEL9K_VCS_DARK_FOREGROUND` are no longer used. Instead use
|
||||
the proper variable `POWERLEVEL9K_VCS_<STATE>_FOREGROUND` to change
|
||||
foreground color.
|
||||
|
||||
### `dir` Shortening Strategies
|
||||
|
||||
There is now a path shortening strategy that will use the `package.json` file to
|
||||
shorten your directory path. See the documentation for the `dir` segment for more
|
||||
details.
|
||||
|
||||
Also, the shorten delimiter was changed to an unicode ellipsis. It is configurable
|
||||
via `POWERLEVEL9K_SHORTEN_DELIMITER`.
|
||||
|
||||
### `rbenv` changes
|
||||
|
||||
The `rbenv` segment now makes use of the full rbenv command, so the correct
|
||||
ruby version is now shown if it differs from the globally one.
|
||||
|
||||
### `node`, `nvm` Segments
|
||||
|
||||
Improvements to speed / reliability.
|
||||
|
||||
### `ram` changes
|
||||
|
||||
The `ram` segment was split up into `ram` and `swap`. The
|
||||
`POWERLEVEL9K_RAM_ELEMENTS` variable is obsolete.
|
||||
|
||||
### New segment `swap` added
|
||||
|
||||
Due to the split up of the ram segment, this one was created. It
|
||||
shows the currently used swap size.
|
||||
|
||||
### New segment `nodeenv` added
|
||||
|
||||
Added new `nodeenv` segment that shows the currently used node environment.
|
||||
|
||||
### New segment `aws_eb_env` added
|
||||
|
||||
This segment displays the current Elastic Beanstalk environment.
|
||||
|
||||
### New segment `chruby` added
|
||||
|
||||
Added new `chruby` segment to support this version manager.
|
||||
|
||||
### New segment `docker_machine` added
|
||||
|
||||
Added new `docker_machine` segment that will show your Docker machine.
|
||||
|
||||
### New segment `anaconda` added
|
||||
|
||||
A new segment `anaconda` was added that shows the current used
|
||||
anaconda environment.
|
||||
|
||||
## New segment `pyenv` added
|
||||
|
||||
This segment shows your active python version as reported by `pyenv`.
|
||||
|
||||
|
||||
## v0.3.2
|
||||
|
||||
### `vcs` changes
|
||||
|
||||
A new state `UNTRACKED` was added to the `vcs` segment. So we now
|
||||
have 3 states for repositories: `UNTRACKED`, `MODIFIED`, and the
|
||||
default state. The `UNTRACKED` state is active when there are files
|
||||
in the repository directory which have not been added to the repo
|
||||
(the same as when the `+` icon appears). The default color for the
|
||||
`UNTRACKED` state is now yellow, and the default color for the
|
||||
`MODIFIED` state is now read, but those colors can be changed by
|
||||
setting these variables, for example:
|
||||
|
||||
```zsh
|
||||
POWERLEVEL9K_VCS_MODIFIED_FOREGROUND='black'
|
||||
POWERLEVEL9K_VCS_MODIFIED_BACKGROUND='white'
|
||||
POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND='green'
|
||||
POWERLEVEL9K_VCS_UNTRACKED_BACKGROUND='blue'
|
||||
```
|
||||
|
||||
## v0.3.1
|
||||
|
||||
### `dir` changes
|
||||
|
||||
A new state `HOME_SUBFOLDER` was added. So if you want to overwrite
|
||||
colors for this segment, also set this variables:
|
||||
```zsh
|
||||
POWERLEVEL9K_DIR_HOME_SUBFOLDER_BACKGROUND='black'
|
||||
POWERLEVEL9K_DIR_HOME_SUBFOLDER_FOREGROUND='white'
|
||||
```
|
||||
|
||||
### `background_jobs` changes
|
||||
Now displays the number of background jobs if there's more than 1.
|
||||
You can disable it by setting :
|
||||
```zsh
|
||||
POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false
|
||||
```
|
||||
|
||||
## v0.3.0
|
||||
|
||||
### Introduced "visual identifiers" to the segments
|
||||
|
||||
Now almost every segment can have a visual identifier, which is an
|
||||
icon whose color could be adjusted by users.
|
||||
|
||||
### Added ability for "joined" segments
|
||||
|
||||
You can now merge segments together by suffixing the segment name with "_joined".
|
||||
For Developers: Be aware that the order of parameters in left/right_prompt_segment
|
||||
has changed. Now a boolean parameter must be set as second parameter (true if joined).
|
||||
|
||||
### `dir` changes
|
||||
|
||||
This segment now has "state", which means you now can change the colors seperatly
|
||||
depending if you are in your homefolder or not.
|
||||
Your variables for that should now look like:
|
||||
```zsh
|
||||
POWERLEVEL9K_DIR_HOME_BACKGROUND='green'
|
||||
POWERLEVEL9K_DIR_HOME_FOREGROUND='cyan'
|
||||
POWERLEVEL9K_DIR_DEFAULT_BACKGROUND='red'
|
||||
POWERLEVEL9K_DIR_DEFAULT_FOREGROUND='yellow'
|
||||
```
|
||||
|
||||
### `status` changes
|
||||
|
||||
The `status` segment was split up into three segments. `background_jobs` prints
|
||||
an icon if there are background jobs. `root_indicator` prints an icon if the user
|
||||
is root. The `status` segment focuses now on the status only.
|
||||
The `status` segment also now has "state". If you want to overwrite the colors,
|
||||
you have to add the state to your variables:
|
||||
```zsh
|
||||
POWERLEVEL9K_STATUS_ERROR_BACKGROUND='green'
|
||||
POWERLEVEL9K_STATUS_ERROR_FOREGROUND='cyan'
|
||||
POWERLEVEL9K_STATUS_OK_BACKGROUND='red'
|
||||
POWERLEVEL9K_STATUS_OK_FOREGROUND='yellow'
|
||||
```
|
||||
|
||||
### New segment `custom_command` added
|
||||
|
||||
A new segment that allows users to define a custom command was added.
|
||||
|
||||
### `virtualenv` changes
|
||||
|
||||
This segment now respects `VIRTUAL_ENV_DISABLE_PROMPT`. If this variable is set
|
||||
to `true`, the segments does not get rendered.
|
||||
|
||||
### `load` changes
|
||||
|
||||
The `load` segement was split and a new segment `ram` was extracted. This new
|
||||
segment is able to show the free ram and used swap.
|
||||
|
||||
### `vcs` changes
|
||||
|
||||
This prompt uses the `VCS_INFO` subsystem by ZSH. From now on this subsystem
|
||||
is only invoked if a `vcs` segment was configured.
|
||||
|
||||
### `rvm` changes
|
||||
|
||||
This segment now does not invoke RVM directly anymore. Instead, is relys on the
|
||||
circumstance that RVM was invoked beforehand and just reads the environment
|
||||
variables '$GEM_HOME' and '$MY_RUBY_HOME'. It also now displays the used gemset.
|
||||
|
||||
### New segment `battery` added
|
||||
|
||||
A new segment that shows the battery status of your laptop was added.
|
||||
|
||||
### New segment `go_version` added
|
||||
|
||||
This segment shows the GO version.
|
||||
|
||||
### New segment `nvm` added
|
||||
|
||||
This segment shows your NodeJS version by using NVM (and if it is not 'default').
|
||||
|
||||
### New segment `todo` added
|
||||
|
||||
This segment shows your ToDos from [todo.sh](http://todotxt.com/).
|
||||
|
||||
### New segment `rust_version` added
|
||||
|
||||
This segment shows your local rust version.
|
||||
|
||||
## v0.2.0
|
||||
|
||||
### `longstatus` is now `status`
|
||||
|
||||
The segments got merged together. To show the segment only if an error occurred,
|
||||
set `POWERLEVEL9K_STATUS_VERBOSE=false` (this is the same behavior as the old
|
||||
`status` segment.
|
||||
|
||||
### Icon overriding mechanism added
|
||||
|
||||
All icons can now be overridden by setting a variable named by the internal icon
|
||||
name. You can get a full list of icon name by calling `get_icon_names`.
|
||||
|
||||
### Same color segements get visual separator
|
||||
|
||||
This separator can be controlled by setting `POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR`
|
||||
or `POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR`. By default this separator is
|
||||
printed in the foreground color.
|
||||
|
||||
### `dir` segment has different strategies for truncation
|
||||
|
||||
Now you can choose between `truncate_middle` or `truncate_from_right` by setting
|
||||
`POWERLEVEL9K_SHORTEN_STRATEGY`. Default behavior is unchanged (truncate whole
|
||||
directories). `POWERLEVEL9K_SHORTEN_DIR_LENGTH` can be used to influence how
|
||||
much will be truncated (either direcories or chars).
|
||||
|
||||
### New segment `ip` added
|
||||
|
||||
This segment shows your internal IP address. You can define which interfaces IP
|
||||
will be shown by specifying it via `POWERLEVEL9K_IP_INTERFACE`.
|
||||
|
||||
### New segment `load` added
|
||||
|
||||
This segment shows your computers 5min load average.
|
||||
|
||||
### New segment `os_icon` added
|
||||
|
||||
This segment shows a little indicator which OS you are running.
|
||||
|
||||
### New segment `php_version` added
|
||||
|
||||
This segment shows your PHP version.
|
||||
|
||||
### New segment `vi_mode` added
|
||||
|
||||
This segment gives you a hint in which VI-mode you currently are. This
|
||||
segment requires a proper configured VI-mode.
|
||||
|
||||
### Added the ability to have empty left or right prompts
|
||||
|
||||
By setting the according variable to an empty array, the left or right
|
||||
prompt will be empty.
|
||||
|
||||
## v0.1.0
|
||||
|
||||
This is the first release
|
46
zsh/.oh-my-zsh/custom/themes/powerlevel9k/CODE_OF_CONDUCT.md
Normal file
46
zsh/.oh-my-zsh/custom/themes/powerlevel9k/CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at bhilburn@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
20
zsh/.oh-my-zsh/custom/themes/powerlevel9k/LICENSE
Normal file
20
zsh/.oh-my-zsh/custom/themes/powerlevel9k/LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014-2017 Ben Hilburn <bhilburn@gmail.com>
|
||||
|
||||
MIT LICENSE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
776
zsh/.oh-my-zsh/custom/themes/powerlevel9k/README.md
Normal file
776
zsh/.oh-my-zsh/custom/themes/powerlevel9k/README.md
Normal file
@ -0,0 +1,776 @@
|
||||
## Powerlevel9k is deprecated and now unmaintained. Please use [Powerlevel10k](https://github.com/romkatv/powerlevel10k)!
|
||||
---
|
||||

|
||||
---
|
||||
[](https://travis-ci.org/bhilburn/powerlevel9k)
|
||||
[](https://gitter.im/bhilburn/powerlevel9k?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Powerlevel9k is a theme for ZSH which uses [Powerline
|
||||
Fonts](https://github.com/powerline/fonts). It can be used with vanilla ZSH or
|
||||
ZSH frameworks such as [Oh-My-Zsh](https://github.com/robbyrussell/oh-my-zsh),
|
||||
[Prezto](https://github.com/sorin-ionescu/prezto),
|
||||
[Antigen](https://github.com/zsh-users/antigen), and [many
|
||||
others](https://github.com/bhilburn/powerlevel9k/wiki/Install-Instructions).
|
||||
|
||||
Get more out of your terminal. Be a badass. Impress everyone in 'Screenshot Your
|
||||
Desktop' threads. Use powerlevel9k.
|
||||
|
||||

|
||||
|
||||
You can check out some other users' configurations in our wiki: [Show Off Your
|
||||
Config](https://github.com/bhilburn/powerlevel9k/wiki/Show-Off-Your-Config).
|
||||
|
||||
There are a number of Powerline ZSH themes available, now. The developers of
|
||||
this theme focus on four primary goals:
|
||||
|
||||
1. Give users a great out-of-the-box configuration with no additional
|
||||
configuration required.
|
||||
2. Make customization easy for users who do want to tweak their prompt.
|
||||
3. Provide useful segments that you can enable to make your prompt even more
|
||||
effective and helpful. We have prompt segments for everything from unit test
|
||||
coverage to your AWS instance.
|
||||
4. Optimize the code for execution speed as much as possible. A snappy terminal
|
||||
is a happy terminal.
|
||||
|
||||
Powerlevel9k can be used to create both very useful and beautiful terminal environments:
|
||||
|
||||

|
||||
|
||||
### Table of Contents
|
||||
|
||||
1. [Installation](#installation)
|
||||
2. [Customization](#prompt-customization)
|
||||
1. [Stylizing Your Prompt](https://github.com/bhilburn/powerlevel9k/wiki/Stylizing-Your-Prompt)
|
||||
2. [Customizing Prompt Segments](#customizing-prompt-segments)
|
||||
3. [Available Prompt Segments](#available-prompt-segments)
|
||||
3. [Troubleshooting](https://github.com/bhilburn/powerlevel9k/wiki/Troubleshooting)
|
||||
|
||||
Be sure to also [check out the Wiki](https://github.com/bhilburn/powerlevel9k/wiki)!
|
||||
|
||||
### Installation
|
||||
There are two installation steps to go from a vanilla terminal to a PL9k
|
||||
terminal. Once you are done, you can optionally customize your prompt.
|
||||
|
||||
[Installation Instructions](https://github.com/bhilburn/powerlevel9k/wiki/Install-Instructions)
|
||||
|
||||
1. [Install the Powerlevel9k Theme](https://github.com/bhilburn/powerlevel9k/wiki/Install-Instructions#step-1-install-powerlevel9k)
|
||||
2. [Install Powerline Fonts](https://github.com/bhilburn/powerlevel9k/wiki/Install-Instructions#step-2-install-a-powerline-font)
|
||||
|
||||
No configuration is necessary post-installation if you like the default
|
||||
settings, but there are plenty of segment customization options available if you
|
||||
are interested.
|
||||
|
||||
### Prompt Customization
|
||||
|
||||
Be sure to check out the wiki page on the additional prompt customization
|
||||
options, including color and icon settings: [Stylizing Your Prompt](https://github.com/bhilburn/powerlevel9k/wiki/Stylizing-Your-Prompt)
|
||||
|
||||
#### Customizing Prompt Segments
|
||||
Customizing your prompt is easy! Select the segments you want to have displayed,
|
||||
and then assign them to either the left or right prompt by adding the following
|
||||
variables to your `~/.zshrc`.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_LEFT_PROMPT_ELEMENTS`|`(context dir vcs)`|Segment list for left prompt|
|
||||
|`POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS`|`(status root_indicator background_jobs history time)`|Segment list for right prompt|
|
||||
|
||||
|
||||
The table above shows the default values, so if you wanted to set these
|
||||
variables manually, you would put the following in
|
||||
your `~/.zshrc`:
|
||||
```zsh
|
||||
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(context dir vcs)
|
||||
POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status root_indicator background_jobs history time)
|
||||
```
|
||||
#### Available Prompt Segments
|
||||
The segments that are currently available are:
|
||||
|
||||
**System Status Segments:**
|
||||
* [`background_jobs`](#background_jobs) - Indicator for background jobs.
|
||||
* [`battery`](#battery) - Current battery status.
|
||||
* [`context`](#context) - Your username and host, conditionalized based on $USER and SSH status.
|
||||
* [`date`](#date) - System date.
|
||||
* [`dir`](#dir) - Your current working directory.
|
||||
* `dir_writable` - Displays a lock icon, if you do not have write permissions on the current folder.
|
||||
* [`disk_usage`](#disk_usage) - Disk usage of your current partition.
|
||||
* `history` - The command number for the current line.
|
||||
* [`host`](#host) - Your current host name
|
||||
* [`ip`](#ip) - Shows the current IP address.
|
||||
* [`vpn_ip`](#vpn_ip) - Shows the current VPN IP address.
|
||||
* [`public_ip`](#public_ip) - Shows your public IP address.
|
||||
* [`load`](#load) - Your machine's load averages.
|
||||
* `os_icon` - Display a nice little icon, depending on your operating system.
|
||||
* `ram` - Show free RAM.
|
||||
* `root_indicator` - An indicator if the user has superuser status.
|
||||
* [`status`](#status) - The return code of the previous command.
|
||||
* `swap` - Prints the current swap size.
|
||||
* [`time`](#time) - System time.
|
||||
* [`user`](#user) - Your current username
|
||||
* [`vi_mode`](#vi_mode)- Your prompt's Vi editing mode (NORMAL|INSERT).
|
||||
* `ssh` - Indicates whether or not you are in an SSH session.
|
||||
|
||||
**Development Environment Segments:**
|
||||
* [`vcs`](#vcs) - Information about this `git` or `hg` repository (if you are in one).
|
||||
|
||||
**Language Segments:**
|
||||
* **GoLang Segments:**
|
||||
* `go_version` - Show the current GO version.
|
||||
* **Javascript / Node.js Segments:**
|
||||
* `node_version` - Show the version number of the installed Node.js.
|
||||
* [`nodeenv`](#nodeenv) - [nodeenv](https://github.com/ekalinin/nodeenv) prompt for displaying node version and environment name.
|
||||
* `nvm` - Show the version of Node that is currently active, if it differs from the version used by NVM
|
||||
* **PHP Segments:**
|
||||
* `php_version` - Show the current PHP version.
|
||||
* `laravel_version` - Show the current Laravel version.
|
||||
* [`symfony2_tests`](#symfony2_tests) - Show a ratio of test classes vs code classes for Symfony2.
|
||||
* `symfony2_version` - Show the current Symfony2 version, if you are in a Symfony2-Project dir.
|
||||
* **Python Segments:**
|
||||
* [`virtualenv`](#virtualenv) - Your Python [VirtualEnv](https://virtualenv.pypa.io/en/latest/).
|
||||
* [`anaconda`](#anaconda) - Your active [Anaconda](https://www.continuum.io/why-anaconda) environment.
|
||||
* `pyenv` - Your active python version as reported by the first word of [`pyenv version`](https://github.com/yyuu/pyenv). Note that the segment is not displayed if that word is _system_ i.e. the segment is inactive if you are using system python.
|
||||
* **Ruby Segments:**
|
||||
* [`chruby`](#chruby) - Ruby environment information using `chruby` (if one is active).
|
||||
* [`rbenv`](#rbenv) - Ruby environment information using `rbenv` (if one is active).
|
||||
* [`rspec_stats`](#rspec_stats) - Show a ratio of test classes vs code classes for RSpec.
|
||||
* `rvm` - Ruby environment information using `$GEM_HOME` and `$MY_RUBY_HOME` (if one is active).
|
||||
* **Rust Segments:**
|
||||
* `rust_version` - Display the current rust version and [logo](https://www.rust-lang.org/logos/rust-logo-blk.svg).
|
||||
* **Swift Segments:**
|
||||
* `swift_version` - Show the version number of the installed Swift.
|
||||
* **Java Segments:**
|
||||
* `java_version` - Show the current Java version.
|
||||
|
||||
**Cloud Segments:**
|
||||
* **AWS Segments:**
|
||||
* [`aws`](#aws) - The current AWS profile, if active.
|
||||
* `aws_eb_env` - The current Elastic Beanstalk Environment.
|
||||
* `docker_machine` - The current Docker Machine.
|
||||
* `kubecontext` - The current context of your `kubectl` configuration.
|
||||
* `dropbox` - Indicates Dropbox directory and syncing status using `dropbox-cli`
|
||||
|
||||
**Other:**
|
||||
* [`custom_command`](#custom_command) - Create a custom segment to display the
|
||||
output of an arbitrary command.
|
||||
* [`command_execution_time`](#command_execution_time) - Display the time the current command took to execute.
|
||||
* [`todo`](http://todotxt.com/) - Shows the number of tasks in your todo.txt tasks file.
|
||||
* `detect_virt` - Virtualization detection with systemd
|
||||
* `newline` - Continues the prompt on a new line.
|
||||
* `openfoam` - Shows the currently sourced [OpenFOAM](https://openfoam.org/) environment.
|
||||
|
||||
---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
##### anaconda
|
||||
|
||||
This segment shows your active anaconda environment. It relies on either the
|
||||
`CONDA_ENV_PATH` or the `CONDA_PREFIX` (depending on the `conda` version)
|
||||
environment variable to be set which happens when you properly `source
|
||||
activate` an environment.
|
||||
|
||||
Special configuration variables:
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_ANACONDA_LEFT_DELIMITER`|"("|The left delimiter just before the environment name.|
|
||||
|`POWERLEVEL9K_ANACONDA_RIGHT_DELIMITER`|")"|The right delimiter just after the environment name.|
|
||||
|
||||
Additionally the following segment specific parameters can be used to customize
|
||||
it: `POWERLEVEL9K_PYTHON_ICON`, `POWERLEVEL9K_ANACONDA_BACKGROUND`, and
|
||||
`POWERLEVEL9K_ANACONDA_FOREGROUND`.
|
||||
|
||||
##### aws
|
||||
|
||||
If you would like to display the [current AWS
|
||||
profile](http://docs.aws.amazon.com/cli/latest/userguide/installing.html), add
|
||||
the `aws` segment to one of the prompts, and define `AWS_DEFAULT_PROFILE` in
|
||||
your `~/.zshrc`:
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`AWS_DEFAULT_PROFILE`|None|Your AWS profile name|
|
||||
|
||||
##### background_jobs
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE`|`true`|If there is more than one background job, this segment will show the number of jobs. Set this to `false` to turn this feature off.|
|
||||
`POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE_ALWAYS`|`false`|Always show the jobs count (even if it's zero).|
|
||||
|
||||
##### battery
|
||||
|
||||
The default settings for this segment will display your current battery status (fails gracefully on
|
||||
systems without a battery). It is supported on both OSX and Linux (note that it requires `acpi` on Linux).
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_BATTERY_CHARGING`|`"yellow"`|Color to indicate a charging battery.|
|
||||
|`POWERLEVEL9K_BATTERY_CHARGED`|`"green"`|Color to indicate a charged battery.|
|
||||
|`POWERLEVEL9K_BATTERY_DISCONNECTED`|`$DEFAULT_COLOR`|Color to indicate absence of battery.|
|
||||
|`POWERLEVEL9K_BATTERY_LOW_THRESHOLD`|`10`|Threshold to consider battery level critical.|
|
||||
|`POWERLEVEL9K_BATTERY_LOW_COLOR`|`"red"`|Color to indicate critically low charge level.|
|
||||
|`POWERLEVEL9K_BATTERY_VERBOSE`|`true`|Display time remaining next to battery level.|
|
||||
|`POWERLEVEL9K_BATTERY_HIDE_ABOVE_THRESHOLD`|`unset`|Threshold from which the battery segment should not be displayed.|
|
||||
|
||||
Note that you can [modify the `_FOREGROUND`
|
||||
color](https://github.com/bhilburn/powerlevel9k/wiki/Stylizing-Your-Prompt#segment-color-customization)
|
||||
without affecting the icon color.
|
||||
|
||||
You can also change the battery icon automatically depending on the battery
|
||||
level. This will override the default battery icon. In order to do this, you
|
||||
need to define the `POWERLEVEL9k_BATTERY_STAGES` variable.
|
||||
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|-------------------------------|---------------|---------------------------------------------------------------|
|
||||
| `POWERLEVEL9K_BATTERY_STAGES` | Unset | A string or array, which each index indicates a charge level. |
|
||||
|
||||
Powerlevel9k will use each index of the string or array as a stage to indicate battery
|
||||
charge level, progressing from left to right. You can provide any number of
|
||||
stages. The setting below, for example, provides 8 stages for Powerlevel9k to use.
|
||||
```zsh
|
||||
POWERLEVEL9K_BATTERY_STAGES="▁▂▃▄▅▆▇█"
|
||||
```
|
||||
|
||||
If you require extra spacing after the icon, you will have to set it as an array,
|
||||
since spaces in the string will be used as one of the stages and you will get a
|
||||
missing icon. To do this, declare the variable as follows:
|
||||
```zsh
|
||||
POWERLEVEL9K_BATTERY_STAGES=($'\u2581 ' $'\u2582 ' $'\u2583 ' $'\u2584 ' $'\u2585 ' $'\u2586 ' $'\u2587 ' $'\u2588 ')
|
||||
```
|
||||
|
||||
Using the array syntax, you can create stages comprised of multiple characters.
|
||||
The below setting provides 40 battery stages.
|
||||
```zsh
|
||||
POWERLEVEL9K_BATTERY_STAGES=(
|
||||
$'▏ ▏' $'▎ ▏' $'▍ ▏' $'▌ ▏' $'▋ ▏' $'▊ ▏' $'▉ ▏' $'█ ▏'
|
||||
$'█▏ ▏' $'█▎ ▏' $'█▍ ▏' $'█▌ ▏' $'█▋ ▏' $'█▊ ▏' $'█▉ ▏' $'██ ▏'
|
||||
$'██ ▏' $'██▎ ▏' $'██▍ ▏' $'██▌ ▏' $'██▋ ▏' $'██▊ ▏' $'██▉ ▏' $'███ ▏'
|
||||
$'███ ▏' $'███▎ ▏' $'███▍ ▏' $'███▌ ▏' $'███▋ ▏' $'███▊ ▏' $'███▉ ▏' $'████ ▏'
|
||||
$'████ ▏' $'████▎▏' $'████▍▏' $'████▌▏' $'████▋▏' $'████▊▏' $'████▉▏' $'█████▏' )
|
||||
```
|
||||
|
||||
You can also change the background of the segment automatically depending on the
|
||||
battery level. This will override the following variables:
|
||||
`POWERLEVEL9K_BATTERY_CHARGING`, `POWERLEVEL9K_BATTERY_CHARGED`,
|
||||
`POWERLEVEL9K_BATTERY_DISCONNECTED`, and `POWERLEVEL9K_BATTERY_LOW_COLOR`. In
|
||||
order to do this, define a color array, from low to high, as shown below:
|
||||
```zsh
|
||||
POWERLEVEL9K_BATTERY_LEVEL_BACKGROUND=(red1 orangered1 darkorange orange1 gold1 yellow1 yellow2 greenyellow chartreuse1 chartreuse2 green1)
|
||||
```
|
||||
|
||||
As with the battery stages, you can use any number of colors and Powerlevel9k
|
||||
will automatically use all of them appropriately.
|
||||
|
||||
Some example settings:
|
||||
|
||||
| Brightness | Possible Array |
|
||||
|----------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| Bright Colors | `(red1 orangered1 darkorange orange1 gold1 yellow1 yellow2 greenyellow chartreuse1 chartreuse2 green1)` |
|
||||
| Normal Colors | `(red3 darkorange3 darkgoldenrod gold3 yellow3 chartreuse2 mediumspringgreen green3 green3 green4 darkgreen)` |
|
||||
| Subdued Colors | `(darkred orange4 yellow4 yellow4 chartreuse3 green3 green4 darkgreen)` |
|
||||
|
||||
##### chruby
|
||||
|
||||
This segment shows the version of Ruby being used when using `chruby` to change your current Ruby stack.
|
||||
|
||||
It uses `$RUBY_ENGINE` and `$RUBY_VERSION` as set by `chruby`.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_CHRUBY_SHOW_ENGINE`|true|Show the currently selected Ruby engine (e.g. `ruby`, `jruby`, `rbx`, etc)
|
||||
|`POWERLEVEL9K_CHRUBY_SHOW_VERSION`|true|Shows the currently selected engine's version (e.g. `2.5.1`)
|
||||
|
||||
##### command_execution_time
|
||||
|
||||
Display the time the previous command took to execute if the time is above
|
||||
`POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD`. The time is formatted to be
|
||||
"human readable", and so scales the units based on the length of execution time.
|
||||
If you want more precision, just set the
|
||||
`POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION` field.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD`|3|Threshold above which to print this segment. Can be set to `0` to always print.|
|
||||
|`POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION`|2|Number of digits to use in the fractional part of the time value.|
|
||||
|
||||
##### custom_command
|
||||
|
||||
The `custom_...` segment allows you to turn the output of a custom command into
|
||||
a prompt segment. As an example, if you wanted to create a custom segment to
|
||||
display your WiFi signal strength, you might define a custom segment called
|
||||
`custom_wifi_signal` like this:
|
||||
```zsh
|
||||
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(context time battery dir vcs virtualenv custom_wifi_signal)
|
||||
POWERLEVEL9K_CUSTOM_WIFI_SIGNAL="echo signal: \$(nmcli device wifi | grep yes | awk '{print \$8}')"
|
||||
POWERLEVEL9K_CUSTOM_WIFI_SIGNAL_BACKGROUND="blue"
|
||||
POWERLEVEL9K_CUSTOM_WIFI_SIGNAL_FOREGROUND="yellow"
|
||||
```
|
||||
If you prefer, you can also define the function in your `.zshrc` rather than
|
||||
putting it in-line with the variable export, as shown above. Just don't forget
|
||||
to invoke your function from your segment! Example code that achieves the same
|
||||
result as the above:
|
||||
```zsh
|
||||
zsh_wifi_signal(){
|
||||
local signal=$(nmcli device wifi | grep yes | awk '{print $8}')
|
||||
local color='%F{yellow}'
|
||||
[[ $signal -gt 75 ]] && color='%F{green}'
|
||||
[[ $signal -lt 50 ]] && color='%F{red}'
|
||||
echo -n "%{$color%}\uf230 $signal%{%f%}" # \uf230 is
|
||||
}
|
||||
|
||||
POWERLEVEL9K_CUSTOM_WIFI_SIGNAL="zsh_wifi_signal"
|
||||
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(context time battery dir vcs virtualenv custom_wifi_signal)
|
||||
```
|
||||
The command, above, gives you the wireless signal segment shown below:
|
||||
|
||||

|
||||
|
||||
You can define as many custom segments as you wish. If you think you have
|
||||
a segment that others would find useful, please consider upstreaming it to the
|
||||
main theme distribution so that everyone can use it!
|
||||
|
||||
##### context
|
||||
|
||||
The `context` segment (user@host string) is conditional. By default, it will
|
||||
only print if you are not your 'normal' user (including if you are root), or if
|
||||
you are SSH'd to a remote host. `SUDO` and `REMOTE_SUDO` states are also available to show whether the current user or remote user has superuser privileges.
|
||||
|
||||
To use this feature, make sure the `context` segment is enabled in your prompt
|
||||
elements (it is by default), and define a `DEFAULT_USER` in your `~/.zshrc`.
|
||||
|
||||
You can customize the `context` segment. For example, you can make it to print the
|
||||
full hostname by setting
|
||||
|
||||
```
|
||||
POWERLEVEL9K_CONTEXT_TEMPLATE="%n@`hostname -f`"
|
||||
```
|
||||
|
||||
You can set the `POWERLEVEL9K_CONTEXT_HOST_DEPTH` variable to change how the
|
||||
hostname is displayed. See [ZSH Manual](http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html#Login-information)
|
||||
for details. The default is set to %m which will show the hostname up to the first ‘.’
|
||||
You can set it to %{N}m where N is an integer to show that many segments of system
|
||||
hostname. Setting N to a negative integer will show that many segments from the
|
||||
end of the hostname.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`DEFAULT_USER`|None|Username to consider a "default context" (you can also set `$USER`).|
|
||||
|`POWERLEVEL9K_ALWAYS_SHOW_CONTEXT`|false|Always show this segment, including $USER and hostname.|
|
||||
|`POWERLEVEL9K_ALWAYS_SHOW_USER`|false|Always show the username, but conditionalize the hostname.|
|
||||
|`POWERLEVEL9K_CONTEXT_TEMPLATE`|%n@%m|Default context prompt (username@machine). Refer to the [ZSH Documentation](http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html) for all possible expansions, including deeper host depths.|
|
||||
|
||||
This segment can have different states. They might help you to visualize your
|
||||
different privileges. Read more about styling with states [here](https://github.com/bhilburn/powerlevel9k/wiki/Stylizing-Your-Prompt#special-segment-colors).
|
||||
|
||||
| State | Meaning |
|
||||
|---------------|----------------------------------------------------------|
|
||||
| `DEFAULT` | You are a normal user |
|
||||
| `ROOT` | You are the root user |
|
||||
| `SUDO` | You are using elevated rights |
|
||||
| `REMOTE_SUDO` | You are SSH'ed into the machine and have elevated rights |
|
||||
| `REMOTE` | You are SSH'ed into the machine |
|
||||
|
||||
##### date
|
||||
|
||||
The `date` segment shows the current system date.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_DATE_FORMAT`|`%D{%d.%m.%y}`|[ZSH time format](http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html#Date-and-time) to use in this segment.|
|
||||
|
||||
##### dir
|
||||
|
||||
The `dir` segment shows the current working directory. When using the "Awesome
|
||||
Powerline" fonts, there are additional glyphs, as well:
|
||||
|
||||
| `Compatible` | `Powerline` | `Awesome Powerline` | Situation
|
||||
|------------|-----------|-------------------|----------------------------
|
||||
| None | None |  | At the root of your home folder |
|
||||
| None | None |  | Within a subfolder of your home directory |
|
||||
| None | None |  | Outside of your home folder |
|
||||
| None | None | ⚙ | Within the `/etc` directory |
|
||||
|
||||
To turn off these icons you could set these variables to an empty string.
|
||||
```zsh
|
||||
POWERLEVEL9K_HOME_ICON=''
|
||||
POWERLEVEL9K_HOME_SUB_ICON=''
|
||||
POWERLEVEL9K_FOLDER_ICON=''
|
||||
POWERLEVEL9K_ETC_ICON=''
|
||||
```
|
||||
You can limit the output to a certain length by truncating long paths.
|
||||
Customizations available are:
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_DIR_PATH_ABSOLUTE`|None|If set to `true`, will use absolute paths instead of home folder abbreviation `~`|
|
||||
|`POWERLEVEL9K_SHORTEN_DIR_LENGTH`|`2`|If your shorten strategy, below, is entire directories, this field determines how many directories to leave at the end. If your shorten strategy is by character count, this field determines how many characters to allow per directory string.|
|
||||
|`POWERLEVEL9K_SHORTEN_STRATEGY`|None|How the directory strings should be truncated. See the table below for more informations.|
|
||||
|`POWERLEVEL9K_SHORTEN_DELIMITER`|`..`|Delimiter to use in truncated strings. This can be any string you choose, including an empty string if you wish to have no delimiter.|
|
||||
|
||||
| Strategy Name | Description |
|
||||
|---------------|-------------|
|
||||
|Default|Truncate whole directories from left. How many is defined by `POWERLEVEL9K_SHORTEN_DIR_LENGTH`|
|
||||
|`truncate_absolute_chars`|Truncates an absolute number of characters from the left such that the number of characters that your path displays (with or without `POWERLEVEL9K_SHORTEN_DELIMITER`) is no more than `POWERLEVEL9K_SHORTEN_DIR_LENGTH` + the length of `POWERLEVEL9K_SHORTEN_DELIMITER` |
|
||||
|`truncate_middle`|Truncates the middle part of a folder. E.g. you are in a folder named `~/MySuperProjects/AwesomeFiles/BoringOffice`, then it will truncated to `~/MyS..cts/Awe..les/BoringOffice`, if `POWERLEVEL9K_SHORTEN_DIR_LENGTH=3` is also set (controls the amount of characters to be left).|
|
||||
|`truncate_from_right`|Just leaves the beginning of a folder name untouched. E.g. your folders will be truncated like so: `/ro../Pr../office`. How many characters will be untouched is controlled by `POWERLEVEL9K_SHORTEN_DIR_LENGTH`.|
|
||||
|`truncate_absolute`|Truncates everything exept the last few characters in the path. E.g. if you are in a folder named `~/Projects/powerlevel9k` and you have set `POWERLEVEL9K_SHORTEN_DIR_LENGTH=3`, you will get `..l9k`.|
|
||||
|`truncate_to_last`|Truncates everything before the last folder in the path.|
|
||||
|`truncate_to_first_and_last`|Truncate middle directories from the path. How many directories will be untouched is controlled by `POWERLEVEL9K_SHORTEN_DIR_LENGTH`. E.g. if you are in a folder named `~/Projects/powerlevel9k` and you have set `POWERLEVEL9K_SHORTEN_DIR_LENGTH=1`, you will get `~/../powerlevel9k`.||
|
||||
|`truncate_to_unique`|Parse all parent path components and truncate them to the shortest unique length. If you copy & paste the result to a shell, after hitting `TAB` it should expand to the original path unambiguously.|
|
||||
|`truncate_with_package_name`|Search for a `package.json` or `composer.json` and prints the `name` field to abbreviate the directory path. The precedence and/or files could be set by `POWERLEVEL9K_DIR_PACKAGE_FILES=(package.json composer.json)`. If you have [jq](https://stedolan.github.io/jq/) installed, it will dramatically improve the speed of this strategy.|
|
||||
|`truncate_with_folder_marker`|Search for a file that is specified by `POWERLEVEL9K_SHORTEN_FOLDER_MARKER` and truncate everything before that (if found, otherwise stop on $HOME and ROOT).|
|
||||
|
||||
For example, if you wanted the truncation behavior of the `fish` shell, which
|
||||
truncates `/usr/share/plasma` to `/u/s/plasma`, you would use the following:
|
||||
```zsh
|
||||
POWERLEVEL9K_SHORTEN_DIR_LENGTH=1
|
||||
POWERLEVEL9K_SHORTEN_DELIMITER=""
|
||||
POWERLEVEL9K_SHORTEN_STRATEGY="truncate_from_right"
|
||||
```
|
||||
In each case you have to specify the length you want to shorten the directory
|
||||
to. So in some cases `POWERLEVEL9K_SHORTEN_DIR_LENGTH` means characters, in
|
||||
others whole directories.
|
||||
|
||||
The `truncate_with_package_name` strategy gives your directory path relative to the root of your project. For example, if you have a project inside `$HOME/projects/my-project` with a `package.json` that looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-cool-project"
|
||||
}
|
||||
```
|
||||
|
||||
The path shown would be `my-cool-project`. If you navigate to `$HOME/projects/my-project/src`, then the path shown would be `my-cool-project/src`. Please note that this currently looks for `.git` directory to determine the root of the project.
|
||||
|
||||
If you want to customize the directory separator, you could set:
|
||||
```zsh
|
||||
# Double quotes are important here!
|
||||
POWERLEVEL9K_DIR_PATH_SEPARATOR="%F{red} $(print_icon 'LEFT_SUBSEGMENT_SEPARATOR') %F{black}"
|
||||
```
|
||||
To omit the first character (usually a slash that gets replaced if you set `POWERLEVEL9K_DIR_PATH_SEPARATOR`),
|
||||
you could set `POWERLEVEL9K_DIR_OMIT_FIRST_CHARACTER=true`.
|
||||
|
||||
You can also customize the leading tilde character when you are in `$HOME` using:
|
||||
```zsh
|
||||
# Double quotes are important here!
|
||||
POWERLEVEL9K_HOME_FOLDER_ABBREVIATION="%F{red} $(print_icon 'HOME_ICON') %F{black}"
|
||||
```
|
||||
You can also configure the `dir` segment to show when you are in a directory without write permissions, using the variable below.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_DIR_SHOW_WRITABLE`|`false`|If set to `true` and you are in a directory that you do not have write permissions for, this segment will display a lock icon and enter the `NOT_WRITABLE` state (which can be customized per [our usual process](https://github.com/bhilburn/powerlevel9k/wiki/Stylizing-Your-Prompt#segment-color-customization)). Note that this functionality is also available in a separate segment, `dir_writable`.|
|
||||
|
||||
If you want to customize the last directory of the path, you can now set `POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND` to a custom color and/or `POWERLEVEL9K_DIR_PATH_HIGHLIGHT_BOLD=true` to display that part in bold.
|
||||
|
||||
You can also color the separator separately by setting the color using `POWERLEVEL9K_DIR_PATH_SEPARATOR_FOREGROUND`.
|
||||
|
||||
##### disk_usage
|
||||
|
||||
The `disk_usage` segment will show the usage level of the partition that your current working directory resides in. It can be configured with the following variables.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_DISK_USAGE_ONLY_WARNING`|false|Hide the segment except when usage levels have hit warning or critical levels.|
|
||||
|`POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL`|90|The usage level that triggers a warning state.|
|
||||
|`POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL`|95|The usage level that triggers a critical state.|
|
||||
|
||||
##### host
|
||||
|
||||
The `host` segment will print the hostname.
|
||||
|
||||
You can set the `POWERLEVEL9K_HOST_TEMPLATE` variable to change how the hostname
|
||||
is displayed. See (ZSH Manual)[http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html#Login-information]
|
||||
for details. The default is set to `%m` which will show the hostname up to the
|
||||
first `.`. You can set it to `%{N}m` where N is an integer to show that many
|
||||
segments of system hostname. Setting `N` to a negative integer will show that many
|
||||
segments from the end of the hostname.
|
||||
|
||||
```
|
||||
POWERLEVEL9K_HOST_TEMPLATE="%2m"
|
||||
```
|
||||
|
||||
By default, LOCAL hosts will show the host icon and remote hosts will show the SSH icon. You can override them by setting
|
||||
```
|
||||
POWERLEVEL9K_HOST_ICON="\uF109 "
|
||||
POWERLEVEL9K_SSH_ICON="\uF489 "
|
||||
```
|
||||
|
||||
|
||||
##### ip
|
||||
|
||||
This segment tries to examine all currently used network interfaces and prints
|
||||
the first address it finds. In the case that this is not the right NIC, you can
|
||||
specify the correct network interface by setting:
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_IP_INTERFACE`|None|The NIC for which you wish to display the IP address. Example: `eth0`.|
|
||||
|
||||
##### vpn_ip
|
||||
|
||||
This segment tries to extract the VPN related IP addresses from nmcli, based on the NIC type:
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_VPN_IP_INTERFACE`|`tun`|The VPN interface.|
|
||||
|
||||
##### public_ip
|
||||
|
||||
This segment will display your public IP address. There are several methods of obtaining this
|
||||
information and by default it will try all of them starting with the most efficient. You can
|
||||
also specify which method you would like it to use. The methods available are dig using opendns,
|
||||
curl, or wget. The host used for wget and curl is http://ident.me by default but can be set to
|
||||
another host if you prefer.
|
||||
|
||||
If you activate a VPN, the icon for this segment will change to the defined VPN icon.
|
||||
|
||||
The public_ip segment will attempt to update your public IP address every 5 minutes by default(also
|
||||
configurable by the user). If you lose connection your cached IP address will be displayed until
|
||||
your timeout expires at which point every time your prompt is generated a new attempt will be made.
|
||||
Until an IP is successfully pulled the value of $POWERLEVEL9K_PUBLIC_IP_NONE will be displayed for
|
||||
this segment. If this value is empty(the default)and $POWERLEVEL9K_PUBLIC_IP_FILE is empty the
|
||||
segment will not be displayed.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_PUBLIC_IP_FILE`|'/tmp/p9k_public_ip'|This is the file your public IP is cached in.|
|
||||
|`POWERLEVEL9K_PUBLIC_IP_HOST`|'http://ident.me'|This is the default host to get your public IP.|
|
||||
|`POWERLEVEL9K_PUBLIC_IP_TIMEOUT`|300|The amount of time in seconds between refreshing your cached IP.|
|
||||
|`POWERLEVEL9K_PUBLIC_IP_METHODS`|(dig curl wget)| These methods in that order are used to refresh your IP.|
|
||||
|`POWERLEVEL9K_PUBLIC_IP_NONE`|None|The string displayed when an IP was not obtained|
|
||||
|
||||
##### load
|
||||
|
||||
Displays one of your load averages with appropriate state coloring. The thresholds are:
|
||||
- `0.7 * NUM_CORES <`: critical
|
||||
- `0.5 * NUM_CORES <`: warning
|
||||
- `less`: normal
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_LOAD_WHICH`|5|Which average to show. Possible values: 1, 5 or 15|
|
||||
|
||||
##### newline
|
||||
|
||||
Puts a newline in your prompt so you can continue using segments on the next
|
||||
line. This allows you to use segments on both lines, unlike
|
||||
`POWERLEVEL9K_PROMPT_ON_NEWLINE`, which simply separates segments from the
|
||||
prompt itself.
|
||||
|
||||
This only works on the left side. On the right side it does nothing.
|
||||
|
||||
##### nodeenv
|
||||
|
||||
Shows the currently used [nodeenv](https://github.com/ekalinin/nodeenv). To avoid
|
||||
Nodeenvs activate command from interfering with Powerlevel9k, you should set
|
||||
`NODE_VIRTUAL_ENV_DISABLE_PROMPT=1` in your `~/.zshrc`.
|
||||
|
||||
##### rbenv
|
||||
|
||||
This segment shows the version of Ruby being used when using `rbenv` to change your current Ruby stack.
|
||||
|
||||
It figures out the version being used by taking the output of the `rbenv version-name` command.
|
||||
|
||||
* If `rbenv` is not in $PATH, nothing will be shown.
|
||||
* By default, if the current local Ruby version is the same as the global Ruby version, nothing will be shown. See the configuration variable, below, to modify this behavior.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW`|`false`|Set to true if you wish to show the rbenv segment even if the current Ruby version is the same as the global Ruby version|
|
||||
|
||||
##### pyenv
|
||||
|
||||
This segment shows the version of Python being used when using `pyenv` to change your current Python stack.
|
||||
|
||||
The `PYENV_VERSION` environment variable will be used if specified. Otherwise it figures out the version being used by taking the output of the `pyenv version-name` command.
|
||||
|
||||
* If `pyenv` is not in $PATH, nothing will be shown.
|
||||
* If the current Python version is the same as the global Python version, nothing will be shown.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW`|`false`|Set to true if you wish to show the pyenv segment even if the current Python version is the same as the global Python version|
|
||||
|
||||
##### rspec_stats
|
||||
|
||||
See [Unit Test Ratios](#unit-test-ratios), below.
|
||||
|
||||
##### status
|
||||
|
||||
This segment shows the return code of the last command.
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_STATUS_CROSS`|`false`|Set to true if you wish not to show the error code when the last command returned an error and optionally hide this segment when the last command completed successfully by setting `POWERLEVEL9K_STATUS_OK` to false.|
|
||||
|`POWERLEVEL9K_STATUS_OK`|`true`|Set to true if you wish to show this segment when the last command completed successfully, false to hide it.|
|
||||
|`POWERLEVEL9K_STATUS_SHOW_PIPESTATUS`|`true`|Set to true if you wish to show the exit status for all piped commands.|
|
||||
|`POWERLEVEL9K_STATUS_HIDE_SIGNAME`|`false`|Set to true return the raw exit code (`1-255`). When set to false, values over 128 are shown as `SIGNAME(-n)` (e.g. `KILL(-9)`)|
|
||||
|
||||
##### ram
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_RAM_ELEMENTS`|Both|Specify `ram_free` or `swap_used` to only show one or the other rather than both.|
|
||||
|
||||
##### symfony2_tests
|
||||
|
||||
See [Unit Test Ratios](#unit-test-ratios), below.
|
||||
|
||||
##### time
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_TIME_FORMAT`|`'H:M:S'`|ZSH time format to use in this segment.|
|
||||
|
||||
As an example, if you wanted a reversed time format, you would use this:
|
||||
```zsh
|
||||
# Reversed time format
|
||||
POWERLEVEL9K_TIME_FORMAT='%D{%S:%M:%H}'
|
||||
```
|
||||
If you are using an "Awesome Powerline Font", you can add a time symbol to this
|
||||
segment, as well:
|
||||
```zsh
|
||||
# Output time, date, and a symbol from the "Awesome Powerline Font" set
|
||||
POWERLEVEL9K_TIME_FORMAT="%D{%H:%M:%S \uE868 %d.%m.%y}"
|
||||
```
|
||||
##### user
|
||||
|
||||
The `user` segment will print the username.
|
||||
|
||||
You can also override the icons by setting:
|
||||
|
||||
```
|
||||
POWERLEVEL9K_USER_ICON="\uF415" #
|
||||
POWERLEVEL9K_ROOT_ICON="#"
|
||||
POWERLEVEL9K_SUDO_ICON=$'\uF09C' #
|
||||
```
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`DEFAULT_USER`|None|Username to consider a "default context".|
|
||||
|`POWERLEVEL9K_ALWAYS_SHOW_USER`|`false`|Always print this segment.|
|
||||
|`POWERLEVEL9K_USER_TEMPLATE`|`%n`|Default username prompt. Refer to the [ZSH Documentation](http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html) for all possible expansions|
|
||||
|
||||
##### vcs
|
||||
|
||||
By default, the `vcs` segment will provide quite a bit of information. Further
|
||||
customization is provided via:
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_HIDE_BRANCH_ICON`|`false`|Set to `true` to hide the branch icon from the segment.|
|
||||
|`POWERLEVEL9K_SHOW_CHANGESET`|`false`|Set to `true` to display the hash / changeset in the segment.|
|
||||
|`POWERLEVEL9K_CHANGESET_HASH_LENGTH`|`12`|How many characters of the hash / changeset to display in the segment.|
|
||||
|`POWERLEVEL9K_VCS_SHOW_SUBMODULE_DIRTY`|`true`|Set to `false` to not reflect submodule status in the top-level repository prompt.|
|
||||
|`POWERLEVEL9K_VCS_HIDE_TAGS`|`false`|Set to `true` to stop tags being displayed in the segment.|
|
||||
|`POWERLEVEL9K_VCS_GIT_HOOKS`|`(vcs-detect-changes git-untracked git-aheadbehind git-stash git-remotebranch git-tagname)`|Layout of the segment for git repositories.|
|
||||
|`POWERLEVEL9K_VCS_HG_HOOKS`|`(vcs-detect-changes)`|Layout of the segment for Mercurial repositories.|
|
||||
|`POWERLEVEL9K_VCS_SVN_HOOKS`|`(vcs-detect-changes svn-detect-changes)`|Layout of the segment for SVN repositories.|
|
||||
|`POWERLEVEL9K_VCS_ACTIONFORMAT_FOREGROUND`|`red`|The color of the foreground font during actions (e.g., `REBASE`).|
|
||||
|
||||
|
||||
##### vcs symbols
|
||||
|
||||
The `vcs` segment uses various symbols to tell you the state of your repository.
|
||||
These symbols depend on your installed font and selected `POWERLEVEL9K_MODE`
|
||||
from the [Installation](#Installation) section above.
|
||||
|
||||
| `Compatible` | `Powerline` | `Awesome Powerline` | Explanation
|
||||
|--------------|---------------------|-------------------|--------------------------
|
||||
| `↑4` | `↑4` | 4 | Number of commits your repository is ahead of your remote branch
|
||||
| `↓5` | `↓5` | 5 | Number of commits your repository is behind of your remote branch
|
||||
| `⍟3` | `⍟3` | 3 | Number of stashes, here 3.
|
||||
| `●` | `●` |  | There are unstaged changes in your working copy
|
||||
| `✚` | `✚` |  | There are staged changes in your working copy
|
||||
| `?` | `?` |  | There are files in your working copy, that are unknown to your repository
|
||||
| `→` | `→` |  | The name of your branch differs from its tracking branch.
|
||||
| `☿` | `☿` |  | A mercurial bookmark is active.
|
||||
| `@` |  |  | Branch Icon
|
||||
| None | None | 2c3705 | The current commit hash. Here "2c3705"
|
||||
| None | None |  | Repository is a git repository
|
||||
| None | None |  | Repository is a Mercurial repository
|
||||
|
||||
##### vcs truncation
|
||||
|
||||
You can limit the branch name to a certain length by truncating long names.
|
||||
Customizations available are:
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_VCS_SHORTEN_LENGTH`|None|This field determines how many characters to show.|
|
||||
|`POWERLEVEL9K_VCS_SHORTEN_MIN_LENGTH`|None|This field determines minimum branch length. Branch name will be truncated if its length greater than this field.|
|
||||
|`POWERLEVEL9K_VCS_SHORTEN_STRATEGY`|None|This field determines how branch name should be truncated. See the table below for more information.|
|
||||
|`POWERLEVEL9K_SHORTEN_DELIMITER`|`...`|Delimiter to use in truncated strings. This can be any string you choose, including an empty string if you wish to have no delimiter.|
|
||||
|
||||
| Strategy Name | Description |
|
||||
|---------------|-------------|
|
||||
|`truncate_middle`|Truncates the middle part of a branch. E.g. branch name is `1234-super_super_long_branch_name`, then it will truncated to `1234-..._name`, if `POWERLEVEL9K_VCS_SHORTEN_LENGTH=5` is also set (controls the amount of characters to be left).|
|
||||
|`truncate_from_right`|Just leaves the beginning of a branch name untouched. E.g. branch name will be truncated like so: `1234-...`. How many characters will be untouched is controlled by `POWERLEVEL9K_VCS_SHORTEN_LENGTH`.|
|
||||
|
||||
For example, if you want to truncate `1234-super_super_long_branch_name` to `1234-..` and don't do it with `development`:
|
||||
```zsh
|
||||
POWERLEVEL9K_VCS_SHORTEN_LENGTH=4
|
||||
POWERLEVEL9K_VCS_SHORTEN_MIN_LENGTH=11
|
||||
POWERLEVEL9K_VCS_SHORTEN_STRATEGY="truncate_from_right"
|
||||
POWERLEVEL9K_VCS_SHORTEN_DELIMITER=".."
|
||||
```
|
||||
|
||||
##### vi_mode
|
||||
|
||||
This segment shows ZSH's current input mode. Note that this is only useful if
|
||||
you are using the [ZSH Line Editor](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html)
|
||||
(VI mode). You can enable this either by `.zshrc` configuration or using a plugin, like
|
||||
[Oh-My-Zsh's vi-mode plugin](https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/vi-mode/vi-mode.plugin.zsh).
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
|----------|---------------|-------------|
|
||||
|`POWERLEVEL9K_VI_INSERT_MODE_STRING`|`"INSERT"`|String to display while in 'Insert' mode.|
|
||||
|`POWERLEVEL9K_VI_COMMAND_MODE_STRING`|`"NORMAL"`|String to display while in 'Command' mode.|
|
||||
|
||||
To hide the segment entirely when in `INSERT` mode, set `POWERLEVEL9K_VI_INSERT_MODE_STRING=''`
|
||||
|
||||
##### virtualenv
|
||||
|
||||
This segment shows your Python [VirtualEnv](https://virtualenv.pypa.io/en/latest/). To avoid
|
||||
VirtualEnvs activate command from interfering with Powerlevel9k, you should set
|
||||
`VIRTUAL_ENV_DISABLE_PROMPT=1` in your `~/.zshrc`.
|
||||
|
||||
#### Unit Test Ratios
|
||||
|
||||
The `symfony2_tests` and `rspec_stats` segments both show a ratio of "real"
|
||||
classes vs test classes in your source code. This is just a very simple ratio,
|
||||
and does not show your code coverage or any sophisticated stats. All this does
|
||||
is count your source files and test files, and calculate the ratio between them.
|
||||
Just enough to give you a quick overview about the test situation of the project
|
||||
you are dealing with.
|
||||
|
||||
### Disabling / Enabling Powerlevel9k
|
||||
|
||||
You can disable P9k and return to a very basic prompt at any time simply by
|
||||
calling:
|
||||
|
||||
```zsh
|
||||
$ prompt_powerlevel9k_teardown
|
||||
```
|
||||
|
||||
You can then re-enable it by calling:
|
||||
|
||||
```zsh
|
||||
$ prompt_powerlevel9k_setup
|
||||
```
|
||||
|
||||
### tl; dr
|
||||
|
||||
Want to just get a quick start? Check out the [Show Off Your
|
||||
Config](https://github.com/bhilburn/powerlevel9k/wiki/Show-Off-Your-Config)
|
||||
portion of the wiki to get going.
|
||||
|
||||
[The Wiki also has a ton of other useful
|
||||
information!](https://github.com/bhilburn/powerlevel9k/wiki)
|
||||
|
||||
### License
|
||||
|
||||
Project: MIT
|
||||
|
||||
Logo: CC-BY-SA. Source repository: https://github.com/bhilburn/powerlevel9k-logo
|
69
zsh/.oh-my-zsh/custom/themes/powerlevel9k/TESTS.md
Normal file
69
zsh/.oh-my-zsh/custom/themes/powerlevel9k/TESTS.md
Normal file
@ -0,0 +1,69 @@
|
||||
# Tests
|
||||
|
||||
## Automated Tests
|
||||
|
||||
The Unit-Tests do not follow exactly the file structure of Powerlevel9k itself,
|
||||
but we try to reflect the structure as much as possible. All tests are located
|
||||
under `test/`. Segment specific tests under `test/segments/` (one file per
|
||||
segment).
|
||||
|
||||
### Installation
|
||||
|
||||
In order to execute the tests you need to install `shunit2`, which is a
|
||||
submodule. To install the submodule, you can execute
|
||||
`git submodule init && git submodule update`.
|
||||
|
||||
### Executing tests
|
||||
|
||||
The tests are shell scripts on their own. So you can execute them right away.
|
||||
To execute all tests you could just execute `./test/suite.spec`.
|
||||
|
||||
### General Test Structure
|
||||
|
||||
The tests usually have a `setUp()` function which is executed before every
|
||||
test function. Speaking of, test functions must be prefixed with `test`. In
|
||||
the tests, you can do [different Assertions](https://github.com/kward/shunit2#-asserts).
|
||||
It is always a good idea to mock the program you want to test (just have a
|
||||
look at other tests), so that the testrunner does not have to have all
|
||||
programs installed.
|
||||
|
||||
### Travis
|
||||
|
||||
We use [Travis](https://travis-ci.org/) for Continuous Integration. This
|
||||
service executes our tests after every push. For now, we need to tell travis
|
||||
where to find the tests, which is what happens in the `.travis.yml` file.
|
||||
|
||||
## Manual Testing
|
||||
|
||||
If unit tests are not sufficient (e.g. you have an issue with your prompt that
|
||||
occurs only in a specific ZSH framework) then you can use either Docker or
|
||||
or our Vagrant.
|
||||
|
||||
### Docker
|
||||
|
||||
This is the easiest to use _if_ you have Docker already installed and running.
|
||||
|
||||
The command `./test-in-docker` should make it fairly easy to get into a running
|
||||
container with the framework of your choice.
|
||||
|
||||
Examples:
|
||||
|
||||
``` zsh
|
||||
# Test Antigen with the oldest version of ZSH
|
||||
$ ./test-in-docker antigen
|
||||
```
|
||||
|
||||
``` zsh
|
||||
# Test Prezto with ZSH version 5.2
|
||||
$ ./test-in-docker --zsh 5.2 prezto
|
||||
```
|
||||
|
||||
You can get Docker at <https://www.docker.com/community-edition>.
|
||||
|
||||
**Note:** Not all frameworks work with all versions of ZSH (or the underlying OS).
|
||||
|
||||
### Vagrant
|
||||
|
||||
Currently there are two test VMs. `test-vm` is an Ubuntu machine with several
|
||||
pre-installed ZSH frameworks. And there is `test-bsd-vm` which is a FreeBSD!
|
||||
For how to run the machines see [here](test-vm/README.md).
|
421
zsh/.oh-my-zsh/custom/themes/powerlevel9k/debug/font-issues.zsh
Executable file
421
zsh/.oh-my-zsh/custom/themes/powerlevel9k/debug/font-issues.zsh
Executable file
@ -0,0 +1,421 @@
|
||||
#!/usr/bin/env zsh
|
||||
#vim:ft=zsh ts=2 sw=2 sts=2 et fenc=utf-8
|
||||
|
||||
source functions/colors.zsh
|
||||
source functions/icons.zsh
|
||||
source functions/utilities.zsh
|
||||
# Map our $OS to neofetch $os
|
||||
os="$OS"
|
||||
|
||||
|
||||
trim() {
|
||||
set -f
|
||||
# shellcheck disable=2048,2086
|
||||
set -- $*
|
||||
printf '%s\n' "${*//[[:space:]]/}"
|
||||
set +f
|
||||
}
|
||||
|
||||
trim_quotes() {
|
||||
trim_output="${1//\'}"
|
||||
trim_output="${trim_output//\"}"
|
||||
printf "%s" "$trim_output"
|
||||
}
|
||||
|
||||
get_ppid() {
|
||||
# Get parent process ID of PID.
|
||||
case "$os" in
|
||||
"Windows")
|
||||
ppid="$(ps -p "${1:-$PPID}" | awk '{printf $2}')"
|
||||
ppid="${ppid/PPID}"
|
||||
;;
|
||||
|
||||
"Linux")
|
||||
ppid="$(grep -i -F "PPid:" "/proc/${1:-$PPID}/status")"
|
||||
ppid="$(trim "${ppid/PPid:}")"
|
||||
;;
|
||||
|
||||
*)
|
||||
ppid="$(ps -p "${1:-$PPID}" -o ppid=)"
|
||||
;;
|
||||
esac
|
||||
|
||||
printf "%s" "$ppid"
|
||||
}
|
||||
|
||||
get_process_name() {
|
||||
# Get PID name.
|
||||
case "$os" in
|
||||
"Windows")
|
||||
name="$(ps -p "${1:-$PPID}" | awk '{printf $8}')"
|
||||
name="${name/COMMAND}"
|
||||
name="${name/*\/}"
|
||||
;;
|
||||
|
||||
"Linux")
|
||||
name="$(< "/proc/${1:-$PPID}/comm")"
|
||||
;;
|
||||
|
||||
*)
|
||||
name="$(ps -p "${1:-$PPID}" -o comm=)"
|
||||
;;
|
||||
esac
|
||||
|
||||
printf "%s" "$name"
|
||||
}
|
||||
|
||||
# Taken from NeoFetch (slightly modified)
|
||||
get_term() {
|
||||
local term
|
||||
|
||||
# If function was run, stop here.
|
||||
# ((term_run == 1)) && return
|
||||
|
||||
# Workaround for macOS systems that
|
||||
# don't support the block below.
|
||||
case "$TERM_PROGRAM" in
|
||||
"iTerm.app") term="iTerm2" ;;
|
||||
"Terminal.app") term="Apple Terminal" ;;
|
||||
"Hyper") term="HyperTerm" ;;
|
||||
*) term="${TERM_PROGRAM/\.app}" ;;
|
||||
esac
|
||||
|
||||
# Most likely TosWin2 on FreeMiNT - quick check
|
||||
[[ "$TERM" == "tw52" || "$TERM" == "tw100" ]] && \
|
||||
term="TosWin2"
|
||||
|
||||
[[ "$SSH_CONNECTION" ]] && \
|
||||
term="$SSH_TTY"
|
||||
|
||||
# Check $PPID for terminal emulator.
|
||||
while [[ -z "$term" ]]; do
|
||||
parent="$(get_ppid "$parent")"
|
||||
[[ -z "$parent" ]] && break
|
||||
name="$(get_process_name "$parent")"
|
||||
|
||||
case "${name// }" in
|
||||
"${SHELL/*\/}"|*"sh"|"screen"|"su"*) ;;
|
||||
|
||||
"login"*|*"Login"*|"init"|"(init)")
|
||||
term="$(tty)"
|
||||
;;
|
||||
|
||||
"ruby"|"1"|"tmux"*|"systemd"|"sshd"*|"python"*|"USER"*"PID"*|"kdeinit"*|"launchd"*)
|
||||
break
|
||||
;;
|
||||
|
||||
"gnome-terminal-") term="gnome-terminal" ;;
|
||||
"urxvtd") term="urxvt" ;;
|
||||
*"nvim") term="Neovim Terminal" ;;
|
||||
*"NeoVimServer"*) term="VimR Terminal" ;;
|
||||
*) term="${name##*/}" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Log that the function was run.
|
||||
# term_run=1
|
||||
|
||||
echo "${term}"
|
||||
}
|
||||
|
||||
get_term_font() {
|
||||
local confs term_font mateterm_config role profile xrdb child profile_filename
|
||||
local term="${1}"
|
||||
# ((term_run != 1)) && get_term
|
||||
|
||||
case "$term" in
|
||||
"alacritty"*)
|
||||
setopt nullglob
|
||||
confs=({$XDG_CONFIG_HOME,$HOME}/{alacritty,}/{.,}alacritty.ym?)
|
||||
unsetopt nullglob
|
||||
|
||||
[[ -f "${confs[1]}" ]] || return
|
||||
|
||||
term_font="$(awk -F ':|#' '/normal:/ {getline; print}' "${confs[1]}")"
|
||||
term_font="${term_font/*family:}"
|
||||
term_font="${term_font/$'\n'*}"
|
||||
term_font="${term_font/\#*}"
|
||||
;;
|
||||
|
||||
"Apple_Terminal")
|
||||
term_font="$(osascript <<END
|
||||
tell application "Terminal" to font name of window frontmost
|
||||
END
|
||||
)"
|
||||
;;
|
||||
|
||||
"iTerm2")
|
||||
# Unfortunately the profile name is not unique, but it seems to be the only thing
|
||||
# that identifies an active profile. There is the "id of current session of current win-
|
||||
# dow" though, but that does not match to a guid in the plist.
|
||||
# So, be warned, collisions may occur!
|
||||
# See: https://groups.google.com/forum/#!topic/iterm2-discuss/0tO3xZ4Zlwg
|
||||
local current_profile_name profiles_count profile_name diff_font
|
||||
|
||||
current_profile_name="$(osascript <<END
|
||||
tell application "iTerm2" to profile name \
|
||||
of current session of current window
|
||||
END
|
||||
)"
|
||||
|
||||
# Warning: Dynamic profiles are not taken into account here!
|
||||
# https://www.iterm2.com/documentation-dynamic-profiles.html
|
||||
font_file="${HOME}/Library/Preferences/com.googlecode.iterm2.plist"
|
||||
|
||||
# Count Guids in "New Bookmarks"; they should be unique
|
||||
profiles_count="$(/usr/libexec/PlistBuddy -c "Print ':New Bookmarks:'" "$font_file" 2>/dev/null | \
|
||||
grep -w -c "Guid")"
|
||||
|
||||
for ((i=0; i<profiles_count; i++)); do
|
||||
profile_name="$(/usr/libexec/PlistBuddy -c "Print ':New Bookmarks:${i}:Name:'" "$font_file" 2>/dev/null)"
|
||||
|
||||
if [[ "$profile_name" == "$current_profile_name" ]]; then
|
||||
# "Normal Font"
|
||||
term_font="$(/usr/libexec/PlistBuddy -c "Print ':New Bookmarks:${i}:Normal Font:'" \
|
||||
"$font_file" 2>/dev/null)"
|
||||
|
||||
# Font for non-ascii characters
|
||||
# Only check for a different non-ascii font, if the user checked
|
||||
# the "use a different font for non-ascii text" switch.
|
||||
diff_font="$(/usr/libexec/PlistBuddy -c "Print ':New Bookmarks:${i}:Use Non-ASCII Font:'" \
|
||||
"$font_file" 2>/dev/null)"
|
||||
|
||||
if [[ "$diff_font" == "true" ]]; then
|
||||
non_ascii="$(/usr/libexec/PlistBuddy -c "Print ':New Bookmarks:${i}:Non Ascii Font:'" \
|
||||
"$font_file" 2>/dev/null)"
|
||||
|
||||
[[ "$term_font" != "$non_ascii" ]] && \
|
||||
term_font="$term_font (normal) / $non_ascii (non-ascii)"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
;;
|
||||
|
||||
"deepin-terminal"*)
|
||||
term_font="$(awk -F '=' '/font=/ {a=$2} /font_size/ {b=$2} END {print a,b}' \
|
||||
"${XDG_CONFIG_HOME}/deepin/deepin-terminal/config.conf")"
|
||||
;;
|
||||
|
||||
"GNUstep_Terminal")
|
||||
term_font="$(awk -F '>|<' '/>TerminalFont</ {getline; f=$3}
|
||||
/>TerminalFontSize</ {getline; s=$3} END {print f,s}' \
|
||||
"${HOME}/GNUstep/Defaults/Terminal.plist")"
|
||||
;;
|
||||
|
||||
"Hyper"*)
|
||||
term_font="$(awk -F':|,' '/fontFamily/ {print $2; exit}' "${HOME}/.hyper.js")"
|
||||
term_font="$(trim_quotes "$term_font")"
|
||||
;;
|
||||
|
||||
"kitty"*)
|
||||
kitty_config="$(kitty --debug-config)"
|
||||
[[ "$kitty_config" != *font_family* ]] && return
|
||||
|
||||
term_font_size="${kitty_config/*font_size}"
|
||||
term_font_size="${term_font_size/$'\n'*}"
|
||||
term_font="${kitty_config/*font_family}"
|
||||
term_font="${term_font/$'\n'*} $term_font_size"
|
||||
;;
|
||||
|
||||
"konsole" | "yakuake")
|
||||
# Get Process ID of current konsole window / tab
|
||||
child="$(get_ppid "$$")"
|
||||
|
||||
declare -a konsole_instances; konsole_instances=( "${(@f)"$(qdbus | grep -F 'org.kde.konsole')"/ /}" )
|
||||
|
||||
for i in "${konsole_instances[@]}"; do
|
||||
declare -a konsole_sessions; konsole_sessions=( "${(@f)"$(qdbus "$i" | grep -F '/Sessions/')"}" )
|
||||
|
||||
for session in "${konsole_sessions[@]}"; do
|
||||
if ((child == $(qdbus "$i" "$session" processId))); then
|
||||
profile="$(qdbus "$i" "$session" environment |\
|
||||
awk -F '=' '/KONSOLE_PROFILE_NAME/ {print $2}')"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[[ "$profile" ]] && break
|
||||
done
|
||||
|
||||
# We could have two profile files for the same profile name, take first match
|
||||
profile_filename="$(grep -l "Name=${profile}" "$HOME"/.local/share/konsole/*.profile)"
|
||||
profile_filename="${profile_filename/$'\n'*}"
|
||||
|
||||
[[ "$profile_filename" ]] && \
|
||||
term_font="$(awk -F '=|,' '/Font=/ {print $2,$3}' "$profile_filename")"
|
||||
;;
|
||||
|
||||
"lxterminal"*)
|
||||
term_font="$(awk -F '=' '/fontname=/ {print $2; exit}' \
|
||||
"${XDG_CONFIG_HOME}/lxterminal/lxterminal.conf")"
|
||||
;;
|
||||
|
||||
"mate-terminal")
|
||||
# To get the actual config we have to create a temporarily file with the
|
||||
# --save-config option.
|
||||
mateterm_config="/tmp/mateterm.cfg"
|
||||
|
||||
# Ensure /tmp exists and we do not overwrite anything.
|
||||
if [[ -d "/tmp" && ! -f "$mateterm_config" ]]; then
|
||||
mate-terminal --save-config="$mateterm_config"
|
||||
|
||||
role="$(xprop -id "${WINDOWID}" WM_WINDOW_ROLE)"
|
||||
role="${role##* }"
|
||||
role="${role//\"}"
|
||||
|
||||
profile="$(awk -F '=' -v r="$role" \
|
||||
'$0~r {
|
||||
getline;
|
||||
if(/Maximized/) getline;
|
||||
if(/Fullscreen/) getline;
|
||||
id=$2"]"
|
||||
} $0~id {if(id) {getline; print $2; exit}}' \
|
||||
"$mateterm_config")"
|
||||
|
||||
rm -f "$mateterm_config"
|
||||
|
||||
mate_get() {
|
||||
gsettings get org.mate.terminal.profile:/org/mate/terminal/profiles/"$1"/ "$2"
|
||||
}
|
||||
|
||||
if [[ "$(mate_get "$profile" "use-system-font")" == "true" ]]; then
|
||||
term_font="$(gsettings get org.mate.interface monospace-font-name)"
|
||||
else
|
||||
term_font="$(mate_get "$profile" "font")"
|
||||
fi
|
||||
term_font="$(trim_quotes "$term_font")"
|
||||
fi
|
||||
;;
|
||||
|
||||
"mintty")
|
||||
term_font="$(awk -F '=' '!/^($|#)/ && /Font/ {printf $2; exit}' "${HOME}/.minttyrc")"
|
||||
;;
|
||||
|
||||
"pantheon"*)
|
||||
term_font="$(gsettings get org.pantheon.terminal.settings font)"
|
||||
|
||||
[[ -z "${term_font//\'}" ]] && \
|
||||
term_font="$(gsettings get org.gnome.desktop.interface monospace-font-name)"
|
||||
|
||||
term_font="$(trim_quotes "$term_font")"
|
||||
;;
|
||||
|
||||
"qterminal")
|
||||
term_font="$(awk -F '=' '/fontFamily=/ {a=$2} /fontSize=/ {b=$2} END {print a,b}' \
|
||||
"${XDG_CONFIG_HOME}/qterminal.org/qterminal.ini")"
|
||||
;;
|
||||
|
||||
"sakura"*)
|
||||
term_font="$(awk -F '=' '/^font=/ {print $2; exit}' \
|
||||
"${XDG_CONFIG_HOME}/sakura/sakura.conf")"
|
||||
;;
|
||||
|
||||
"st")
|
||||
term_font="$(ps -o command= -p "$parent" | grep -F -- "-f")"
|
||||
|
||||
if [[ "$term_font" ]]; then
|
||||
term_font="${term_font/*-f/}"
|
||||
term_font="${term_font/ -*/}"
|
||||
|
||||
else
|
||||
# On Linux we can get the exact path to the running binary through the procfs
|
||||
# (in case `st` is launched from outside of $PATH) on other systems we just
|
||||
# have to guess and assume `st` is invoked from somewhere in the users $PATH
|
||||
[[ -L "/proc/$parent/exe" ]] && binary="/proc/$parent/exe" || binary="$(type -p st)"
|
||||
|
||||
# Grep the output of strings on the `st` binary for anything that looks vaguely
|
||||
# like a font definition. NOTE: There is a slight limitation in this approach.
|
||||
# Technically "Font Name" is a valid font. As it doesn't specify any font options
|
||||
# though it is hard to match it correctly amongst the rest of the noise.
|
||||
[[ -n "$binary" ]] && \
|
||||
term_font="$(strings "$binary" | grep -F -m 1 \
|
||||
-e "pixelsize=" \
|
||||
-e "size=" \
|
||||
-e "antialias=" \
|
||||
-e "autohint=")"
|
||||
fi
|
||||
|
||||
term_font="${term_font/xft:}"
|
||||
term_font="${term_font/:*}"
|
||||
;;
|
||||
|
||||
"terminology")
|
||||
term_font="$(strings "${XDG_CONFIG_HOME}/terminology/config/standard/base.cfg" |\
|
||||
awk '/^font\.name$/{print a}{a=$0}')"
|
||||
term_font="${term_font/.pcf}"
|
||||
term_font="${term_font/:*}"
|
||||
;;
|
||||
|
||||
"termite")
|
||||
[[ -f "${XDG_CONFIG_HOME}/termite/config" ]] && \
|
||||
termite_config="${XDG_CONFIG_HOME}/termite/config"
|
||||
|
||||
term_font="$(awk -F '= ' '/\[options\]/ {
|
||||
opt=1
|
||||
}
|
||||
/^\s*font/ {
|
||||
if(opt==1) a=$2;
|
||||
opt=0
|
||||
} END {print a}' "/etc/xdg/termite/config" \
|
||||
"$termite_config")"
|
||||
;;
|
||||
|
||||
"urxvt" | "urxvtd" | "rxvt-unicode" | "xterm")
|
||||
xrdb="$(xrdb -query)"
|
||||
term_font="$(grep -im 1 -e "^${term/d}"'\**\.*font' -e '^\*font' <<< "$xrdb")"
|
||||
term_font="${term_font/*"*font:"}"
|
||||
term_font="${term_font/*".font:"}"
|
||||
term_font="${term_font/*"*.font:"}"
|
||||
term_font="$(trim "$term_font")"
|
||||
|
||||
[[ -z "$term_font" && "$term" == "xterm" ]] && \
|
||||
term_font="$(grep '^XTerm.vt100.faceName' <<< "$xrdb")"
|
||||
|
||||
term_font="$(trim "${term_font/*"faceName:"}")"
|
||||
|
||||
# xft: isn't required at the beginning so we prepend it if it's missing
|
||||
[[ "${term_font:0:1}" != "-" && "${term_font:0:4}" != "xft:" ]] && \
|
||||
term_font="xft:$term_font"
|
||||
|
||||
# Xresources has two different font formats, this checks which
|
||||
# one is in use and formats it accordingly.
|
||||
case "$term_font" in
|
||||
*"xft:"*)
|
||||
term_font="${term_font/xft:}"
|
||||
term_font="${term_font/:*}"
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
IFS=- read -r _ _ term_font _ <<< "$term_font"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
"xfce4-terminal")
|
||||
term_font="$(awk -F '=' '/^FontName/{a=$2}/^FontUseSystem=TRUE/{a=$0} END {print a}' \
|
||||
"${XDG_CONFIG_HOME}/xfce4/terminal/terminalrc")"
|
||||
|
||||
[[ "$term_font" == "FontUseSystem=TRUE" ]] && \
|
||||
term_font="$(gsettings get org.gnome.desktop.interface monospace-font-name)"
|
||||
|
||||
term_font="$(trim_quotes "$term_font")"
|
||||
|
||||
# Default fallback font hardcoded in terminal-preferences.c
|
||||
[[ -z "$term_font" ]] && term_font="Monospace 12"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "${term_font}"
|
||||
}
|
||||
|
||||
local currentTerminal=$(get_term)
|
||||
local currentFont=$(get_term_font "${currentTerminal}")
|
||||
print -P "===== Font debugging ====="
|
||||
print -P "You are using %F{blue}${currentTerminal}%f with Font %F{blue}${currentFont}%f\n"
|
||||
|
||||
if [[ $(echo "${currentFont}" | grep -c -E "Powerline|Awesome|Nerd") -eq 0 ]]; then
|
||||
print -P "%F{yellow}WARNING%f It does not seem like you use an Powerline-enabled or Awesome Terminal Font!"
|
||||
print -P "Please make sure that your font settings are correct!"
|
||||
else
|
||||
print -P "Your font settings seem to be all right. If you still have issues,"
|
||||
print -P "it is more likely to be a font issue than a Powerlevel9k related one."
|
||||
fi
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user