Skip to content

Git: worktree creation and unlinking scripts

Source: Notion | Last edited: 2024-09-07 | ID: a34db094-bf6...


This tutorial will guide you through using two scripts to manage your Git worktrees effectively. The scripts will help you switch branches, create worktrees, and unlink worktrees. We’ll follow the 5W1H principle (Who, What, When, Where, Why, and How) to ensure clarity.

The motivation behind using git worktree is to streamline the development process by allowing multiple branches to be worked on simultaneously within the same repository. This is particularly useful when you want to have multiple physical directories open in the same IDE (Cursor IDE or Visual Studio Code) to avoid switching windows back and forth. This approach provides a more coherent interface, making it easier to manage and compare different branches.

Managing multiple worktrees allows you to work on different branches simultaneously without switching contexts (or git checkout to another branch). These scripts automate the process, making it more efficient and less error-prone.

These scripts are intended for developers who use Git for version control and need to manage multiple worktrees within the same Workspace of Cursor or VS Code IDE efficiently.

  • Script 1: Switches to a specified branch and creates a worktree for the current branch in the upper directory.
  • Script 2: Unlinks a specified worktree and provides commands to navigate back to the main repository.

Use these scripts when you need to:

  • Switch to a different branch and create a worktree for the current branch.
  • Unlink an existing worktree and clean up your worktree management.

Imagine you are a developer working on a new feature for a project. You are currently on the helpers branch and need to switch to the cost_adjusted_v2 branch to review some recent changes. However, you also want to continue working on the helpers branch without losing your current context. Using Script 1, you can switch to the cost_adjusted_v2 branch and create a worktree for the helpers branch in the upper directory, allowing you to work on both branches simultaneously. The perk of this setup is that, because there are two directories involved, you can take advantage of the file system structure to open both branches in the same workspace in your IDE (such as Visual Studio Code). This means you can have the cost_adjusted_v2 branch open in one folder and the helpers branch open in another folder within the same workspace, making it easy to switch between them and compare changes side-by-side. After completing your tasks on the helpers branch, you decide to clean up by unlinking the worktree. You use Script 2 to safely unlink the helpers worktree, ensuring that any uncommitted changes are either committed or stashed, and navigate back to the main repository. This workflow allows you to efficiently manage multiple branches and worktrees, reducing context-switching overhead and maintaining a clean working environment.

These scripts should be run in the terminal within the root directory of your Git repository.

Follow these steps to use the scripts:

Script 1: Switch Branch and Create Worktree

Section titled “Script 1: Switch Branch and Create Worktree”
  1. Create the Script File
Terminal window
# Create a temporary script file
temp_script=$(mktemp)
# Write the script content to the temporary file
cat << 'EOF' > "$temp_script"
# Define color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Get the current branch name
current_branch=$(git rev-parse --abbrev-ref HEAD)
printf "${BLUE}Current branch:${NC} %s\n" "$current_branch"
# Check for uncommitted changes
printf "${CYAN}Checking for uncommitted changes...${NC}\n"
if ! git diff-index --quiet HEAD -- || [ -n "$(git ls-files --others --exclude-standard)" ]; then
printf "${RED}Warning: There are uncommitted changes or untracked files in the current branch.${NC}\n"
printf "${YELLOW}If you proceed, these changes will be lost.${NC}\n"
read -p "Do you still want to switch branches? (yes/no): " confirm_uncommitted
if [ "$confirm_uncommitted" != "yes" ]; then
printf "${RED}Branch switching aborted due to uncommitted changes.${NC}\n"
exit 1
fi
fi
# List all branches
branches=($(git branch --format='%(refname:short)'))
printf "${CYAN}Available branches:${NC}\n"
for i in "${!branches[@]}"; do
printf "${YELLOW}%d) %s${NC}\n" "$((i+1))" "${branches[$i]}"
done
# Ask for the target branch to switch to
read -p "Enter the number of the target branch to switch to: " branch_number
# Validate the input
if ! [[ "$branch_number" =~ ^[0-9]+$ ]] || [ "$branch_number" -lt 1 ] || [ "$branch_number" -gt "${#branches[@]}" ]; then
printf "${RED}Invalid selection. Exiting.${NC}\n"
exit 1
fi
target_branch="${branches[$((branch_number-1))]}"
printf "${CYAN}Switching to the target branch...${NC}\n"
if git checkout "$target_branch"; then
printf "${GREEN}Switched to branch %s.${NC}\n" "$target_branch"
else
printf "${RED}Failed to switch to branch %s.${NC}\n" "$target_branch"
exit 1
fi
# Create a meaningful worktree directory name
timestamp=$(date +%Y%m%d%H%M%S)
upper_directory=$(dirname "$(pwd)")
worktree_path="$upper_directory/${current_branch}_worktree_$timestamp"
printf "${CYAN}Creating a worktree for branch %s in %s...${NC}\n" "$current_branch" "$worktree_path"
if git worktree add "$worktree_path" "$current_branch"; then
printf "${GREEN}Worktree created at %s for branch %s.${NC}\n" "$worktree_path" "$current_branch"
else
printf "${RED}Failed to create worktree at %s for branch %s.${NC}\n" "$worktree_path" "$current_branch"
exit 1
fi
# Display final summary
printf "${CYAN}Summary of actions taken:${NC}\n"
printf "${GREEN}1. Switched to branch %s.${NC}\n" "$target_branch"
printf "${GREEN}2. Created worktree at %s for branch %s.${NC}\n" "$worktree_path" "$current_branch"
# Provide the cd command to navigate to the new worktree directory
printf "${CYAN}To navigate to the new worktree directory, use the following command:${NC}\n"
printf "${YELLOW}cd %s${NC}\n" "$worktree_path"
EOF
# Make the temporary script executable
chmod +x "$temp_script"
# Execute the temporary script
"$temp_script"
# Remove the temporary script
rm "$temp_script"
  1. Run the Script
  • Open your terminal and navigate to the root directory of your Git repository.
  • Copy and paste the script into your terminal and press Enter.
  • Follow the prompts to switch branches and create a worktree.
  • Branch Listing: The script lists all available branches and allows you to select one by entering its corresponding number.
  • Uncommitted Changes Check: The script checks for uncommitted changes and warns you before proceeding.
  • Worktree Naming: The worktree directory name includes the current branch name and a timestamp for uniqueness.
  • Manual Navigation: The script provides a cd command to manually navigate to the new worktree directory.
  1. Create the Script File
Terminal window
# Create a temporary script file
temp_script=$(mktemp)
# Write the script content to the temporary file
cat << 'EOF' > "$temp_script"
# Define color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
# Get the list of worktrees
printf "${CYAN}Getting the list of worktrees...${NC}\n"
worktree_list=$(git worktree list)
printf "${BLUE}Worktree list:${NC}\n"
printf "%s\n" "$worktree_list"
# Extract the current worktree path
current_worktree=$(pwd)
printf "${BLUE}Current worktree:${NC} %s\n" "$current_worktree"
# Extract the main repository path
main_repo_path=$(echo "$worktree_list" | grep -v "$current_worktree" | awk '{print $1}' | head -n 1)
printf "${BLUE}Main repository path:${NC} %s\n" "$main_repo_path"
# Check if the main repository path is found
if [ -z "$main_repo_path" ]; then
printf "${RED}Main repository path not found.${NC}\n"
exit 1
fi
# Find the branch or commit associated with the current worktree
originating_branch=$(echo "$worktree_list" | grep "$current_worktree" | awk '{print $2}' | sed 's/\[//;s/\]//')
printf "${BLUE}Originating branch or commit:${NC} %s\n" "$originating_branch"
# Check if the originating branch is a commit hash and try to get the branch name
if [[ "$originating_branch" =~ ^[0-9a-f]{7,40}$ ]]; then
printf "${YELLOW}Originating branch is a commit hash. Trying to resolve to branch name...${NC}\n"
# Check if the commit is part of any local branches
branch_name=$(git branch --contains "$originating_branch" | grep -v 'remotes/' | head -n 1 | sed 's/* //')
if [ -z "$branch_name" ]; then
# If no local branch contains the commit, fall back to name-rev
branch_name=$(git name-rev --name-only "$originating_branch")
fi
printf "${YELLOW}Resolved branch name:${NC} %s\n" "$branch_name"
if [ -n "$branch_name" ]; then
originating_branch=$branch_name
fi
fi
# Print the originating branch
printf "${BLUE}The originating branch for the current worktree is:${NC} %s\n" "$originating_branch"
# Check for uncommitted changes
printf "${CYAN}Checking for uncommitted changes...${NC}\n"
if ! git diff-index --quiet HEAD -- || [ -n "$(git ls-files --others --exclude-standard)" ]; then
printf "${RED}Warning: There are uncommitted changes or untracked files in the worktree.${NC}\n"
printf "${YELLOW}If you proceed, these changes will be lost.${NC}\n"
read -p "Do you still want to unlink the worktree? (yes/no): " confirm_uncommitted
if [ "$confirm_uncommitted" != "yes" ]; then
printf "${RED}Worktree unlinking aborted due to uncommitted changes.${NC}\n"
exit 1
fi
fi
# Find the worktree ID
worktree_id=$(basename "$current_worktree")
worktree_config_path="$main_repo_path/.git/worktrees/$worktree_id"
# Check if the worktree config path exists
if [ ! -d "$worktree_config_path" ]; then
printf "${RED}Worktree config path not found: %s${NC}\n" "$worktree_config_path"
exit 1
fi
# Detailed warning about unlinking consequences
printf "${YELLOW}Warning: Unlinking the worktree will have the following consequences:${NC}\n"
printf "${YELLOW}1. The worktree will be unlinked from Git's management.${NC}\n"
printf "${YELLOW}2. The worktree directory and its contents will remain intact.${NC}\n"
printf "${YELLOW}3. Any uncommitted changes in the worktree will be lost.${NC}\n"
printf "${YELLOW}4. Re-linking the worktree later is not straightforward. You will need to delete the existing directory and create a new worktree.${NC}\n"
printf "${YELLOW}5. You cannot create a worktree for a branch that is currently checked out in the main repository. You need to switch to a different branch in the main repository before creating a worktree for the original branch.${NC}\n"
printf "${YELLOW}6. Ensure you have committed or stashed any important changes before proceeding.${NC}\n"
# Ask for confirmation to unlink the current worktree
read -p "Do you want to unlink the worktree at $current_worktree? (yes/no): " confirm
if [ "$confirm" = "yes" ]; then
# Unlink the worktree
printf "${CYAN}Unlinking the worktree...${NC}\n"
rm -rf "$worktree_config_path"
if [ $? -eq 0 ]; then
printf "${GREEN}Worktree at %s has been unlinked.${NC}\n" "$current_worktree"
# Navigate to the main repository
printf "${CYAN}Navigating to the main repository...${NC}\n"
cd "$main_repo_path" || exit
printf "${BLUE}Navigated to main repository:${NC} %s\n" "$(pwd)"
# Display final summary
printf "${CYAN}Summary of actions taken:${NC}\n"
printf "${GREEN}1. Worktree at %s has been unlinked.${NC}\n" "$current_worktree"
printf "${GREEN}2. Navigated to main repository at %s.${NC}\n" "$main_repo_path"
printf "${PURPLE}To navigate back to the main repository and switch to the originating branch, run the following commands:${NC}\n"
printf "${PURPLE}cd %s${NC}\n" "$main_repo_path"
printf "${PURPLE}git checkout %s${NC}\n" "$originating_branch"
else
printf "${RED}Failed to unlink the worktree at %s.${NC}\n" "$current_worktree"
exit 1
fi
else
printf "${YELLOW}Worktree unlinking aborted.${NC}\n"
fi
EOF
# Make the temporary script executable
chmod +x "$temp_script"
# Execute the temporary script
"$temp_script"
# Remove the temporary script
rm "$temp_script"
  1. Run the Script
  • Open your terminal and navigate to the root directory of your Git repository.
  • Copy and paste the script into your terminal and press Enter.
  • Follow the prompts to unlink the worktree.
  • Worktree Listing: The script lists all existing worktrees and identifies the current worktree.
  • Uncommitted Changes Check: The script checks for uncommitted changes and warns you before proceeding.
  • Unlinking Consequences: The script provides detailed warnings about the consequences of unlinking a worktree.
  • Manual Navigation: The script provides commands to navigate back to the main repository and switch to the originating branch.
  • Uncommitted Changes: The script checks for uncommitted changes and warns you, giving you the option to abort the operation to prevent data loss.
  • Branch Selection: The script lists all branches and allows you to select one by number, reducing the risk of typos.
  • Worktree Naming: The worktree directory name includes a timestamp, ensuring uniqueness and preventing conflicts.
  • Uncommitted Changes: The script checks for uncommitted changes and warns you, giving you the option to abort the operation to prevent data loss.
  • Worktree Identification: The script identifies the current worktree and provides detailed warnings about the consequences of unlinking it.
  • Manual Navigation: The script provides commands to manually navigate back to the main repository and switch branches, ensuring you are aware of the changes being made.

By following these steps, you can efficiently manage your Git worktrees using the provided scripts. The first script helps you switch branches and create worktrees, while the second script helps you unlink worktrees and navigate back to the main repository. The scripts include checks for uncommitted changes, detailed warnings, and provide manual navigation commands to ensure a smooth workflow.