Skip to content

Instantly share code, notes, and snippets.

@junegunn
Last active March 26, 2024 09:56
Show Gist options
  • Save junegunn/8b572b8d4b5eddd8b85e5f4d40f17236 to your computer and use it in GitHub Desktop.
Save junegunn/8b572b8d4b5eddd8b85e5f4d40f17236 to your computer and use it in GitHub Desktop.
# GIT heart FZF
# -------------
is_in_git_repo() {
git rev-parse HEAD > /dev/null 2>&1
}
fzf-down() {
fzf --height 50% --min-height 20 --border --bind ctrl-/:toggle-preview "$@"
}
_gf() {
is_in_git_repo || return
git -c color.status=always status --short |
fzf-down -m --ansi --nth 2..,.. \
--preview '(git diff --color=always -- {-1} | sed 1,4d; cat {-1})' |
cut -c4- | sed 's/.* -> //'
}
_gb() {
is_in_git_repo || return
git branch -a --color=always | grep -v '/HEAD\s' | sort |
fzf-down --ansi --multi --tac --preview-window right:70% \
--preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" $(sed s/^..// <<< {} | cut -d" " -f1)' |
sed 's/^..//' | cut -d' ' -f1 |
sed 's#^remotes/##'
}
_gt() {
is_in_git_repo || return
git tag --sort -version:refname |
fzf-down --multi --preview-window right:70% \
--preview 'git show --color=always {}'
}
_gh() {
is_in_git_repo || return
git log --date=short --format="%C(green)%C(bold)%cd %C(auto)%h%d %s (%an)" --graph --color=always |
fzf-down --ansi --no-sort --reverse --multi --bind 'ctrl-s:toggle-sort' \
--header 'Press CTRL-S to toggle sort' \
--preview 'grep -o "[a-f0-9]\{7,\}" <<< {} | xargs git show --color=always' |
grep -o "[a-f0-9]\{7,\}"
}
_gr() {
is_in_git_repo || return
git remote -v | awk '{print $1 "\t" $2}' | uniq |
fzf-down --tac \
--preview 'git log --oneline --graph --date=short --pretty="format:%C(auto)%cd %h%d %s" {1}' |
cut -d$'\t' -f1
}
_gs() {
is_in_git_repo || return
git stash list | fzf-down --reverse -d: --preview 'git show --color=always {1}' |
cut -d: -f1
}
if [[ $- =~ i ]]; then
bind '"\er": redraw-current-line'
bind '"\C-g\C-f": "$(_gf)\e\C-e\er"'
bind '"\C-g\C-b": "$(_gb)\e\C-e\er"'
bind '"\C-g\C-t": "$(_gt)\e\C-e\er"'
bind '"\C-g\C-h": "$(_gh)\e\C-e\er"'
bind '"\C-g\C-r": "$(_gr)\e\C-e\er"'
bind '"\C-g\C-s": "$(_gs)\e\C-e\er"'
fi
join-lines() {
local item
while read item; do
echo -n "${(q)item} "
done
}
() {
local c
for c in $@; do
eval "fzf-g$c-widget() { local result=\$(_g$c | join-lines); zle reset-prompt; LBUFFER+=\$result }"
eval "zle -N fzf-g$c-widget"
eval "bindkey '^g^$c' fzf-g$c-widget"
done
} f b t r h s
@DanSM-5
Copy link

DanSM-5 commented May 23, 2022

I'd like to ask the reason why some commands in the pipes are after fzf

Here is what I mean. Take _gb as an example.

_gb() {
  is_in_git_repo || return
  git branch -a --color=always | grep -v '/HEAD\s' | sort |
  fzf-down --ansi --multi --tac --preview-window right:70% \
    --preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" $(sed s/^..// <<< {} | cut -d" " -f1)' |
  sed 's/^..//' | cut -d' ' -f1 |
  sed 's#^remotes/##'
}

The part sed 's/^..//' | cut -d' ' -f1 | sed 's#^remotes/##' at the bottom does not make much sense to me as fzf will display the results before these commands. Is there any reason to call them at the end?

As I can see, the above command can be simplified

_gb() {
  is_in_git_repo || return
  git branch -a --color=always | grep -v '/HEAD\s' | sort |
  sed 's/^..//' | cut -d' ' -f1 |
  fzf-down --ansi --multi --tac --preview-window right:70% \
    --preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" {}' 
}

or if you want to include the sed part remove the word "remotes/"

_gb() {
  is_in_git_repo || return
  git branch -a --color=always | grep -v '/HEAD\s' | sort |
  sed 's/^..//' | cut -d' ' -f1 | sed 's#remotes/##'
  fzf-down --ansi --multi --tac --preview-window right:70% \
    --preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" {}' 
}

@rockyzhang24
Copy link

rockyzhang24 commented May 24, 2022

@DanSM-5 Pipe a command after fzf means pass the selection(s) from fzf into this command for further processing.

To implement a feature, you can process the data first and then pass them into fzf and make selections to get what you need, or you can first pass the data into fzf and then post-process the selections to get what you need.

@DanSM-5
Copy link

DanSM-5 commented May 24, 2022

@rockyzhang24 I see, I was pretty much using this for the visualization but that's a nice part too.
For the _gb function, I just noticed that moving those commands above will remove the * for the current branch. So it is likely intentional to leave it in fzf list output. I was only paying attention to the color highlight so I really missed that.

@eric-bentley
Copy link

eric-bentley commented Jun 6, 2022

If you use a non-standard diff tool (such as bcompare), you'll need to tell git to use the built-in with the --no-ext-diff flag:

_gf() {
  is_in_git_repo || return
  git -c color.status=always status --short |
  fzf-down -m --ansi --nth 2..,.. \
    --preview '(git diff --no-ext-diff --color=always -- {-1} | sed 1,4d; cat {-1})' |
  cut -c4- | sed 's/.* -> //'
}

@junegunn
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment