Created
February 12, 2026 23:40
-
-
Save tmacam/d242d31bd6009f3f77071fae87cebe3b to your computer and use it in GitHub Desktop.
A bash function that creates git worktrees for feature branches with a standardized naming convention.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Creates a git worktree for a feature branch and navigates into it. | |
| # | |
| # Creates a new git worktree in the parent directory of the current | |
| # repository. | |
| # | |
| # The worktree directory is named "{repoName}-{FeatureBranchName}" and | |
| # the branch is created as "tiagoa/{FeatureBranchName}". | |
| # | |
| # By default, the new branch is based off 'main'. Use --current to branch | |
| # from HEAD, or --branch to specify a different base branch. | |
| # | |
| # To install, the quickest way is to dot-source this script in your | |
| # .bashrc/.zshrc. | |
| # ── Private helpers (namespaced to avoid polluting the global scope) ────────── | |
| __nfwt_err() { | |
| printf 'Error: %s\n' "$*" >&2 | |
| } | |
| __nfwt_usage() { | |
| cat <<'EOF' | |
| Usage: | |
| nfwt <feature-branch-name> [--current | --branch <base-branch>] | |
| Description: | |
| Creates a git worktree in the parent directory of the current repository. | |
| Worktree directory name: {repoName}-{feature-branch-name} | |
| New branch name: tiagoa/{feature-branch-name} | |
| Options: | |
| -c, --current Branch from current HEAD | |
| -b, --branch <name> Branch from a specific base branch | |
| -h, --help Show this help | |
| Examples: | |
| nfwt my-feature | |
| nfwt my-feature --current | |
| nfwt my-feature --branch develop | |
| EOF | |
| } | |
| # ── Main function ──────────────────────────────────────────────────────────── | |
| new_feature_worktree() { | |
| local feature_branch_name | |
| local use_current=false | |
| local base_branch="" | |
| local base_ref | |
| local git_root | |
| local repo_name | |
| local parent_dir | |
| local branch_name | |
| local worktree_dir | |
| local branches | |
| if [[ $# -lt 1 ]]; then | |
| __nfwt_usage >&2 | |
| return 64 | |
| fi | |
| feature_branch_name="$1" | |
| shift | |
| if [[ -z "${feature_branch_name}" ]]; then | |
| __nfwt_err "Feature branch name cannot be empty." | |
| return 64 | |
| fi | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -c|--current) | |
| if [[ -n "${base_branch}" ]]; then | |
| __nfwt_err "--current and --branch cannot be used together." | |
| return 64 | |
| fi | |
| use_current=true | |
| shift | |
| ;; | |
| -b|--branch) | |
| if [[ "${use_current}" == true ]]; then | |
| __nfwt_err "--current and --branch cannot be used together." | |
| return 64 | |
| fi | |
| if [[ $# -lt 2 || -z "${2}" ]]; then | |
| __nfwt_err "--branch requires a non-empty branch name." | |
| return 64 | |
| fi | |
| base_branch="$2" | |
| shift 2 | |
| ;; | |
| -h|--help) | |
| __nfwt_usage | |
| return 0 | |
| ;; | |
| --) | |
| shift | |
| break | |
| ;; | |
| *) | |
| __nfwt_err "Unknown option: $1" | |
| __nfwt_usage >&2 | |
| return 64 | |
| ;; | |
| esac | |
| done | |
| if [[ $# -gt 0 ]]; then | |
| __nfwt_err "Unexpected extra arguments: $*" | |
| __nfwt_usage >&2 | |
| return 64 | |
| fi | |
| if ! command -v git >/dev/null 2>&1; then | |
| __nfwt_err "git is not installed or not available in PATH." | |
| return 69 | |
| fi | |
| if ! git_root="$(git rev-parse --show-toplevel 2>/dev/null)"; then | |
| __nfwt_err "Not inside a git repository." | |
| return 128 | |
| fi | |
| if [[ ! -d "${git_root}" ]]; then | |
| __nfwt_err "Resolved repository root does not exist: ${git_root}" | |
| return 70 | |
| fi | |
| repo_name="$(basename "${git_root}")" | |
| parent_dir="$(dirname "${git_root}")" | |
| if [[ "${use_current}" == true ]]; then | |
| base_ref='HEAD' | |
| elif [[ -n "${base_branch}" ]]; then | |
| base_ref="${base_branch}" | |
| else | |
| base_ref='main' | |
| fi | |
| if [[ "${base_ref}" != 'HEAD' ]]; then | |
| if ! git rev-parse --verify "${base_ref}" >/dev/null 2>&1; then | |
| branches="$(git branch --list --format='%(refname:short)' 2>/dev/null || true)" | |
| __nfwt_err "Base branch '${base_ref}' does not exist." | |
| if [[ -n "${branches}" ]]; then | |
| printf 'Available branches:\n%s\n' "${branches}" >&2 | |
| fi | |
| return 65 | |
| fi | |
| fi | |
| branch_name="tiagoa/${feature_branch_name}" | |
| worktree_dir="${parent_dir}/${repo_name}-${feature_branch_name}" | |
| if [[ -e "${worktree_dir}" ]]; then | |
| __nfwt_err "Directory already exists: ${worktree_dir}" | |
| return 73 | |
| fi | |
| if git show-ref --verify --quiet "refs/heads/${branch_name}"; then | |
| __nfwt_err "Branch already exists locally: ${branch_name}" | |
| return 68 | |
| fi | |
| printf "Creating worktree at '%s' with branch '%s' from '%s'...\n" "${worktree_dir}" "${branch_name}" "${base_ref}" | |
| if ! git worktree add -b "${branch_name}" "${worktree_dir}" "${base_ref}"; then | |
| __nfwt_err "Failed to create git worktree." | |
| return 74 | |
| fi | |
| printf 'Worktree created successfully.\n' | |
| if ! cd "${worktree_dir}"; then | |
| __nfwt_err "Worktree was created but failed to change directory to: ${worktree_dir}" | |
| return 75 | |
| fi | |
| } | |
| # Alias for convenience (matches the PowerShell 'nfwt' alias). | |
| # Source this file in your .bashrc / .zshrc: | |
| # source /path/to/FeatureWorktree.sh | |
| nfwt() { | |
| new_feature_worktree "$@" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment