sistema_progs

Programas para customizar o meu entorno de traballo nos meus equipos persoais
Log | Files | Refs

prompt-git.bash (11331B)


      1 # blesh/contrib/prompt-git.bash (C) 2020-2021, akinomyoga
      2 
      3 # bleopt prompt_rps1='\q{contrib/git-info}'
      4 # bleopt prompt_rps1='\q{contrib/git-name}'
      5 # bleopt prompt_rps1='\q{contrib/git-hash}'
      6 # bleopt prompt_rps1='\q{contrib/git-branch}'
      7 # bleopt prompt_rps1='\q{contrib/git-path}'
      8 
      9 ble-import contrib/prompt-defer
     10 
     11 #------------------------------------------------------------------------------
     12 
     13 _ble_contrib_prompt_git_data=()
     14 _ble_contrib_prompt_git_base=
     15 _ble_contrib_prompt_git_base_dir=
     16 _ble_contrib_prompt_git_vars=(git_base git_base_dir hash branch)
     17 
     18 ## @fn ble/contrib/prompt-git/.check-gitdir path
     19 ##   @var[out] git_base git_base_dir
     20 function ble/contrib/prompt-git/.check-gitdir {
     21   local path=$1
     22   [[ -f $path/.git/HEAD ]] || return 1
     23   ble/prompt/unit/assign _ble_contrib_prompt_git_base     "$path"
     24   ble/prompt/unit/assign _ble_contrib_prompt_git_base_dir "$path/.git"
     25   return 0
     26 }
     27 ## @fn ble/contrib/prompt-git/.check-submodule path
     28 ##   @var[out] git_base git_base_dir
     29 function ble/contrib/prompt-git/.check-submodule {
     30   local path=$1 content
     31   [[ -f $path/.git ]] || return 1
     32   ble/util/mapfile content < "$path/.git"
     33   [[ $content == 'gitdir:'* ]] || return 1
     34   local git_base=$path
     35   local git_base_dir=${content#'gitdir:'}
     36   git_base_dir=${git_base_dir#' '}
     37   [[ $git_base_dir == /* ]] ||
     38     git_base_dir=$path/$git_base_dir
     39   [[ -f $git_base_dir/HEAD ]]
     40   ble/prompt/unit/assign _ble_contrib_prompt_git_base     "$git_base"
     41   ble/prompt/unit/assign _ble_contrib_prompt_git_base_dir "$git_base_dir"
     42   return 0
     43 }
     44 function ble/prompt/unit:_ble_contrib_prompt_git/update {
     45   ble/prompt/unit/add-hash '$PWD'
     46 
     47   type git &>/dev/null || return 1
     48   # [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) ]]
     49   local path=$PWD found=
     50   while
     51     if ble/contrib/prompt-git/.check-gitdir "$path"; then
     52       [[ $prompt_unit_changed ]]
     53       return "$?"
     54     elif ble/contrib/prompt-git/.check-submodule "$path"; then
     55       [[ $prompt_unit_changed ]]
     56       return "$?"
     57     fi
     58     [[ $path == */* ]]
     59   do path=${path%/*}; done
     60 
     61   ble/prompt/unit/assign _ble_contrib_prompt_git_base ''
     62   [[ $prompt_unit_changed ]]
     63 }
     64 
     65 ## @fn ble/contrib/prompt-git/initialize
     66 ##   @var[out] git_base git_base_dir
     67 function ble/contrib/prompt-git/initialize {
     68   ble/prompt/unit#update _ble_contrib_prompt_git
     69   ble/util/restore-vars _ble_contrib_prompt_ git_base git_base_dir
     70   [[ $git_base ]]
     71 }
     72 
     73 ## @fn ble/contrib/prompt-git/check-dirty
     74 ##   現在の working tree に編輯があるかどうかを非同期で取得します。
     75 ##   @var[in] git_base
     76 _ble_contrib_prompt_git_dirty=0
     77 ble/contrib/prompt-defer/clear _ble_contrib_prompt_git_dirty
     78 function ble/contrib/prompt-defer:_ble_contrib_prompt_git_dirty/clear { _ble_contrib_prompt_git_dirty=0; }
     79 function ble/contrib/prompt-defer:_ble_contrib_prompt_git_dirty/worker {
     80   # git 1.7.2 (2010-09) supports --ignore-submodules=untracked
     81   git status --porcelain --ignore-submodules=untracked | ble/bin/awk '
     82     /^[^ ?]./ { staged = 1;}
     83     /^.[^ ?]/ { unstaged = 1;}
     84     /^\?\?/ { untracked = 1; }
     85     END {
     86       if (unstaged) exit 1;
     87       if (staged) exit 2;
     88       if (untracked) exit 3;
     89       exit 0
     90     }
     91   '
     92 }
     93 function ble/contrib/prompt-defer:_ble_contrib_prompt_git_dirty/callback { _ble_contrib_prompt_git_dirty=$?; }
     94 function ble/contrib/prompt-git/check-dirty {
     95   [[ $_ble_contrib_prompt_git_base ]] || return 0
     96   ble/contrib/prompt-defer/submit _ble_contrib_prompt_git_dirty "$_ble_contrib_prompt_git_base" ''
     97   ble/prompt/unit/add-hash '$_ble_contrib_prompt_git_dirty'
     98   return "$_ble_contrib_prompt_git_dirty"
     99 }
    100 function ble/contrib/prompt-git/is-dirty { ble/contrib/prompt-git/check-dirty; (($?!=0&&$?!=3)); }
    101 function ble/contrib/prompt-git/get-dirty-mark {
    102   dirty_mark=
    103   ble/contrib/prompt-git/check-dirty; local ext=$?
    104   if [[ :$1: == *:colored:* ]]; then
    105     case $ext in
    106     (1) dirty_mark=$'\e[1;38:5:202m*\e[m' ;;
    107     (2) dirty_mark=$'\e[1;32m*\e[m' ;;
    108     (3) dirty_mark=$'\e[1;94m+\e[m' ;;
    109     esac
    110   else
    111     case $ext in
    112     (1) dirty_mark='*' ;;
    113     (2) dirty_mark='^' ;;
    114     (3) dirty_mark='+' ;;
    115     esac
    116   fi
    117 }
    118 
    119 ## @fn ble/contrib/prompt-git/update-head-information
    120 ##   @var[out] hash branch
    121 function ble/contrib/prompt-git/update-head-information {
    122   [[ $hash || $branch ]] && return 0
    123 
    124   local head_file=$git_base_dir/HEAD
    125   [[ -s $head_file ]] || return 0
    126   local content; ble/util/mapfile content < "$head_file"
    127 
    128   if [[ $content == *'ref: refs/heads/'* ]]; then
    129     branch=${content#*refs/heads/}
    130     content=
    131 
    132     local branch_file=$git_base_dir/refs/heads/$branch
    133     if [[ -s $branch_file ]]; then
    134       ble/util/mapfile content < "$branch_file"
    135     elif local refs_file=$git_base_dir/info/refs; [[ -s $refs_file ]]; then
    136       local lines line
    137       ble/util/mapfile lines < "$refs_file"
    138       for line in "${lines[@]}"; do
    139         if [[ $line == *["$_ble_term_IFS"]refs/heads/"$branch" ]]; then
    140           content=${line%%[$_ble_term_IFS]*}
    141           break
    142         fi
    143       done
    144     fi
    145   fi
    146 
    147   [[ $content && ! ${content//[0-9a-fA-F]} ]] && hash=$content
    148   return 0
    149 }
    150 ## @fn ble/contrib/prompt-git/get-tag-name
    151 ##   @var[out] tag
    152 function ble/contrib/prompt-git/get-tag-name {
    153   # ble/util/assign-array tag 'git describe --tags --exact-match 2>/dev/null'
    154   tag=
    155   ble/contrib/prompt-git/update-head-information # -> hash, branch
    156   [[ $hash ]] || return 1
    157 
    158   local file tagsdir=$git_base_dir/refs/tags hash1
    159   local files ret; ble/util/eval-pathname-expansion '"$tagsdir"/*'; files=("${ret[@]}")
    160   for file in "${files[@]}"; do
    161     local tag1=${file#$tagsdir/}
    162     [[ -s $file ]] || continue
    163     ble/util/mapfile hash1 < "$file"
    164     if [[ $hash1 == "$hash" ]]; then
    165       tag=$tag1
    166       return 0
    167     fi
    168   done
    169 
    170   if local refs_file=$git_base_dir/info/refs; [[ -s $refs_file ]]; then
    171     local lines line
    172     ble/util/mapfile lines < "$refs_file"
    173     for line in "${lines[@]}"; do
    174       if [[ $line == "$hash"["$_ble_term_IFS"]refs/tags/* ]]; then
    175         tag=${line##refs/tags/*}
    176         return 0
    177       fi
    178     done
    179   fi
    180 }
    181 
    182 ## @fn ble/contrib/prompt-git/get-state opts
    183 ##   @var[out] ret
    184 function ble/contrib/prompt-git/get-state {
    185   local opts=$1
    186   # https://github.com/git/git/blob/4fd6c5e44459e6444c2cd93383660134c95aabd1/contrib/completion/git-prompt.sh#L452-L475
    187   # https://github.com/git/git/blob/4fd6c5e44459e6444c2cd93383660134c95aabd1/contrib/completion/git-prompt.sh#L312-L333
    188   if [[ -d $git_base_dir/rebase-merge ]]; then
    189     ret=REBASE
    190   elif [[ -d $git_base_dir/rebase-apply ]]; then
    191     if [[ -f $git_base_dir/rebase-apply/rebasing ]]; then
    192       ret=REBASE
    193     elif [[ -f $git_base_dir/rebase-apply/applying ]]; then
    194       ret=AM
    195     else
    196       ret=AM/REBASE
    197     fi
    198   elif [[ -f $git_base_dir/MERGE_HEAD ]]; then
    199     ret=MERGING
    200   elif [[ -f $git_base_dir/CHERRY_PICK_HEAD ]]; then
    201     ret=CHERRY-PICKING
    202   elif [[ -f $git_base_dir/REVERT_HEAD ]]; then
    203     ret=REVERTING
    204   elif
    205     local todo= rest IFS=$_ble_term_IFS
    206     [[ -f $git_base_dir/sequencer/todo ]] &&
    207       ble/bash/read todo rest < "$git_base_dir/sequencer/todo"
    208     [[ $todo == p || $todo == pick ]]; then
    209     ret=CHERRY-PICKING
    210   elif [[ $todo == revert ]]; then
    211     ret=REVERTING
    212   elif [[ -f $git_base_dir/BISECT_LOG ]]; then
    213     ret=BISECTING
    214   elif ble/contrib/prompt-git/update-head-information; [[ ! $branch ]]; then
    215     ret=DETACHED
    216   elif [[ $branch && ! $hash ]]; then
    217     ret=ORPHAN
    218   else
    219     ret=
    220   fi
    221 
    222   if [[ :$opts: == *:colored:* ]]; then
    223     case $ret in
    224     (REBASE)         ret=$'\e[1;48;5;27;38;5;231m '$ret$' \e[m' ;;
    225     (AM | AM/REBASE) ret=$'\e[1;48;5;34;38;5;231m '$ret$' \e[m' ;;
    226     (MERGING)        ret=$'\e[1;48;5;172;38;5;231m '$ret$' \e[m' ;;
    227     (CHERRY-PICKING) ret=$'\e[1;48;5;200;38;5;231m '$ret$' \e[m' ;;
    228     (REVERTING)      ret=$'\e[1;48;5;124;38;5;231m '$ret$' \e[m' ;;
    229     (BISECTING)      ret=$'\e[1;48;5;93;38;5;231m '$ret$' \e[m' ;;
    230     (DETACHED)       ret=$'\e[91m'$ret$'\e[m' ;;
    231     (ORPHAN)         ret=$'\e[38;5;70m'$ret$'\e[m' ;;
    232     (?*)             ret=$'\e[1;48;5;242;38;5;231m '$ret$' \e[m' ;;
    233     esac
    234   fi
    235 
    236   [[ $ret ]]
    237 }
    238 
    239 function ble/contrib/prompt-git/describe-head {
    240   local opts=:$1:
    241   ret=
    242 
    243   local dirty_mark=
    244   [[ $opts == *:check-dirty:* ]] &&
    245     ble/contrib/prompt-git/get-dirty-mark colored
    246 
    247   ble/contrib/prompt-git/update-head-information # -> hash, branch
    248   if [[ $branch ]]; then
    249     local sgr=$'\e[1;34m' sgr0=$'\e[m'
    250     local out=$sgr$branch$sgr0
    251     if [[ $opts == *:add-hash:* && $hash ]]; then
    252       out="$out (${hash::7}$dirty_mark)"
    253     else
    254       out=$out$dirty_mark
    255     fi
    256 
    257     [[ $opts == *:check-state:* ]] &&
    258       ble/contrib/prompt-git/get-state colored &&
    259       out="$ret $out"
    260     ret=$out
    261     return 0
    262   fi
    263 
    264   local tag
    265   ble/contrib/prompt-git/get-tag-name
    266   if [[ $tag ]]; then
    267     local sgr=$'\e[1;32m' sgr0=$'\e[m'
    268     local out=$sgr$tag$sgr0
    269     [[ $opts == *:add-hash:* && $hash ]] &&
    270       out="$out ${hash::7}"
    271     out=$out$dirty_mark
    272     [[ $opts == *:check-state:* ]] &&
    273       ble/contrib/prompt-git/get-state colored &&
    274       out="$ret ($out)"
    275     ret=$out
    276     return 0
    277   fi
    278 
    279   # "master~23" 等の分かりにくい説明なのでこれは使わない
    280   # ble/util/assign-array ret 'git describe --contains --all 2>/dev/null'
    281   # if [[ $ret ]]; then
    282   #   local label_detached=$'\e[91mDETACHED\e[m'
    283   #   local sgr=$'\e[32m' sgr0=$'\e[m'
    284   #   ret="($label_detached at $sgr$ret$sgr0)"
    285   #   return 0
    286   # fi
    287 
    288   if [[ $hash ]]; then
    289     local out=${hash::7}$dirty_mark
    290     [[ $opts == *:check-state:* ]] &&
    291       ble/contrib/prompt-git/get-state colored &&
    292       out="$ret ($out)"
    293     ret=$out
    294     return 0
    295   fi
    296 
    297   ret=$'\e[91mUNKNOWN\e[m'
    298 }
    299 
    300 #------------------------------------------------------------------------------
    301 
    302 function ble/prompt/backslash:contrib/git-info {
    303   local "${_ble_contrib_prompt_git_vars[@]/%/=}" # WA #D1570 checked
    304   if ble/contrib/prompt-git/initialize; then
    305     local sgr=$'\e[1m' sgr0=$'\e[m'
    306     local name=$sgr${git_base##*?/}$sgr0
    307     local ret; ble/contrib/prompt-git/describe-head add-hash:check-dirty:check-state; local branch=$ret
    308     ble/prompt/print "$name $branch"
    309     [[ $PWD == "$git_base"/?* ]] &&
    310       ble/prompt/print " /${PWD#$git_base/}"
    311     return 0
    312   else
    313     return 1
    314   fi
    315 }
    316 function ble/prompt/backslash:contrib/git-name {
    317   local "${_ble_contrib_prompt_git_vars[@]/%/=}" # WA #D1570 checked
    318   if ble/contrib/prompt-git/initialize; then
    319     local name=${git_base%.git}
    320     name=${name%/}
    321     name=${name##*?/}
    322     ble/prompt/print "${git_base##*?/}"
    323   fi
    324 }
    325 function ble/prompt/backslash:contrib/git-hash {
    326   local "${_ble_contrib_prompt_git_vars[@]/%/=}" # WA #D1570 checked
    327   if ble/contrib/prompt-git/initialize; then
    328     ble/contrib/prompt-git/update-head-information
    329     ble/prompt/print "${hash::${1:-7}}"
    330   fi
    331 }
    332 function ble/prompt/backslash:contrib/git-branch {
    333   local "${_ble_contrib_prompt_git_vars[@]/%/=}" # WA #D1570 checked
    334   if ble/contrib/prompt-git/initialize; then
    335     local ret; ble/contrib/prompt-git/describe-head check-dirty
    336     ble/prompt/print "$ret"
    337   fi
    338 }
    339 function ble/prompt/backslash:contrib/git-path {
    340   local "${_ble_contrib_prompt_git_vars[@]/%/=}" # WA #D1570 checked
    341   if ble/contrib/prompt-git/initialize; then
    342     if [[ $PWD == "$git_base"/?* ]]; then
    343       ble/prompt/print "/${PWD#$git_base/}"
    344     elif [[ $PWD == "$git_base" ]]; then
    345       ble/prompt/print /
    346     fi
    347   fi
    348 }