Created
January 30, 2026 09:06
-
-
Save willwillems/a6baaec1814b3431e01163a6b0ca71d9 to your computer and use it in GitHub Desktop.
wtpull - Set up a git worktree with ignored files and VS Code status bar color
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 | |
| # | |
| # wtpull - Set up a git worktree with ignored files and visual differentiation | |
| # | |
| # Copies .gitignore'd files from the main worktree to the current worktree, | |
| # and sets a unique VS Code status bar color based on the branch name. | |
| # | |
| # Usage: | |
| # wtpull Copy ignored files and set VS Code status bar color | |
| # wtpull --no-vscode Copy ignored files only, skip VS Code customization | |
| # wtpull --help Show this help message | |
| # | |
| # The status bar color is deterministic based on branch name, so the same | |
| # branch always gets the same color. Colors are from the Flexoki palette. | |
| # | |
| # Requirements: | |
| # - Must be run from within a git worktree (not the main repo) | |
| # - jq (for JSON manipulation) | |
| # - rsync | |
| set -euo pipefail | |
| # Flexoki 800/700 color pairs (background/hover) | |
| COLORS=( | |
| "#6C201C:#942822" # red | |
| "#71320D:#9D4310" # orange | |
| "#664D01:#8E6B01" # yellow | |
| "#3D4C07:#536907" # green | |
| "#164F4A:#1C6C66" # cyan | |
| "#163B66:#1A4F8C" # blue | |
| "#3C2A62:#4F3685" # purple | |
| "#641F46:#87285E" # magenta | |
| ) | |
| COLOR_NAMES=(red orange yellow green cyan blue purple magenta) | |
| show_help() { | |
| sed -n '2,16p' "$0" | sed 's/^# \?//' | |
| exit 0 | |
| } | |
| error() { | |
| echo "error: $1" >&2 | |
| exit 1 | |
| } | |
| warn() { | |
| echo "warning: $1" >&2 | |
| } | |
| # Parse arguments | |
| NO_VSCODE=false | |
| for arg in "$@"; do | |
| case "$arg" in | |
| --help|-h) show_help ;; | |
| --no-vscode) NO_VSCODE=true ;; | |
| *) error "unknown option: $arg" ;; | |
| esac | |
| done | |
| # Validate: must be in a git repo | |
| if ! git rev-parse --git-dir &>/dev/null; then | |
| error "not in a git repository" | |
| fi | |
| # Get worktree list (first one is the main worktree) | |
| mapfile -t worktrees < <(git worktree list --porcelain | sed -n 's/^worktree //p') | |
| if [[ ${#worktrees[@]} -lt 2 ]]; then | |
| error "no additional worktrees found — nothing to pull from" | |
| fi | |
| main_worktree="${worktrees[0]}" | |
| current_dir="$(pwd)" | |
| # Validate: must not be in the main worktree | |
| if [[ "$current_dir" == "$main_worktree" ]]; then | |
| error "you are in the main worktree — run this from a secondary worktree" | |
| fi | |
| # Validate: must be in a worktree (not just a subdirectory somewhere) | |
| in_worktree=false | |
| for wt in "${worktrees[@]:1}"; do | |
| if [[ "$current_dir" == "$wt" || "$current_dir" == "$wt"/* ]]; then | |
| in_worktree=true | |
| break | |
| fi | |
| done | |
| if [[ "$in_worktree" == false ]]; then | |
| error "not in a git worktree" | |
| fi | |
| # --- Step 1: Rsync ignored files from main worktree --- | |
| echo "Copying ignored files from main worktree..." | |
| git -C "$main_worktree" ls-files -oi --exclude-standard -z \ | |
| | rsync -a0 --files-from=- "$main_worktree"/ ./ | |
| echo "Done copying ignored files." | |
| # --- Step 2: VS Code status bar color --- | |
| if [[ "$NO_VSCODE" == true ]]; then | |
| exit 0 | |
| fi | |
| # Check for jq | |
| if ! command -v jq &>/dev/null; then | |
| warn "jq not found — skipping VS Code customization" | |
| exit 0 | |
| fi | |
| # Get branch name | |
| branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD") | |
| # Hash branch name to pick a color (deterministic) | |
| hash=$(echo -n "$branch" | md5sum | cut -c1-8) | |
| index=$((16#$hash % ${#COLORS[@]})) | |
| color_pair="${COLORS[$index]}" | |
| bg_color="${color_pair%%:*}" | |
| hover_color="${color_pair##*:}" | |
| color_name="${COLOR_NAMES[$index]}" | |
| echo "Setting VS Code status bar to $color_name (based on branch '$branch')..." | |
| # Create .vscode directory if needed | |
| mkdir -p .vscode | |
| settings_file=".vscode/settings.json" | |
| # The color customizations to merge in | |
| new_settings=$(cat <<EOF | |
| { | |
| "workbench.colorCustomizations": { | |
| "statusBar.background": "$bg_color", | |
| "statusBarItem.hoverBackground": "$hover_color" | |
| } | |
| } | |
| EOF | |
| ) | |
| if [[ -f "$settings_file" ]]; then | |
| # Merge with existing settings (deep merge for colorCustomizations) | |
| existing=$(cat "$settings_file") | |
| merged=$(echo "$existing" | jq --argjson new "$new_settings" ' | |
| .["workbench.colorCustomizations"] = ( | |
| (.["workbench.colorCustomizations"] // {}) * $new["workbench.colorCustomizations"] | |
| ) | |
| ') | |
| echo "$merged" > "$settings_file" | |
| else | |
| # Create new settings file | |
| echo "$new_settings" > "$settings_file" | |
| fi | |
| # --- Step 3: Hide local changes from git using --skip-worktree --- | |
| git update-index --skip-worktree "$settings_file" 2>/dev/null || true | |
| echo "Done. VS Code status bar color set to $color_name." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment