From b9c0e7b58fbd18e0c040b5fc827196261f6e71ff Mon Sep 17 00:00:00 2001 From: carlfriedrich Date: Wed, 3 Aug 2022 17:23:01 +0200 Subject: [PATCH] gcp: interactively select target branch When we want to perform a cherry pick, we had to pass the target branch as an argument to gcp. Forgit, however, aims to make git selections interactive where possible. Hence it seems natural to select the target branch interactively as well. --- conf.d/forgit.plugin.fish | 44 +++++++++++++++++++++++++++++++++----- forgit.plugin.zsh | 45 ++++++++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/conf.d/forgit.plugin.fish b/conf.d/forgit.plugin.fish index e941e77..6b1b32d 100644 --- a/conf.d/forgit.plugin.fish +++ b/conf.d/forgit.plugin.fish @@ -374,9 +374,43 @@ function forgit::cherry::pick -d "git cherry-picking" --argument-names 'target' -m -0 --tiebreak=index $FORGIT_CHERRY_PICK_FZF_OPTS " - git cherry "$base" "$target" --abbrev -v | forgit::reverse_lines | - env FZF_DEFAULT_OPTS="$opts" fzf | cut -d' ' -f2 | forgit::reverse_lines | - xargs -I% git cherry-pick % + set fzf_selection (git cherry "$base" "$target" --abbrev -v | forgit::reverse_lines | + env FZF_DEFAULT_OPTS="$opts" fzf) + + set fzf_exitval $status + test $fzf_exitval != 0 && return $fzf_exitval + set commits (echo "$fzf_selection" | forgit::reverse_lines | cut -d' ' -f2) + + git cherry-pick $commits + +end + +function forgit::cherry::pick::from::branch -d "git cherry-picking with interactive branch selection" + forgit::inside_work_tree || return 1 + + set preview "git log {1} $forgit_log_preview_options" + + set opts " + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index --header-lines=1 + --preview=\"$preview\" + $FORGIT_CHERRY_PICK_FROM_BRANCH_FZF_OPTS + " + + set cmd "git branch --color=always --all | LC_ALL=C sort -k1.1,1.1 -rs" + + # loop until either the branch selector is closed or a commit to be cherry + # picked has been selected from within a branch + while true + + set branch (eval "$cmd" | FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $1}') + test -z "$branch" && return 1 + + forgit::cherry::pick "$branch" + + set exitval $status + test $exitval != 130 && return $exitval + end end function forgit::fixup -d "git fixup" @@ -640,9 +674,9 @@ if test -z "$FORGIT_NO_ALIASES" end if test -n "$forgit_cherry_pick" - alias $forgit_cherry_pick 'forgit::cherry::pick' + alias $forgit_cherry_pick 'forgit::cherry::pick::from::branch' else - alias gcp 'forgit::cherry::pick' + alias gcp 'forgit::cherry::pick::from::branch' end if test -n "$forgit_rebase" diff --git a/forgit.plugin.zsh b/forgit.plugin.zsh index 157296e..658b21a 100755 --- a/forgit.plugin.zsh +++ b/forgit.plugin.zsh @@ -169,7 +169,7 @@ forgit::clean() { } forgit::cherry::pick() { - local base target preview opts + local base target preview opts fzf_selection fzf_exitval base=$(git branch --show-current) [[ -z $1 ]] && echo "Please specify target branch" && return 1 target="$1" @@ -180,9 +180,44 @@ forgit::cherry::pick() { -m -0 --tiebreak=index $FORGIT_CHERRY_PICK_FZF_OPTS " - git cherry "$base" "$target" --abbrev -v | forgit::reverse_lines | - FZF_DEFAULT_OPTS="$opts" fzf | cut -d' ' -f2 | forgit::reverse_lines | - xargs -I% git cherry-pick % + fzf_selection=$(git cherry "$base" "$target" --abbrev -v | forgit::reverse_lines | + FZF_DEFAULT_OPTS="$opts" fzf) + fzf_exitval=$? + [[ $fzf_exitval != 0 ]] && return $fzf_exitval + + commits=() + while IFS="" read -r line + do + commits+=("$line") + done < <(echo "$fzf_selection" | forgit::reverse_lines | cut -d' ' -f2) + + git cherry-pick "${commits[@]}" +} + +forgit::cherry::pick::from::branch() { + forgit::inside_work_tree || return 1 + [[ $# -ne 0 ]] && { git checkout -b "$@"; return $?; } + local cmd preview opts branch exitval + cmd="git branch --color=always --all | LC_ALL=C sort -k1.1,1.1 -rs" + preview="git log {1} $forgit_log_preview_options" + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index --header-lines=1 + --preview=\"$preview\" + $FORGIT_CHERRY_PICK_FROM_BRANCH_FZF_OPTS + " + # loop until either the branch selector is closed or a commit to be cherry + # picked has been selected from within a branch + while true + do + branch="$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $1}')" + [[ -z "$branch" ]] && return 1 + + forgit::cherry::pick "$branch" + + exitval=$? + [[ $exitval != 130 ]] && return $exitval + done } forgit::rebase() { @@ -458,7 +493,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then alias "${forgit_checkout_tag:-gct}"='forgit::checkout::tag' alias "${forgit_clean:-gclean}"='forgit::clean' alias "${forgit_stash_show:-gss}"='forgit::stash::show' - alias "${forgit_cherry_pick:-gcp}"='forgit::cherry::pick' + alias "${forgit_cherry_pick:-gcp}"='forgit::cherry::pick::from::branch' alias "${forgit_rebase:-grb}"='forgit::rebase' alias "${forgit_fixup:-gfu}"='forgit::fixup' alias "${forgit_blame:-gbl}"='forgit::blame'