A1P4P
gh api
-H "Accept: application/vnd.github+json"
-H "X-GitHub-Api-Version: 2022-11-28"
/user
#!/bin/zsh
declare -A prop flag msg message_prev declare -a keys actprop actkey conf act
echo | espeak 2> /dev/null quiet=$?
action= while [[ "$1" =~ "^--" ]]; do case $1 in '--quiet') quiet=1; shift;; '--verbose') verbose=1; shift;; '--action') action=$2; shift 2;; *) echo error: unknown argument $1 break ;; esac done
ask() { test "$action" && REPLY=$action && return read -r -k "?$1" }
reset-actions() { actprop=() actkey=() conf=() act=() restart=1 }
reset() { prop=() flag=() msg=() keys=() reset-actions restart=0 }
prop() { k=$2 flag[$k]="$1" keys+=($k) msg[$k]="${3/%1/$k}" prop[$k]="$4" shift 4 [ "$1" ] && echo unclaimed "$@" }
prop_git() { test "$verbose" && echo checking $2: git $4 prop "$1" "$2" "$3" "$(zsh -c "git $4" 2>/dev/null)" }
rep() { prop_git "l" "$1" "$2" "$3" }
prs() { prop_git "s" "$1" "$2" "$3" }
acti()
{
test "$verbose" && echo acti "$@"
actprop+=(
actg() { acti $1 $2 $3 "git $4" }
out() { local key=$1 local prefix=$2 local message=$3 echo "$prefix \e[1m$message\e[0m" [ $quiet != 0 ] && return test "$action" && return test "$message_prev[$key]" = "$message" && return espeak -v en+f4 "$message" 2> /dev/null & message_prev[$key]="$message" }
print-actions() { for n ({1..${#actprop}}) { [[ $actprop[$n] != $key ]] && continue kk+=($actkey[$n]) actk[$actkey[$n]]=$act[$n] echo " ﹝$actkey[$n]﹞ — $conf[$n]" } echo " [Enter] - continue" echo " * - exit" }
perform-actions() { declare -a kk declare -A actk for key ($keys) { let n+=1 v=$prop[$key] [[ ! $flag[$key] =~ a ]] && continue [[ -z "$v" || "$v" == 0 ]] && continue
out "What to do with" "$(prop-print $key)?"
print-actions
ask '>'
[ "$REPLY" = $'\n' ] && continue
echo
[ -z "$actk[$REPLY]" ] && return 1
test "$verbose" && echo "$actk[$REPLY]"
eval $actk[$REPLY]
test "$action" && exit
break
}
test "$action" && exit
[ $#actk -eq 0 ] && echo "Nothing to do" && exit
echo
}
prop-print()
{
key=$1
m=$msg[$key]
if [[
summary() { local m="Summary: head '$prop[head]' on branch '$prop[branch]'" for key in $keys; do [[ ! $flag[$key] =~ s ]] && continue p="$prop[$key]" [ -z "$p" -o "$p" = 0 ] && continue m+=", $(prop-print $key)" done out "" "" $m }
report()
{
summary
for key in
git-get-file() { REPLY= cat $prop[root]/.git/$1 2> /dev/null | read echo "$REPLY" }
git-status-parse()
{
local mm=$(git-get-file MERGE_MSG)
local mh=$(git-get-file MERGE_HEAD)
if [[ "$mh" ]]; then prop[in_progress]=merge; fi
local oc=$(git-get-file rebase-apply/original-commit)
local fcm=$(git-get-file rebase-apply/final-commit)
local sc=$(git-get-file rebase-merge/stopped-sha) # also REBASE_HEAD) #
local sm=$(git-get-file rebase-merge/message) # also MERGE_MSG
test "$oc" && actg in_progress s "Show current patch '$fcm'" "show $oc"
test "$sc" && actg in_progress s "Show current patch '$sm'" "show $sc"
git status --untracked-files=no | while read a; do
if [[ "$a" =~ "currently (.) commit ([^.])" ]]; then prop[in_progress]=$match[1]; hash=$match[2]; fi
[[ "$a" =~ "currently ([^ ])" ]] && prop[in_progress]=$match[1]
[[ "$a" =~ "while ([^ ])" ]] && prop[in_progress]=$match[1]
[[ "$a" =~ 'git ((.) --edit-todo)' ]] && actg in_progress v "view and edit $match[2] todo list" "$match[1]"
[[ "$a" =~ 'git ((.) --continue)' ]] && actg in_progress c "continue $match[2]" "$match[1]"
[[ "$fcm" && "$a" =~ 'git ((.) --skip)' ]] && actg in_progress S "skip current patch '$fcm'" "$match[1]"
[[ "$sm" && "$a" =~ 'git ((.) --skip)' ]] && actg in_progress S "skip current patch '$sm'" "$match[1]"
[[ "$a" =~ 'git ((.*) --abort)' ]] && actg in_progress C "cancel $match[2]" "$match[1]"
done
}
in_progress()
{
local conflict_pattern='^(^<<<<<<< )|(^=======$)|(^>>>>>>> )'
#prs conflicted '%1 file(s)' "grep -e '$conflict_pattern' --
# | wc -l | ( read c; echo $(((c+2)/3)))"
grep -s -r -n -e "$conflict_pattern" --
}
select_branch() { select a in $(git branch --format='%(refname:short)'); do break; done [ $quiet = 0 ] && espeak -v en+f4 "Selected branch $a" 2> /dev/null & echo $a }
diff_to_quickfix() { local file= while read a;do [[ "$a" =~ "^+++ (.)" ]] && file=$match [[ "$a" =~ "^@@.+([0-9]+)" ]] && echo "$file:$match:$a" done #perl -ne '/^+++ (.+)/ && { $f="$1"};/@@.*+(\d+)/ &&print "$f:$1:$_\n"' }
xdg-open() {
test "$SSH_CLIENT" \
&& ssh ${SSH_CLIENT%% *} xdg-open $PWD/modifications.html \
|| /usr/bin/xdg-open "$@"
}
modifications() { reset-actions git diff --stat actg modified ' ' "show diff of the modifications" 'diff' actg modified 'l' "run difftool" "difftool" actg modified 'h' "html view" "diff --relative --no-prefix | pygmentize -l diff -O full -o modifications.html; xdg-open modifications.html" print-actions ask '>' [ "$REPLY" = $'\n' ] && return
[ -z "$actk[$REPLY]" ] && return
test "$verbose" && echo "$actk[$REPLY]"
eval $actk[$REPLY]
test "$action" && exit
break
}
modified() { ## checks for modified and staged(cached) files and what to do with them
prs modified '%1 file(s)' 'ls-files --modified | wc -l'
acti modified ' ' "show modifications" "modifications"
actg modified u "update stage with modifications selectively" 'add --patch'
actg modified s "push into stash selectively" 'stash push --patch'
actg modified d "discard selectively" 'checkout --patch'
acti modified 'e' "edit with vim quickfix" "vim -q <(git diff -U0 --relative --no-prefix | diff_to_quickfix)"
actg modified 'g' "gui" "gui"
prs staged '%1 file(s)' 'diff --name-only --staged | wc -l'
actg staged ' ' "show" 'diff --staged'
acti staged e "edit with vim quickfix" "vim -q <(git diff -U0 --staged --relative --no-prefix | diff_to_quickfix)"
actg staged c "commit" commit
actg staged g "run GUI commit tools" citool
actg staged r "unstage (reset) modifications selectively" 'reset --patch'
actg staged R "unstage (reset) all modifications" reset
}
head-branch()
{
rep head "%1 '@v'" 'describe --all --contains --always'
acti head ' ' 'print report' report
actg head c 'show the last commit' 'show --stat'
acti head e "edit with vim quickfix" "vim -q <(git show -U0 --relative --no-prefix | diff_to_quickfix)"
actg head l 'list recent log'
rep branch "%1 '@v'" 'rev-parse --abbrev-ref HEAD'
rep local_branches '%1' 'branch | wc -l'
branches_format=(
$'%(committerdate:short) - %(align:left,25)%(committerdate:relative)'
$'%(upstream:trackshort) %(end)'
$'%(align:left,25)\e[37m%(objectname:short)\e[0m \e[1m%(refname:short)\e[0m%(end)'
$'%(subject)\e[3;37m. %(authorname)\e[0m')
actg local_branches ' ' 'list' "for-each-ref --sort=committerdate --format '$branches_format' refs/heads"
actg local_branches 's' 'switch' 'switch $(select_branch)'
rep remote_branches '%1' 'branch --remote | wc -l'
actg remote_branches ' ' 'list recent' "branch --remotes --sort=committerdate --format '$branches_format' | tail -n $((LINES-2))"
prs stashes '%1' 'stash list | wc -l'
actg stashes ' ' "show and list stash" 'stash show; git stash list --stat'
actg stashes o "pop from stash" 'stash pop'
actg stashes d "drop the topmost stash" 'stash drop'
rep commited '%1' 'log -1 --format="%ar"'
}
remote()
{
rep remote '%1' "config --get branch.$prop[branch].remote"
local h=$git_dir/FETCH_HEAD
test -e $h &&
prop l fetch_age '%1 (min) @v'
"$(((date +%s
- stat -c %Z $h
) / 60))"
#((fetch= ! ${#fetch_age} || $prop[fetch_age] > 10 || $prop[fetch_age] < 0))
if [[ -z "$prop[fetch_age]" || "$prop[fetch_age]" -gt 360 || "$prop[fetch_age]" -lt 0 ]]; then
git fetch --all --prune
prs gone_branches '%1' "branch -vv | grep ': gone]' | wc -l"
actg gone_branches 'd' "delete merged gone branches" 'branch -d $(git branch --format="%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(else)%(end)")'
actg gone_branches 'D' "delete all gone branches" 'branch -D $(git branch --format="%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(else)%(end)")'
fi
if [[ $prop[remote] ]]; then
prs local_commits '%1' 'rev-list --count @{push}..'
actg local_commits ' ' "list" "log @{push}.."
actg local_commits p "Push to remote" push
rep remote_commits '%1' 'rev-list --count HEAD..@{u}'
actg remote_commits ' ' "list" 'log --stat HEAD..@{u}'
actg remote_commits l "pull from remote" 'pull --autostash'
else # when there is no remote assigned
rep head_remote '%1' "config --get branch.$prop[head].remote"
if hr=$prop[head_remote] && [ $prop[branch] != HEAD ]; then
echo head_remote $prop[head_remote]
echo hr $hr
echo branch $prop[branch]
actg branch p "Push new branch to remote $hr" "push --set-upstream $hr $prop[branch]"
fi
fi
}
action_itemes() { rep action_itemes '%1' 'grep --no-messages --max-depth=2 -w -eTODO -eFIXME | wc -l' actg action_itemes ' ' list 'grep --no-messages -w -n -eTODO -eFIXME' acti action_itemes 'e' edit 'vim -q <(git grep -w -n -eTODO -eFIXME)' }
untracked()
{
prs untracked '%1 file(s)' 'ls-files --others --exclude-standard --directory| wc -l'
actg untracked ' ' "list" 'status --untracked-files=normal'
actg untracked a "add and stage" 'add --interactive'
actg untracked c "cleanup" 'clean --interactive -d'
acti untracked r "remove selectively" 'rm --recursive --interactive=always $(git ls-files --others --directory --exclude-standard --exclude .gitignore)'
actg untracked i "ignore"
'ls-files --others --directory --exclude-standard --exclude .gitignore
>> .gitignore'
}
git-general() { # rep - property for report only, prs - long and short git_dir=$(git rev-parse --git-dir) rep root '%1 @v' 'rev-parse --show-toplevel'
in_progress
modified
head-branch
remote
action_itemes
untracked
}
gitw-start()
{
case
fail() { let fails+=1 set | grep HIST history setopt echo }
[ "$1" = unit-tests ] && { local fails=0 #d=$(mktemp -d) d=/tmp/git-wizard-test rm -rf $d mkdir $d pushd $d touch empty HISTFILE=qqq SAVEHIST=3 set -o pipefail git-wizard --action y | grep Initialized || fail ls -A #git-wizard popd #rm -rf $d echo Fails: $fails exit $fails }
if [