espelhamento de https://github.com/DanielFGray/fzf-scripts
Você não pode selecionar mais de 25 tópicos
Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
293 linhas
11 KiB
293 linhas
11 KiB
#!/usr/bin/env bash |
|
|
|
# /usr/share/bash-completion/completions/git |
|
# https://github.com/junegunn/fzf/wiki/examples#git |
|
|
|
# declare BOLD=$(tput bold || tput md) |
|
declare c_reset=$(tput sgr0) |
|
declare c_green=$(tput setaf 2 || tput AF 2) |
|
declare c_red=$(tput setaf 1 || tput AF 1) |
|
|
|
has() { # {{{ |
|
command -v "$1" &> /dev/null |
|
} |
|
# }}} |
|
|
|
ask() { # {{{ |
|
read -r -n1 -p "$* " ans |
|
echo |
|
[[ ${ans^} == Y* ]] |
|
} |
|
# }}} |
|
|
|
err() { # {{{ |
|
printf "${c_red}%s${c_reset}" "$*" >&2 |
|
} |
|
# }}} |
|
|
|
die() { # {{{ |
|
[[ -n "$1" ]] && err "$1" |
|
exit 1 |
|
} |
|
# }}} |
|
|
|
has fzf || die 'fzf not found' |
|
# [[ -d "$PWD/.git" ]] || die 'not a git repo' |
|
|
|
fzf() { # {{{ |
|
local prompt |
|
if [[ $1 == --prompt=* ]]; then |
|
prompt="${1##*=}>" |
|
shift |
|
fi |
|
branch=$(git status 2> /dev/null | sed 's/On branch />/;q') |
|
opts=( +s -e -i --reverse --cycle --prompt="fzgit${branch}>${prompt} " ) |
|
[[ -v FZMP_FZF_OPTIONS ]] && opts=( $FZMP_FZF_OPTIONS ) |
|
command fzf "${opts[@]}" \ |
|
--inline-info \ |
|
--ansi \ |
|
--no-clear \ |
|
"$@" |
|
} |
|
# }}} |
|
|
|
declare -A git_cmds_descriptions=( # {{{ |
|
['add']='Add file contents to the index' |
|
['am']='Apply a series of patches from a mailbox' |
|
['annotate']='Annotate file lines with commit information' |
|
['apply']='Apply a patch to files and/or to the index' |
|
['archive']='Create an archive of files from a named tree' |
|
['bisect']='Find by binary search the change that introduced a bug' |
|
['blame']='Show what revision and author last modified each line of a' |
|
['branch']='List, create, or delete branches' |
|
['bundle']='Move objects and refs by archive' |
|
['cat-file']='Provide content or type and size information for' |
|
['check-attr']='Display gitattributes information' |
|
['check-ignore']='Debug gitignore / exclude files' |
|
['check-mailmap']='Show canonical names and email addresses of' |
|
['check-ref-format']='Ensures that a reference name is well formed' |
|
['checkout']='Checkout a branch or paths to the working tree' |
|
['checkout-index']='Copy files from the index to the working tree' |
|
['cherry']='Find commits yet to be applied to upstream' |
|
['cherry-pick']='Apply the changes introduced by some existing commits' |
|
['citool']='Graphical alternative to git-commit' |
|
['clean']='Remove untracked files from the working tree' |
|
['clone']='Clone a repository into a new directory' |
|
['column']='Display data in columns' |
|
['commit']='Record changes to the repository' |
|
['commit-tree']='Create a new commit object' |
|
['config']='Get and set repository or global options' |
|
['count-objects']='Count unpacked number of objects and their disk' |
|
['credential']='Retrieve and store user credentials' |
|
['daemon']='A really simple server for Git repositories' |
|
['describe']='Show the most recent tag that is reachable from a commit' |
|
['diff']='Show changes between commits, commit and working tree, etc' |
|
['diff-files']='Compares files in the working tree and the index' |
|
['diff-index']='Compare a tree to the working tree or index' |
|
['diff-tree']='Compares the content and mode of blobs found via two' |
|
['difftool']='Show changes using common diff tools' |
|
['fast-export']='Git data exporter' |
|
['fast-import']='Backend for fast Git data importers' |
|
['fetch']='Download objects and refs from another repository' |
|
['fetch-pack']='Receive missing objects from another repository' |
|
['filter-branch']='Rewrite branches' |
|
['fmt-merge-msg']='Produce a merge commit message' |
|
['for-each-ref']='Output information on each ref' |
|
['format-patch']='Prepare patches for e-mail submission' |
|
['fsck']='Verifies the connectivity and validity of the objects in the' |
|
['fsck-objects']='Verifies the connectivity and validity of the' |
|
['gc']='Cleanup unnecessary files and optimize the local repository' |
|
['get-tar-commit-id']='Extract commit ID from an archive created using' |
|
['grep']='Print lines matching a pattern' |
|
['gui']='A portable graphical interface to Git' |
|
['hash-object']='Compute object ID and optionally creates a blob from' |
|
['help']='Display help information about Git' |
|
['http-backend']='Server side implementation of Git over HTTP' |
|
['http-fetch']='Download from a remote Git repository via HTTP' |
|
['http-push']='Push objects over HTTP/DAV to another repository' |
|
['imap-send']='Send a collection of patches from stdin to an IMAP' |
|
['index-pack']='Build pack index file for an existing packed archive' |
|
['init']='Create an empty Git repository or reinitialize an existing' |
|
['init-db']='Creates an empty Git repository' |
|
['instaweb']='Instantly browse your working repository in gitweb' |
|
['log']='Show commit logs' |
|
['ls-files']='Show information about files in the index and the' |
|
['ls-remote']='List references in a remote repository' |
|
['ls-tree']='List the contents of a tree object' |
|
['mailinfo']='Extracts patch and authorship from a single e-mail' |
|
['mailsplit']='Simple UNIX mbox splitter program' |
|
['merge']='Join two or more development histories together' |
|
['merge-base']='Find as good common ancestors as possible for a merge' |
|
['merge-file']='Run a three-way file merge' |
|
['merge-index']='Run a merge for files needing merging' |
|
['merge-one-file']='The standard helper program to use with' |
|
['merge-tree']='Show three-way merge without touching index' |
|
['mergetool']='Run merge conflict resolution tools to resolve merge' |
|
['mktag']='Creates a tag object' |
|
['mktree']='Build a tree-object from ls-tree formatted text' |
|
['mv']='Move or rename a file, a directory, or a symlink' |
|
['notes']='Add or inspect object notes' |
|
['pack-objects']='Create a packed archive of objects' |
|
['pack-redundant']='Find redundant pack files' |
|
['pack-refs']='Pack heads and tags for efficient repository access' |
|
['patch-id']='Compute unique ID for a patch' |
|
['prune']='Prune all unreachable objects from the object database' |
|
['prune-packed']='Remove extra objects that are already in pack files' |
|
['pull']='Fetch from and integrate with another repository or a local' |
|
['push']='Update remote refs along with associated objects' |
|
['quiltimport']='Applies a quilt patchset onto the current branch' |
|
['read-tree']='Reads tree information into the index' |
|
['rebase']='Forward-port local commits to the updated upstream head' |
|
['receive-pack']='Receive what is pushed into the repository' |
|
['reflog']='Manage reflog information' |
|
['relink']='Hardlink common objects in local repositories' |
|
['remote']='Manage set of tracked repositories' |
|
['remote-ext']='Bridge smart transport to external command.' |
|
['remote-fd']='Reflect smart transport stream back to caller' |
|
['repack']='Pack unpacked objects in a repository' |
|
['replace']='Create, list, delete refs to replace objects' |
|
['request-pull']='Generates a summary of pending changes' |
|
['rerere']='Reuse recorded resolution of conflicted merges' |
|
['reset']='Reset current HEAD to the specified state' |
|
['rev-list']='Lists commit objects in reverse chronological order' |
|
['rev-parse']='Pick out and massage parameters' |
|
['revert']='Revert some existing commits' |
|
['rm']='Remove files from the working tree and from the index' |
|
['send-pack']='Push objects over Git protocol to another repository' |
|
['sh-i18n--envsubst']="Git's own envsubst(1) for i18n fallbacks" |
|
['shell']='Restricted login shell for Git-only SSH access' |
|
['shortlog']="Summarize 'git log' output" |
|
['show']='Show various types of objects' |
|
['show-branch']='Show branches and their commits' |
|
['show-index']='Show packed archive index' |
|
['show-ref']='List references in a local repository' |
|
['stage']='Add file contents to the staging area' |
|
['stash']='Stash the changes in a dirty working directory away' |
|
['status']='Show the working tree status' |
|
['stripspace']='Remove unnecessary whitespace' |
|
['submodule']='Initialize, update or inspect submodules' |
|
['subtree']='Merge subtrees together and split repository into' |
|
['symbolic-ref']='Read, modify and delete symbolic refs' |
|
['tag']='Create, list, delete or verify a tag object signed with GPG' |
|
['unpack-file']="Creates a temporary file with a blob's contents" |
|
['unpack-objects']='Unpack objects from a packed archive' |
|
['update-index']='Register file contents in the working tree to the' |
|
['update-ref']='Update the object name stored in a ref safely' |
|
['update-server-info']='Update auxiliary info file to help dumb' |
|
['upload-archive']='Send archive back to git-archive' |
|
['upload-pack']='Send objects packed back to git-fetch-pack' |
|
['var']='Show a Git logical variable' |
|
['verify-commit']='Check the GPG signature of commits' |
|
['verify-pack']='Validate packed Git archive files' |
|
['verify-tag']='Check the GPG signature of tags' |
|
['web--browse']='Git helper script to launch a web browser' |
|
['whatchanged']='Show logs with difference each commit introduces' |
|
['write-tree']='Create a tree object from the current index' |
|
) |
|
# }}} |
|
|
|
declare -A implemented_git_cmds=( |
|
['stash']='git_stash' |
|
['add']='git_add' |
|
['checkout']='git_checkout' |
|
['commit']='git commit -v' |
|
['push']='git push' |
|
['log']='git_log' |
|
) |
|
|
|
git_log() { # {{{ |
|
local show="git show --color=always \"\$(grep -m1 -o \"[a-f0-9]\{7\}\" <<< {})\"" |
|
fzf --prompt='log' -e --no-sort --tiebreak=index \ |
|
--bind="enter:execute:$show | less -R" \ |
|
--preview="$show" \ |
|
< <(git log --graph --color=always \ |
|
--format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@") |
|
} |
|
# }}} |
|
|
|
git_checkout() { # {{{ |
|
local list response key branch header |
|
list=$(git branch --all --color -vv; git tag) || return 1 |
|
mapfile -t response < <(fzf --prompt='checkout' \ |
|
--header="$header" --expect=ctrl-x <<< "$list") |
|
key="${response[0]}" |
|
branch=$(perl -pe 's/^\*?\s*(remotes\/[^\/]*\/)?([^ ]+).*/\2/' <<< "${response[1]}") |
|
git checkout "$branch" || return 1 |
|
} |
|
# }}} |
|
|
|
git_add() { # {{{ |
|
local out response query key header |
|
header='use ctrl-p to add in patch mode' |
|
while out=$(git ls-files -mo --exclude-standard | |
|
fzf --prompt='add' --tac --multi \ |
|
--header="$header" --query="$query" --print-query \ |
|
--preview='git diff --color=always {} 2>&1' \ |
|
--preview-window=up \ |
|
--bind=ctrl-p:accept --expect=ctrl-p) |
|
do |
|
mapfile -t response <<< "$out" |
|
query="${response[0]}" && unset response[0] |
|
key="${response[1]}" && unset response[1] |
|
[[ "${#response[@]}" == 0 ]] && continue |
|
if [[ "$key" == 'ctrl-p' ]]; then |
|
git add -p "${response[@]}" < /dev/tty |
|
else |
|
git add "${response[@]}" |
|
fi |
|
done |
|
} |
|
# }}} |
|
|
|
git_stash() { # {{{ |
|
local out response query key sha header |
|
header='use ctrl-d to show a diff or ctrl-b to create a new branch' |
|
while out=$(git stash list \ |
|
--pretty="%C(yellow)%h %>(14)%Cgreen%cr %C(blue)%gs" | |
|
fzf --prompt='stash' --no-sort --header="$header" \ |
|
--query="$query" --print-query \ |
|
--preview='git diff --color {1}' \ |
|
--expect=ctrl-d,ctrl-b) |
|
do |
|
mapfile -t response <<< "$out" |
|
query="${response[0]}" && unset response[0] |
|
key="${response[1]}" && unset response[1] |
|
sha="${response[-1]}" |
|
sha="${sha%% *}" |
|
[[ -z "$sha" ]] && continue |
|
case "$key" in |
|
'ctrl-d') git diff "$sha" --color=always | less -R ;; |
|
'ctrl-b') git stash branch "stash-$sha" "$sha" ;; |
|
*) git stash show -p "$sha" --color=always | less -R ;; |
|
esac |
|
done |
|
} |
|
# }}} |
|
|
|
pick_cmd() { |
|
for c in "${!implemented_git_cmds[@]}"; do |
|
printf '%s%-15s%s -- %s\n' "${c_green}" "$c" "${c_reset}" "${git_cmds_descriptions[$c]}" |
|
done | fzf | awk '{print $1}' |
|
} |
|
|
|
main() { |
|
local pick |
|
while pick=$(pick_cmd); do |
|
if [[ -n $pick ]] && has "${implemented_git_cmds[${pick%% *}]}"; then |
|
${implemented_git_cmds[$pick]} |
|
else |
|
break |
|
fi |
|
done |
|
} |
|
|
|
reset_screen() { |
|
tput rmcup |
|
} |
|
|
|
finish() { |
|
reset_screen |
|
} |
|
|
|
trap finish EXIT SIGINT SIGTERM |
|
main
|
|
|