diff --git a/README.md b/README.md index 6bc5897..1d78e48 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ It's **lightweight** and **easy to use**. | `grw` | Interactive `git commit --fixup=reword && git rebase -i --autosquash` selector | | `gclean` | Interactive `git clean` selector | | `gwt` | Interactive `git worktree` selector | +| `gwa` | Interactive `git worktree add` selector | | `gwd` | Interactive `git worktree remove` selector | 📥 Installation @@ -203,6 +204,7 @@ forgit_fixup=gfu forgit_squash=gsq forgit_reword=grw forgit_worktree=gwt +forgit_worktree_add=gwa forgit_worktree_delete=gwd ``` @@ -280,6 +282,7 @@ Each forgit command can be customized with dedicated environment variables for g | `gsq` | `FORGIT_SQUASH_GIT_OPTS` | `FORGIT_SQUASH_FZF_OPTS` | | `grw` | `FORGIT_REWORD_GIT_OPTS` | `FORGIT_REWORD_FZF_OPTS` | | `gwt` | | `FORGIT_WORKTREE_FZF_OPTS` | +| `gwa` | `FORGIT_WORKTREE_ADD_BRANCH_GIT_OPTS` | `FORGIT_WORKTREE_ADD_FZF_OPTS` | | `gwd` | `FORGIT_WORKTREE_DELETE_GIT_OPTS` | `FORGIT_WORKTREE_DELETE_FZF_OPTS` | ### Pagers @@ -350,6 +353,7 @@ export FORGIT_LOG_FZF_OPTS=' | `FORGIT_FULLSCREEN_CONTEXT` | lines of diff context in full-screen mode | 10 | | `FORGIT_DIR_VIEW` | command used to preview directories | `tree` if available, otherwise `find` | | `FORGIT_CLEAN_LIST_FILES_OPTS` | arguments passed to `git ls-files` together with `--others` to determine which files are shown when invoking `forgit clean` | | +| `FORGIT_WORKTREE_ADD_DIR` | directory where new worktrees are created | `/.wt` | ⌨ Keybindings --------------- diff --git a/bin/git-forgit b/bin/git-forgit index 7f4e4d0..57ab4e5 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -1262,6 +1262,11 @@ _forgit_paths_list() { find "$path" -name "*$ext" -print |sed -e "s#$ext\$##" -e 's#.*/##' -e '/^$/d' | sort -fu } +# Get the root path of the main worktree (not the current worktree) +_forgit_main_worktree_root() { + git worktree list --porcelain | sed -n 's/^worktree //p;q' +} + # Parse git worktree list --porcelain output and format it for display # Output format: [XY] /path/to/worktree (branch) 3 hours ago # X: '*' = current worktree, ' ' = other @@ -1410,6 +1415,51 @@ _forgit_worktree_delete() { done } +_forgit_worktree_add() { + _forgit_inside_git_repo || return 1 + + # Default to main worktree root, not current worktree's root + local wt_dir="${FORGIT_WORKTREE_ADD_DIR:-$(_forgit_main_worktree_root)/.wt}" + + # Two or more arguments: pass through to git directly + if [[ $# -ge 2 ]]; then + git worktree add "$@" + return $? + fi + + local new_branch="${1:-}" + local opts branch + + _forgit_worktree_add_branch_git_opts=() + _forgit_parse_array _forgit_worktree_add_branch_git_opts "$FORGIT_WORKTREE_ADD_BRANCH_GIT_OPTS" + + local header_opt="" + [[ -z "$new_branch" ]] && header_opt="--header-lines=1" + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index $header_opt + --preview=\"$FORGIT branch_preview {}\" + $FORGIT_WORKTREE_ADD_FZF_OPTS + " + branch=$(_forgit_branch_list "${_forgit_worktree_add_branch_git_opts[@]:---all}" | + FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name) + [[ -z "$branch" ]] && return 1 + + # Strip remotes// prefix for remote branches + local local_branch="${branch#remotes/*/}" + + if [[ -n "$new_branch" ]]; then + # gwa : create new branch from selected base + local target="$wt_dir/$new_branch" + git worktree add -b "$new_branch" "$target" "$branch" >&2 && echo "$target" + else + # gwa: create worktree for selected branch + local target="$wt_dir/$local_branch" + git worktree add "$target" "$branch" >&2 && echo "$target" + fi +} + check_prequisites() { local installed_fzf_version local higher_fzf_version @@ -1484,6 +1534,7 @@ PUBLIC_COMMANDS=( "stash_show" "stash_push" "worktree" + "worktree_add" "worktree_delete" ) diff --git a/completions/_git-forgit b/completions/_git-forgit index 226abb6..ac5d6d0 100644 --- a/completions/_git-forgit +++ b/completions/_git-forgit @@ -92,6 +92,7 @@ _git-forgit() { 'stash_push:git stash push selector' 'switch_branch:git switch branch selector' 'worktree:git worktree browser' + 'worktree_add:git worktree add selector' 'worktree_delete:git worktree remove selector' ) _describe -t commands 'git forgit' subcommands diff --git a/completions/git-forgit.bash b/completions/git-forgit.bash index 3270758..16573b0 100755 --- a/completions/git-forgit.bash +++ b/completions/git-forgit.bash @@ -92,6 +92,7 @@ _git_forgit() stash_push switch_branch worktree + worktree_add worktree_delete " diff --git a/completions/git-forgit.fish b/completions/git-forgit.fish index 7827f24..350d1de 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 reword squash stash_show stash_push switch_branch worktree worktree_delete + revert_commit reword squash stash_show stash_push switch_branch worktree worktree_add worktree_delete if contains -- $subcmd (commandline -opc) return 1 end @@ -51,6 +51,7 @@ complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_show -d 'git s 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 switch_branch -d 'git switch branch selector' complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree -d 'git worktree browser' +complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree_add -d 'git worktree add selector' complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree_delete -d 'git worktree remove selector' complete -c git-forgit -n '__fish_seen_subcommand_from add' -a "(complete -C 'git add ')" diff --git a/conf.d/forgit.plugin.fish b/conf.d/forgit.plugin.fish index a0205e2..96bf7dc 100644 --- a/conf.d/forgit.plugin.fish +++ b/conf.d/forgit.plugin.fish @@ -36,7 +36,14 @@ function forgit::worktree return $status end set -l tree (git-forgit worktree) - test -n "$tree"; and cd "$tree" + test -d "$tree"; and cd "$tree" +end + +function forgit::worktree::add + set -l tree (git-forgit worktree_add $argv) + or return $status + test -d "$tree"; or return 0 + cd "$tree"; or return 1 end # register abbreviations @@ -66,5 +73,6 @@ if test -z "$FORGIT_NO_ALIASES" 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; or string collect "gwt") forgit::worktree + abbr -a -- (string collect $forgit_worktree_add; or string collect "gwa") forgit::worktree::add abbr -a -- (string collect $forgit_worktree_delete; or string collect "gwd") git-forgit worktree_delete end diff --git a/forgit.plugin.zsh b/forgit.plugin.zsh index a928fa1..b312837 100755 --- a/forgit.plugin.zsh +++ b/forgit.plugin.zsh @@ -164,7 +164,14 @@ forgit::worktree() { if [[ $# -ne 0 ]]; then "$FORGIT" worktree "$@"; return $?; fi local tree tree=$("$FORGIT" worktree) || return $? - [[ -n "$tree" ]] && builtin cd "$tree" || return 1 + [[ -d "$tree" ]] && builtin cd "$tree" || return 1 +} + +forgit::worktree::add() { + local tree + tree=$("$FORGIT" worktree_add "$@") || return $? + [[ -d "$tree" ]] || return 0 + builtin cd "$tree" || return 1 } forgit::worktree::delete() { @@ -200,6 +207,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then builtin export forgit_reword="${forgit_reword:-grw}" builtin export forgit_blame="${forgit_blame:-gbl}" builtin export forgit_worktree="${forgit_worktree:-gwt}" + builtin export forgit_worktree_add="${forgit_worktree_add:-gwa}" builtin export forgit_worktree_delete="${forgit_worktree_delete:-gwd}" builtin alias "${forgit_add}"='forgit::add' @@ -227,6 +235,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then builtin alias "${forgit_reword}"='forgit::reword' builtin alias "${forgit_blame}"='forgit::blame' builtin alias "${forgit_worktree}"='forgit::worktree' + builtin alias "${forgit_worktree_add}"='forgit::worktree::add' builtin alias "${forgit_worktree_delete}"='forgit::worktree::delete' fi