From 2218cbdca83ae8ca98bb4d07c89ef303f3641347 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Sun, 3 Nov 2024 14:48:15 -0500 Subject: [PATCH 01/11] feat: add interactive git worktree operations --- bin/git-forgit | 98 +++++++++++++++++++++++++++++++++++++++ conf.d/forgit.plugin.fish | 4 ++ forgit.plugin.zsh | 24 ++++++++++ 3 files changed, 126 insertions(+) diff --git a/bin/git-forgit b/bin/git-forgit index 4aebe59a..29ab816b 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -40,6 +40,7 @@ $FORGIT_FZF_DEFAULT_OPTS _forgit_warn() { printf "%b[Warn]%b %s\n" '\e[0;33m' '\e[0m' "$@" >&2; } _forgit_info() { printf "%b[Info]%b %s\n" '\e[0;32m' '\e[0m' "$@" >&2; } +_forgit_inside_git_dir() { git rev-parse --is_inside-git-dir >/dev/null; } _forgit_inside_work_tree() { git rev-parse --is-inside-work-tree >/dev/null; } # tac is not available on OSX, tail -r is not available on Linux, so we use either of them _forgit_reverse_lines() { tac 2> /dev/null || tail -r; } @@ -1002,6 +1003,98 @@ _forgit_ignore_clean() { [[ -d "$FORGIT_GI_REPO_LOCAL" ]] && rm -rf "$FORGIT_GI_REPO_LOCAL" } +_forgit_worktree_preview() { + local sha + # trailing space in grep to avoid matching worktrees with a common path + sha=$(git worktree list | grep "$1 " | awk '{print $2}') + # bare git-dir has no history + [[ "$sha" == "(bare)" ]] && return + _forgit_worktree_preview_git_opts=() + _forgit_parse_array _forgit_worktree_preview_git_opts "$FORGIT_WORKTREE_PREVIEW_GIT_OPTS" + # the trailing '--' ensures that this works for branches that have a name + # that is identical to a file + git log "$sha" "${_forgit_worktree_preview_git_opts[@]}" -- +} + +_forgit_worktree_jump() { + _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 + local count tree opts + count=$(git worktree list | wc -l) + + [[ $count -eq 1 ]] && return 1 + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index + --preview=\"$FORGIT worktree_preview {1}\" + $FORGIT_WORKTREE_JUMP_FZF_OPTS + " + + tree=$(git worktree list | awk '{print $1}' | FZF_DEFAULT_OPTS="$opts" fzf) + [[ -z "$tree" ]] && return 1 + echo "$tree" +} + +_forgit_git_worktree_lock() { + _forgit_worktree_lock_git_opts=() + _forgit_parse_array _forgit_worktree_lock_git_opts "$FORGIT_WORKTREE_LOCK_GIT_OPTS" + git worktree lock "${_forgit_worktree_lock_git_opts[@]}" "$@" +} + +_forgit_worktree_lock() { + _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 + local tree opts + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s -m --tiebreak=index + --preview=\"$FORGIT worktree_preview {1}\" + $FORGIT_WORKTREE_LOCK_FZF_OPTS + " + + tree=$(git worktree list | awk '{print $1}' | grep -v "(bare)" | FZF_DEFAULT_OPTS="$opts" fzf) + [[ -z "$tree" ]] && return 1 + _forgit_git_worktree_lock "$tree" +} + +_forgit_git_worktree_remove() { + _forgit_worktree_remove_git_opts=() + _forgit_parse_array _forgit_worktree_remove_git_opts "$FORGIT_WORKTREE_REMOVE_GIT_OPTS" + git worktree remove "${_forgit_worktree_remove_git_opts[@]}" "$@" +} + +_forgit_worktree_remove() { + _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 + local tree opts + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s -m --tiebreak=index --header-lines=1 + --preview=\"$FORGIT worktree_preview {1}\" + $FORGIT_WORKTREE_REMOVE_FZF_OPTS + " + + tree=$(git worktree list | awk '{print $1}' | grep -v "(bare)" | FZF_DEFAULT_OPTS="$opts" fzf) + [[ -z "$tree" ]] && return 1 + _forgit_git_worktree_remove "$tree" +} + +_forgit_worktree_unlock() { + _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 + local tree opts + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s -m --tiebreak=index --header-lines=1 + --preview=\"$FORGIT worktree_preview {1}\" + $FORGIT_WORKTREE_UNLOCK_FZF_OPTS + " + + tree=$(git worktree list | awk '{print $1}' | grep -v "(bare)" | FZF_DEFAULT_OPTS="$opts" fzf) + [[ -z "$tree" ]] && return 1 + git worktree unlock "$tree" +} + public_commands=( "add" "blame" @@ -1023,12 +1116,17 @@ public_commands=( "revert_commit" "stash_show" "stash_push" + "worktree_jump" + "worktree_lock" + "worktree_remove" + "worktree_unlock" ) private_commands=( "add_preview" "blame_preview" "branch_preview" + "worktree_preview" "checkout_commit_preview" "checkout_file_preview" "cherry_pick_from_branch_preview" diff --git a/conf.d/forgit.plugin.fish b/conf.d/forgit.plugin.fish index 18761d04..edab6dea 100644 --- a/conf.d/forgit.plugin.fish +++ b/conf.d/forgit.plugin.fish @@ -51,4 +51,8 @@ if test -z "$FORGIT_NO_ALIASES" abbr -a -- (string collect $forgit_revert_commit; or string collect "grc") git-forgit revert_commit abbr -a -- (string collect $forgit_blame; or string collect "gbl") git-forgit blame abbr -a -- (string collect $forgit_checkout_tag; or string collect "gct") git-forgit checkout_tag + abbr -a -- (string collect $forgit_worktree_jump; or string collect "gwj") 'set tree (git-forgit worktree_jump); test -n "$tree"; and cd "$tree"' + abbr -a -- (string collect $forgit_worktree_lock; or string collect "gwl") git-forgit worktree_lock + abbr -a -- (string collect $forgit_worktree_remove; or string collect "gwr") git-forgit worktree_remove + abbr -a -- (string collect $forgit_worktree_unlock; or string collect "gwu") git-forgit worktree_unlock end diff --git a/forgit.plugin.zsh b/forgit.plugin.zsh index b6d4a5ed..253a40f9 100755 --- a/forgit.plugin.zsh +++ b/forgit.plugin.zsh @@ -137,6 +137,22 @@ forgit::ignore::clean() { "$FORGIT" ignore_clean "$@" } +forgit::worktree::jump() { + cd "$("$FORGIT" worktree_jump "$@")" || exit +} + +forgit::worktree::lock() { + "$FORGIT" worktree_lock "$@" +} + +forgit::worktree::remove() { + "$FORGIT" worktree_remove "$@" +} + +forgit::worktree::unlock() { + "$FORGIT" worktree_unlock "$@" +} + # register aliases # shellcheck disable=SC2139 if [[ -z "$FORGIT_NO_ALIASES" ]]; then @@ -160,6 +176,10 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then export forgit_rebase="${forgit_rebase:-grb}" export forgit_fixup="${forgit_fixup:-gfu}" export forgit_blame="${forgit_blame:-gbl}" + export forgit_worktree_jump="${forgit_worktree_jump:-gwj}" + export forgit_worktree_lock="${forgit_worktree_lock:-gwl}" + export forgit_worktree_remove="${forgit_worktree_remove:-gwr}" + export forgit_worktree_unlock="${forgit_worktree_unlock:-gwu}" alias "${forgit_add}"='forgit::add' alias "${forgit_reset_head}"='forgit::reset::head' @@ -180,5 +200,9 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then alias "${forgit_rebase}"='forgit::rebase' alias "${forgit_fixup}"='forgit::fixup' alias "${forgit_blame}"='forgit::blame' + alias "${forgit_worktree_jump}"='forgit::worktree::jump' + alias "${forgit_worktree_lock}"='forgit::worktree::lock' + alias "${forgit_worktree_remove}"='forgit::worktree::remove' + alias "${forgit_worktree_unlock}"='forgit::worktree::unlock' fi From 525441f7543f460eaae7df1095ae92263d2a4995 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Sun, 3 Nov 2024 14:38:52 -0500 Subject: [PATCH 02/11] docs: update README to include worktree operations --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index c0f24ccc..a6c9867f 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,14 @@ Then add the following to your shell's config file: - **Interactive `git commit --fixup && git rebase -i --autosquash` selector** (`gfu`) +- **Interactive `git worktree list` selector** (`gwj`) + +- **Interactive `git worktree lock ` selector** (`gwl`) + +- **Interactive `git worktree remove ` selector** (`gwr`) + +- **Interactive `git worktree unlock ` selector** (`gwu`) + # ⌨ Keybindings | Key | Action | @@ -188,6 +196,10 @@ forgit_cherry_pick=gcp forgit_rebase=grb forgit_blame=gbl forgit_fixup=gfu +forgit_worktree_jump=gwj +forgit_worktree_lock=gwl +forgit_worktree_remove=gwr +forgit_worktree_unlock=gwu ``` ## git integration @@ -246,6 +258,10 @@ These are passed to the according `git` calls. | `gbl` | `FORGIT_BLAME_GIT_OPTS` | | `gfu` | `FORGIT_FIXUP_GIT_OPTS` | | `gcp` | `FORGIT_CHERRY_PICK_GIT_OPTS` | +| `gwj` | `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | +| `gwl` | `FORGIT_WORKTREE_LOCK_GIT_OPTS`, `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | +| `gwr` | `FORGIT_WORKTREE_REMOVE_GIT_OPTS`, `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | +| `gwu` | `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | ## pagers @@ -300,6 +316,10 @@ Customizing fzf options for each command individually is also supported: | `gbl` | `FORGIT_BLAME_FZF_OPTS` | | `gfu` | `FORGIT_FIXUP_FZF_OPTS` | | `gcp` | `FORGIT_CHERRY_PICK_FZF_OPTS` | +| `gwj` | `FORGIT_WORKTREE_JUMP_FZF_OPTS` | +| `gwl` | `FORGIT_WORKTREE_LOCK_FZF_OPTS` | +| `gwr` | `FORGIT_WORKTREE_REMOVE_FZF_OPTS` | +| `gwu` | `FORGIT_WORKTREE_UNLOCK_FZF_OPTS` | Complete loading order of fzf options is: From 7e50ba63bbaf6fadc6b7ba0896d7cce40ac45a87 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Wed, 13 Nov 2024 17:51:10 -0500 Subject: [PATCH 03/11] fix: typos --- bin/git-forgit | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/git-forgit b/bin/git-forgit index 29ab816b..5b8c7213 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -40,7 +40,7 @@ $FORGIT_FZF_DEFAULT_OPTS _forgit_warn() { printf "%b[Warn]%b %s\n" '\e[0;33m' '\e[0m' "$@" >&2; } _forgit_info() { printf "%b[Info]%b %s\n" '\e[0;32m' '\e[0m' "$@" >&2; } -_forgit_inside_git_dir() { git rev-parse --is_inside-git-dir >/dev/null; } +_forgit_inside_git_dir() { git rev-parse --is-inside-git-dir >/dev/null; } _forgit_inside_work_tree() { git rev-parse --is-inside-work-tree >/dev/null; } # tac is not available on OSX, tail -r is not available on Linux, so we use either of them _forgit_reverse_lines() { tac 2> /dev/null || tail -r; } @@ -1005,7 +1005,7 @@ _forgit_ignore_clean() { _forgit_worktree_preview() { local sha - # trailing space in grep to avoid matching worktrees with a common path + # trailing space in grep to avoid matching worktrees with a common path sha=$(git worktree list | grep "$1 " | awk '{print $2}') # bare git-dir has no history [[ "$sha" == "(bare)" ]] && return From a3399018c12e87c9d6ac606c76c15cad94ad8a3e Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Wed, 13 Nov 2024 19:22:43 -0500 Subject: [PATCH 04/11] fix: address reviewer comments --- bin/git-forgit | 62 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/bin/git-forgit b/bin/git-forgit index 5b8c7213..775bbb4d 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -1003,24 +1003,31 @@ _forgit_ignore_clean() { [[ -d "$FORGIT_GI_REPO_LOCAL" ]] && rm -rf "$FORGIT_GI_REPO_LOCAL" } +_forgit_filter_existing_paths() { + while read -r path; do + [[ -d "$path" ]] && echo "$path" + done +} + _forgit_worktree_preview() { local sha # trailing space in grep to avoid matching worktrees with a common path sha=$(git worktree list | grep "$1 " | awk '{print $2}') - # bare git-dir has no history - [[ "$sha" == "(bare)" ]] && return - _forgit_worktree_preview_git_opts=() - _forgit_parse_array _forgit_worktree_preview_git_opts "$FORGIT_WORKTREE_PREVIEW_GIT_OPTS" + if [[ "$sha" == "(bare)" ]]; then + printf "%b(bare)%b %s\n" '\e[0;33m' '\e[0m' 'No history for git dir' + return + fi # the trailing '--' ensures that this works for branches that have a name # that is identical to a file - git log "$sha" "${_forgit_worktree_preview_git_opts[@]}" -- + git log "$sha" "${_forgit_log_preview_options[@]}" -- } _forgit_worktree_jump() { _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 - local count tree opts - count=$(git worktree list | wc -l) + local worktree_list count tree opts + worktree_list=$(git worktree list | grep -vE "prunable$" | awk '{print $1}' | _forgit_filter_existing_paths) + count=$(echo "$worktree_list" | wc -l) [[ $count -eq 1 ]] && return 1 opts=" @@ -1030,7 +1037,7 @@ _forgit_worktree_jump() { $FORGIT_WORKTREE_JUMP_FZF_OPTS " - tree=$(git worktree list | awk '{print $1}' | FZF_DEFAULT_OPTS="$opts" fzf) + tree=$(echo "$worktree_list" | FZF_DEFAULT_OPTS="$opts" fzf) [[ -z "$tree" ]] && return 1 echo "$tree" } @@ -1043,16 +1050,21 @@ _forgit_git_worktree_lock() { _forgit_worktree_lock() { _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 + if [[ $# -ne 0 ]]; then + git worktree lock "$@" + worktree_lock_status=$? + return $worktree_lock_status + fi local tree opts opts=" $FORGIT_FZF_DEFAULT_OPTS - +s -m --tiebreak=index + +s +m --tiebreak=index --preview=\"$FORGIT worktree_preview {1}\" $FORGIT_WORKTREE_LOCK_FZF_OPTS " - tree=$(git worktree list | awk '{print $1}' | grep -v "(bare)" | FZF_DEFAULT_OPTS="$opts" fzf) + tree=$(git worktree list | grep -vE "\(bare\)|locked" | awk '{print $1}' | FZF_DEFAULT_OPTS="$opts" fzf) [[ -z "$tree" ]] && return 1 _forgit_git_worktree_lock "$tree" } @@ -1065,32 +1077,50 @@ _forgit_git_worktree_remove() { _forgit_worktree_remove() { _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 - local tree opts + if [[ $# -ne 0 ]]; then + git worktree remove "$@" + worktree_remove_status=$? + return $worktree_remove_status + fi + local worktree_list tree opts + worktree_list=$(git worktree list | grep -v "(bare)") + + count=$(echo "$worktree_list" | wc -l) + [[ $count -eq 1 ]] && return 1 opts=" $FORGIT_FZF_DEFAULT_OPTS - +s -m --tiebreak=index --header-lines=1 + +s +m --tiebreak=index --preview=\"$FORGIT worktree_preview {1}\" $FORGIT_WORKTREE_REMOVE_FZF_OPTS " - tree=$(git worktree list | awk '{print $1}' | grep -v "(bare)" | FZF_DEFAULT_OPTS="$opts" fzf) + tree=$(echo "$worktree_list" | awk '{print $1}' | FZF_DEFAULT_OPTS="$opts" fzf) [[ -z "$tree" ]] && return 1 _forgit_git_worktree_remove "$tree" } _forgit_worktree_unlock() { _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 - local tree opts + if [[ $# -ne 0 ]]; then + git worktree unlock "$@" + worktree_unlock_status=$? + return $worktree_unlock_status + fi + local worktree_list tree opts + + worktree_list=$(git worktree list | grep -v "(bare)" | grep -E "locked$") + count=$(echo "$worktree_list" | wc -l) + [[ $count -eq 0 ]] && return 1 opts=" $FORGIT_FZF_DEFAULT_OPTS - +s -m --tiebreak=index --header-lines=1 + +s +m --tiebreak=index --preview=\"$FORGIT worktree_preview {1}\" $FORGIT_WORKTREE_UNLOCK_FZF_OPTS " - tree=$(git worktree list | awk '{print $1}' | grep -v "(bare)" | FZF_DEFAULT_OPTS="$opts" fzf) + tree=$(echo "$worktree_list" | awk '{print $1}' | FZF_DEFAULT_OPTS="$opts" fzf) [[ -z "$tree" ]] && return 1 git worktree unlock "$tree" } From 260341835a00c24353ac4b1a15fc105661666a11 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Sun, 17 Nov 2024 16:31:42 -0500 Subject: [PATCH 05/11] refactor: rename worktree_jump to worktree_select --- README.md | 4 ++-- bin/git-forgit | 6 +++--- conf.d/forgit.plugin.fish | 3 ++- forgit.plugin.zsh | 8 +++++++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a6c9867f..10ab7ec7 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ forgit_cherry_pick=gcp forgit_rebase=grb forgit_blame=gbl forgit_fixup=gfu -forgit_worktree_jump=gwj +forgit_worktree_select=gws forgit_worktree_lock=gwl forgit_worktree_remove=gwr forgit_worktree_unlock=gwu @@ -316,7 +316,7 @@ Customizing fzf options for each command individually is also supported: | `gbl` | `FORGIT_BLAME_FZF_OPTS` | | `gfu` | `FORGIT_FIXUP_FZF_OPTS` | | `gcp` | `FORGIT_CHERRY_PICK_FZF_OPTS` | -| `gwj` | `FORGIT_WORKTREE_JUMP_FZF_OPTS` | +| `gws` | `FORGIT_WORKTREE_SELECT_FZF_OPTS` | | `gwl` | `FORGIT_WORKTREE_LOCK_FZF_OPTS` | | `gwr` | `FORGIT_WORKTREE_REMOVE_FZF_OPTS` | | `gwu` | `FORGIT_WORKTREE_UNLOCK_FZF_OPTS` | diff --git a/bin/git-forgit b/bin/git-forgit index 775bbb4d..662d8852 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -1022,7 +1022,7 @@ _forgit_worktree_preview() { git log "$sha" "${_forgit_log_preview_options[@]}" -- } -_forgit_worktree_jump() { +_forgit_worktree_select() { _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 local worktree_list count tree opts worktree_list=$(git worktree list | grep -vE "prunable$" | awk '{print $1}' | _forgit_filter_existing_paths) @@ -1034,7 +1034,7 @@ _forgit_worktree_jump() { $FORGIT_FZF_DEFAULT_OPTS +s +m --tiebreak=index --preview=\"$FORGIT worktree_preview {1}\" - $FORGIT_WORKTREE_JUMP_FZF_OPTS + $FORGIT_WORKTREE_SELECT_FZF_OPTS " tree=$(echo "$worktree_list" | FZF_DEFAULT_OPTS="$opts" fzf) @@ -1146,7 +1146,7 @@ public_commands=( "revert_commit" "stash_show" "stash_push" - "worktree_jump" + "worktree_select" "worktree_lock" "worktree_remove" "worktree_unlock" diff --git a/conf.d/forgit.plugin.fish b/conf.d/forgit.plugin.fish index edab6dea..eadc438b 100644 --- a/conf.d/forgit.plugin.fish +++ b/conf.d/forgit.plugin.fish @@ -51,7 +51,8 @@ if test -z "$FORGIT_NO_ALIASES" abbr -a -- (string collect $forgit_revert_commit; or string collect "grc") git-forgit revert_commit abbr -a -- (string collect $forgit_blame; or string collect "gbl") git-forgit blame abbr -a -- (string collect $forgit_checkout_tag; or string collect "gct") git-forgit checkout_tag - abbr -a -- (string collect $forgit_worktree_jump; or string collect "gwj") 'set tree (git-forgit worktree_jump); test -n "$tree"; and cd "$tree"' + abbr -a -- (string collect $forgit_worktree_select; or string collect "gwj") git-forgit worktree_select + abbr -a -- (string collect $forgit_worktree_jump; or string collect "gwj") 'set tree (git-forgit worktree_select); test -n "$tree"; and cd "$tree"' abbr -a -- (string collect $forgit_worktree_lock; or string collect "gwl") git-forgit worktree_lock abbr -a -- (string collect $forgit_worktree_remove; or string collect "gwr") git-forgit worktree_remove abbr -a -- (string collect $forgit_worktree_unlock; or string collect "gwu") git-forgit worktree_unlock diff --git a/forgit.plugin.zsh b/forgit.plugin.zsh index 253a40f9..98bb2e14 100755 --- a/forgit.plugin.zsh +++ b/forgit.plugin.zsh @@ -137,8 +137,12 @@ forgit::ignore::clean() { "$FORGIT" ignore_clean "$@" } +forgit::worktree::select() { + "$FORGIT" worktree_select "$@" +} + forgit::worktree::jump() { - cd "$("$FORGIT" worktree_jump "$@")" || exit + cd "$(forgit::worktree::select "$@")" || exit } forgit::worktree::lock() { @@ -176,6 +180,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then export forgit_rebase="${forgit_rebase:-grb}" export forgit_fixup="${forgit_fixup:-gfu}" export forgit_blame="${forgit_blame:-gbl}" + export forgit_worktree_select="${forgit_worktree_select:-gws}" export forgit_worktree_jump="${forgit_worktree_jump:-gwj}" export forgit_worktree_lock="${forgit_worktree_lock:-gwl}" export forgit_worktree_remove="${forgit_worktree_remove:-gwr}" @@ -200,6 +205,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then alias "${forgit_rebase}"='forgit::rebase' alias "${forgit_fixup}"='forgit::fixup' alias "${forgit_blame}"='forgit::blame' + alias "${forgit_worktree_select}"='forgit::worktree::select' alias "${forgit_worktree_jump}"='forgit::worktree::jump' alias "${forgit_worktree_lock}"='forgit::worktree::lock' alias "${forgit_worktree_remove}"='forgit::worktree::remove' From e2764ec4dea721077825c285885438ace6ecebce Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Sat, 23 Nov 2024 11:37:45 -0500 Subject: [PATCH 06/11] feat: add fish shell completions --- completions/git-forgit.fish | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/completions/git-forgit.fish b/completions/git-forgit.fish index 72c896ba..86db2b4d 100644 --- a/completions/git-forgit.fish +++ b/completions/git-forgit.fish @@ -8,7 +8,7 @@ function __fish_forgit_needs_subcommand for subcmd in add blame branch_delete checkout_branch checkout_commit checkout_file checkout_tag \ cherry_pick cherry_pick_from_branch clean diff fixup ignore log reflog rebase reset_head \ - revert_commit stash_show stash_push + revert_commit stash_show stash_push worktree_select worktree_lock worktree_remove worktree_unlock if contains -- $subcmd (commandline -opc) return 1 end @@ -42,6 +42,10 @@ complete -c git-forgit -n __fish_forgit_needs_subcommand -a reset_head -d 'git r complete -c git-forgit -n __fish_forgit_needs_subcommand -a revert_commit -d 'git revert commit selector' complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_show -d 'git stash viewer' complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_push -d 'git stash push selector' +complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree_select -d 'git worktree selector' +complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree_lock -d 'git worktree lock selector' +complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree_remove -d 'git worktree remove selector' +complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree_unlock -d 'git worktree unlock selector' complete -c git-forgit -n '__fish_seen_subcommand_from add' -a "(complete -C 'git add ')" complete -c git-forgit -n '__fish_seen_subcommand_from branch_delete' -a "(__fish_git_local_branches)" @@ -59,3 +63,7 @@ complete -c git-forgit -n '__fish_seen_subcommand_from reset_head' -a "(__fish_g complete -c git-forgit -n '__fish_seen_subcommand_from revert_commit' -a "(__fish_git_commits)" complete -c git-forgit -n '__fish_seen_subcommand_from stash_show' -a "(__fish_git_complete_stashes)" complete -c git-forgit -n '__fish_seen_subcommand_from stash_push' -a "(__fish_git_files modified deleted modified-staged-deleted)" +complete -c git-forgit -n '__fish_seen_subcommand_from worktree_select' -a "(__fish_git_complete_worktrees)" +complete -c git-forgit -n '__fish_seen_subcommand_from worktree_lock' -a "(complete -C 'git worktree lock ')" +complete -c git-forgit -n '__fish_seen_subcommand_from worktree_remove' -a "(complete -C 'git worktree remove ')" +complete -c git-forgit -n '__fish_seen_subcommand_from worktree_unlock' -a "(complete -C 'git worktree unlock ')" From 36fd50515303097babfa348a694f151aee88e748 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Sun, 1 Dec 2024 12:32:13 -0500 Subject: [PATCH 07/11] feat: add zsh shell completions --- completions/_git-forgit | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/completions/_git-forgit b/completions/_git-forgit index b26e4dbf..251d0631 100644 --- a/completions/_git-forgit +++ b/completions/_git-forgit @@ -18,6 +18,10 @@ _git-stash-show() { _alternative "files:filename:($(git stash list | sed -n -e 's/:.*//p'))" } +_git-worktrees() { + _alternative "worktrees:worktree:($(git worktree list --porcelain | awk '/worktree/ {print $2}'))" +} + # The completions for git already define a _git-diff completion function, but # it provides the wrong results when called from _git-forgit because it heavily # depends on the context it's been called from (usage of $curcontext and @@ -77,6 +81,10 @@ _git-forgit() { 'revert_commit:git revert commit selector' 'stash_show:git stash viewer' 'stash_push:git stash push selector' + 'worktree_select:git worktree selector' + 'worktree_lock:git worktree lock selector' + 'worktree_remove:git worktree remove selector' + 'worktree_unlock:git worktree unlock selector' ) _describe -t commands 'git forgit' subcommands ;; @@ -97,6 +105,10 @@ _git-forgit() { reset_head) _git-staged ;; revert_commit) __git_recent_commits ;; stash_show) _git-stash-show ;; + worktree_select) _git-worktrees ;; + worktree_lock) _git-worktrees ;; + worktree_remove) _git-worktrees ;; + worktree_unlock) _git-worktrees ;; esac } @@ -122,6 +134,10 @@ compdef _git-rebase forgit::rebase compdef _git-staged forgit::reset::head compdef __git_recent_commits forgit::revert::commit compdef _git-stash-show forgit::stash::show +compdef _git-worktrees forgit::worktree::select +compdef _git-worktrees forgit::worktree::lock +compdef _git-worktrees forgit::worktree::remove +compdef _git-worktrees forgit::worktree::unlock # this is the case of calling the command and pressing tab # the very first time of a shell session, we have to manually From bcdd3a1e776d9fb72fab819eca00d4f19d55c294 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Sun, 1 Dec 2024 12:40:30 -0500 Subject: [PATCH 08/11] feat: add bash shell completions --- completions/git-forgit.bash | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/completions/git-forgit.bash b/completions/git-forgit.bash index 80a443f0..32e767bb 100755 --- a/completions/git-forgit.bash +++ b/completions/git-forgit.bash @@ -35,6 +35,11 @@ _git_stash_show() __gitcomp_nl "$(__git stash list | sed -n -e 's/:.*//p')" } +_git_worktrees() +{ + __gitcomp_nl "$(git worktree list --porcelain | awk '/worktree/ {print $2}')" +} + # Completion for git-forgit # This includes git aliases, e.g. "alias.cb=forgit checkout_branch" will # correctly complete available branches on "git cb". @@ -77,6 +82,10 @@ _git_forgit() revert_commit stash_show stash_push + worktree_select + worktree_lock + worktree_remove + worktree_unlock " case ${cword} in @@ -102,6 +111,10 @@ _git_forgit() reset_head) _git_reset ;; revert_commit) _git_revert ;; stash_show) _git_stash_show ;; + worktree_select) _git_worktrees ;; + worktree_lock) _git_worktrees ;; + worktree_remove) _git_worktrees ;; + worktree_unlock) _git_worktrees ;; esac ;; *) @@ -136,6 +149,10 @@ then __git_complete forgit::reset::head _git_reset __git_complete forgit::revert::commit _git_revert __git_complete forgit::stash::show _git_stash_show + __git_complete forgit::worktree::select _git_worktrees + __git_complete forgit::worktree::lock _git_worktrees + __git_complete forgit::worktree::remove _git_worktrees + __git_complete forgit::worktree::unlock _git_worktrees # Completion for forgit plugin shell aliases if [[ -z "$FORGIT_NO_ALIASES" ]]; then @@ -155,5 +172,9 @@ then __git_complete "${forgit_reset_head}" _git_reset __git_complete "${forgit_revert_commit}" _git_revert __git_complete "${forgit_stash_show}" _git_stash_show + __git_complete "${forgit_worktree_select}" _git_worktrees + __git_complete "${forgit_worktree_lock}" _git_worktrees + __git_complete "${forgit_worktree_remove}" _git_worktrees + __git_complete "${forgit_worktree_unlock}" _git_worktrees fi fi From a0dc12703fa2bcb47acf6ed5f7833e41c2a09d22 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Wed, 18 Dec 2024 11:30:54 -0500 Subject: [PATCH 09/11] feat: add flag parsing for git worktree lock --- bin/git-forgit | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/bin/git-forgit b/bin/git-forgit index 662d8852..bd62090a 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -1050,12 +1050,23 @@ _forgit_git_worktree_lock() { _forgit_worktree_lock() { _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 + + local tree opts reason if [[ $# -ne 0 ]]; then - git worktree lock "$@" - worktree_lock_status=$? - return $worktree_lock_status + if [[ "$1" == "--reason" ]]; then + if [[ -z "$2" ]]; then + _forgit_warn "Option \`--reason' requires a value" + return 1 + fi + reason="$2" + shift 2 + fi + + if [[ $# -eq 0 ]] || _forgit_contains_non_flags "$@"; then + git worktree lock "$@" --reason "$reason" + return $? + fi fi - local tree opts opts=" $FORGIT_FZF_DEFAULT_OPTS @@ -1066,7 +1077,12 @@ _forgit_worktree_lock() { tree=$(git worktree list | grep -vE "\(bare\)|locked" | awk '{print $1}' | FZF_DEFAULT_OPTS="$opts" fzf) [[ -z "$tree" ]] && return 1 - _forgit_git_worktree_lock "$tree" + + if [[ -z "$reason" ]]; then + _forgit_git_worktree_lock "$tree" + else + _forgit_git_worktree_lock "$tree" --reason "$reason" + fi } _forgit_git_worktree_remove() { From 99d05c541fd67b8e885e550d59e0869fdd7e3061 Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Wed, 18 Dec 2024 12:08:37 -0500 Subject: [PATCH 10/11] feat: add flag parsing for git worktree remove --- bin/git-forgit | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/bin/git-forgit b/bin/git-forgit index bd62090a..0b0612ab 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -1093,12 +1093,30 @@ _forgit_git_worktree_remove() { _forgit_worktree_remove() { _forgit_inside_work_tree || _forgit_inside_git_dir || return 1 - if [[ $# -ne 0 ]]; then - git worktree remove "$@" + local worktree_list tree opts force_flags + + force_flags=() + + while (("$#")); do + case "$1" in + -f | --force) + force_flags+=("$1") + shift + ;; + -*) shift ;; + *) + tree="$1" + shift + ;; + esac + done + + if [[ -n "$tree" ]]; then + _forgit_git_worktree_remove "${force_flags[@]}" "$tree" worktree_remove_status=$? return $worktree_remove_status fi - local worktree_list tree opts + worktree_list=$(git worktree list | grep -v "(bare)") count=$(echo "$worktree_list" | wc -l) @@ -1113,7 +1131,7 @@ _forgit_worktree_remove() { tree=$(echo "$worktree_list" | awk '{print $1}' | FZF_DEFAULT_OPTS="$opts" fzf) [[ -z "$tree" ]] && return 1 - _forgit_git_worktree_remove "$tree" + _forgit_git_worktree_remove "${force_flags[@]}" "$tree" } _forgit_worktree_unlock() { From 6a2257cf716be8427e2fe6ff0009cfae9b6fbc0e Mon Sep 17 00:00:00 2001 From: Sufien Tout Date: Tue, 24 Dec 2024 15:44:05 -0500 Subject: [PATCH 11/11] docs: include gws and gwj aliases together --- README.md | 106 +++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 10ab7ec7..a1822e2e 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,9 @@ Then add the following to your shell's config file: - **Interactive `git commit --fixup && git rebase -i --autosquash` selector** (`gfu`) -- **Interactive `git worktree list` selector** (`gwj`) +- **Interactive `git worktree list` selector** (`gwj`/`gws`) + + + `gwj` jumps to the worktree using `cd` and can only be used via the alias, no equivalent behavior using forgit as a git subcommand - **Interactive `git worktree lock ` selector** (`gwl`) @@ -196,7 +198,7 @@ forgit_cherry_pick=gcp forgit_rebase=grb forgit_blame=gbl forgit_fixup=gfu -forgit_worktree_select=gws +forgit_worktree_select=gwj/gws forgit_worktree_lock=gwl forgit_worktree_remove=gwr forgit_worktree_unlock=gwu @@ -238,30 +240,30 @@ git cf If you want to customize `git`'s behavior within forgit there is a dedicated variable for each forgit command. These are passed to the according `git` calls. -| Command | Option | -| -------- | --------------------------------------------------------------------------- | -| `ga` | `FORGIT_ADD_GIT_OPTS` | -| `glo` | `FORGIT_LOG_GIT_OPTS` | -| `grl` | `FORGIT_REFLOG_GIT_OPTS` | -| `gd` | `FORGIT_DIFF_GIT_OPTS` | -| `grh` | `FORGIT_RESET_HEAD_GIT_OPTS` | -| `gcf` | `FORGIT_CHECKOUT_FILE_GIT_OPTS` | -| `gcb` | `FORGIT_CHECKOUT_BRANCH_GIT_OPTS`, `FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS` | -| `gbd` | `FORGIT_BRANCH_DELETE_GIT_OPTS` | -| `gct` | `FORGIT_CHECKOUT_TAG_GIT_OPTS` | -| `gco` | `FORGIT_CHECKOUT_COMMIT_GIT_OPTS` | -| `grc` | `FORGIT_REVERT_COMMIT_GIT_OPTS` | -| `gss` | `FORGIT_STASH_SHOW_GIT_OPTS` | -| `gsp` | `FORGIT_STASH_PUSH_GIT_OPTS` | -| `gclean` | `FORGIT_CLEAN_GIT_OPTS` | -| `grb` | `FORGIT_REBASE_GIT_OPTS` | -| `gbl` | `FORGIT_BLAME_GIT_OPTS` | -| `gfu` | `FORGIT_FIXUP_GIT_OPTS` | -| `gcp` | `FORGIT_CHERRY_PICK_GIT_OPTS` | -| `gwj` | `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | -| `gwl` | `FORGIT_WORKTREE_LOCK_GIT_OPTS`, `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | -| `gwr` | `FORGIT_WORKTREE_REMOVE_GIT_OPTS`, `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | -| `gwu` | `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | +| Command | Option | +| ----------- | --------------------------------------------------------------------------- | +| `ga` | `FORGIT_ADD_GIT_OPTS` | +| `glo` | `FORGIT_LOG_GIT_OPTS` | +| `grl` | `FORGIT_REFLOG_GIT_OPTS` | +| `gd` | `FORGIT_DIFF_GIT_OPTS` | +| `grh` | `FORGIT_RESET_HEAD_GIT_OPTS` | +| `gcf` | `FORGIT_CHECKOUT_FILE_GIT_OPTS` | +| `gcb` | `FORGIT_CHECKOUT_BRANCH_GIT_OPTS`, `FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS` | +| `gbd` | `FORGIT_BRANCH_DELETE_GIT_OPTS` | +| `gct` | `FORGIT_CHECKOUT_TAG_GIT_OPTS` | +| `gco` | `FORGIT_CHECKOUT_COMMIT_GIT_OPTS` | +| `grc` | `FORGIT_REVERT_COMMIT_GIT_OPTS` | +| `gss` | `FORGIT_STASH_SHOW_GIT_OPTS` | +| `gsp` | `FORGIT_STASH_PUSH_GIT_OPTS` | +| `gclean` | `FORGIT_CLEAN_GIT_OPTS` | +| `grb` | `FORGIT_REBASE_GIT_OPTS` | +| `gbl` | `FORGIT_BLAME_GIT_OPTS` | +| `gfu` | `FORGIT_FIXUP_GIT_OPTS` | +| `gcp` | `FORGIT_CHERRY_PICK_GIT_OPTS` | +| `gwj`/`gws` | `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | +| `gwl` | `FORGIT_WORKTREE_LOCK_GIT_OPTS`, `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | +| `gwr` | `FORGIT_WORKTREE_REMOVE_GIT_OPTS`, `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | +| `gwu` | `FORGIT_WORKTREE_PREVIEW_GIT_OPTS` | ## pagers @@ -270,7 +272,7 @@ Forgit will use the default configured pager from git (`core.pager`, variables: | Use case | Option | Fallbacks to | -| ------------ | ------------------- | --------------------------------------------- | +| -------------------- | --------------------- | --------------------------------------------- | | common pager | `FORGIT_PAGER` | `git config core.pager` _or_ `cat` | | pager on `git show` | `FORGIT_SHOW_PAGER` | `git config pager.show` _or_ `$FORGIT_PAGER` | | pager on `git diff` | `FORGIT_DIFF_PAGER` | `git config pager.diff` _or_ `$FORGIT_PAGER` | @@ -295,31 +297,31 @@ export FORGIT_FZF_DEFAULT_OPTS=" Customizing fzf options for each command individually is also supported: -| Command | Option | -|----------|-----------------------------------| -| `ga` | `FORGIT_ADD_FZF_OPTS` | -| `glo` | `FORGIT_LOG_FZF_OPTS` | -| `grl` | `FORGIT_REFLOG_FZF_OPTS` | -| `gi` | `FORGIT_IGNORE_FZF_OPTS` | -| `gd` | `FORGIT_DIFF_FZF_OPTS` | -| `grh` | `FORGIT_RESET_HEAD_FZF_OPTS` | -| `gcf` | `FORGIT_CHECKOUT_FILE_FZF_OPTS` | -| `gcb` | `FORGIT_CHECKOUT_BRANCH_FZF_OPTS` | -| `gbd` | `FORGIT_BRANCH_DELETE_FZF_OPTS` | -| `gct` | `FORGIT_CHECKOUT_TAG_FZF_OPTS` | -| `gco` | `FORGIT_CHECKOUT_COMMIT_FZF_OPTS` | -| `grc` | `FORGIT_REVERT_COMMIT_FZF_OPTS` | -| `gss` | `FORGIT_STASH_FZF_OPTS` | -| `gsp` | `FORGIT_STASH_PUSH_FZF_OPTS` | -| `gclean` | `FORGIT_CLEAN_FZF_OPTS` | -| `grb` | `FORGIT_REBASE_FZF_OPTS` | -| `gbl` | `FORGIT_BLAME_FZF_OPTS` | -| `gfu` | `FORGIT_FIXUP_FZF_OPTS` | -| `gcp` | `FORGIT_CHERRY_PICK_FZF_OPTS` | -| `gws` | `FORGIT_WORKTREE_SELECT_FZF_OPTS` | -| `gwl` | `FORGIT_WORKTREE_LOCK_FZF_OPTS` | -| `gwr` | `FORGIT_WORKTREE_REMOVE_FZF_OPTS` | -| `gwu` | `FORGIT_WORKTREE_UNLOCK_FZF_OPTS` | +| Command | Option | +|-------------|-----------------------------------| +| `ga` | `FORGIT_ADD_FZF_OPTS` | +| `glo` | `FORGIT_LOG_FZF_OPTS` | +| `grl` | `FORGIT_REFLOG_FZF_OPTS` | +| `gi` | `FORGIT_IGNORE_FZF_OPTS` | +| `gd` | `FORGIT_DIFF_FZF_OPTS` | +| `grh` | `FORGIT_RESET_HEAD_FZF_OPTS` | +| `gcf` | `FORGIT_CHECKOUT_FILE_FZF_OPTS` | +| `gcb` | `FORGIT_CHECKOUT_BRANCH_FZF_OPTS` | +| `gbd` | `FORGIT_BRANCH_DELETE_FZF_OPTS` | +| `gct` | `FORGIT_CHECKOUT_TAG_FZF_OPTS` | +| `gco` | `FORGIT_CHECKOUT_COMMIT_FZF_OPTS` | +| `grc` | `FORGIT_REVERT_COMMIT_FZF_OPTS` | +| `gss` | `FORGIT_STASH_FZF_OPTS` | +| `gsp` | `FORGIT_STASH_PUSH_FZF_OPTS` | +| `gclean` | `FORGIT_CLEAN_FZF_OPTS` | +| `grb` | `FORGIT_REBASE_FZF_OPTS` | +| `gbl` | `FORGIT_BLAME_FZF_OPTS` | +| `gfu` | `FORGIT_FIXUP_FZF_OPTS` | +| `gcp` | `FORGIT_CHERRY_PICK_FZF_OPTS` | +| `gwj`/`gws` | `FORGIT_WORKTREE_SELECT_FZF_OPTS` | +| `gwl` | `FORGIT_WORKTREE_LOCK_FZF_OPTS` | +| `gwr` | `FORGIT_WORKTREE_REMOVE_FZF_OPTS` | +| `gwu` | `FORGIT_WORKTREE_UNLOCK_FZF_OPTS` | Complete loading order of fzf options is: