Skip to content

Instantly share code, notes, and snippets.

@mattr-
Last active June 27, 2026 17:06
Show Gist options
  • Select an option

  • Save mattr-/e6fa24005475884e951a0c32c4e486df to your computer and use it in GitHub Desktop.

Select an option

Save mattr-/e6fa24005475884e951a0c32c4e486df to your computer and use it in GitHub Desktop.
git worktree conversion scripts
#!/usr/bin/env bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${BLUE}Cleaning up partially converted repository...${NC}"
echo ""
# Verify we have the expected structure
if [ ! -d .bare ]; then
echo -e "${RED}Error: No .bare directory found${NC}"
exit 1
fi
if [ ! -f .git ] || ! grep -q "gitdir:" .git 2>/dev/null; then
echo -e "${RED}Error: .git file is missing or invalid${NC}"
exit 1
fi
# Find worktree directories
worktrees=()
while IFS= read -r line; do
# Extract path from worktree list output (first column)
path=$(echo "$line" | awk '{print $1}')
# Get just the directory name
dirname=$(basename "$path")
# Skip the bare repo line
if [[ "$dirname" != "bare" && "$dirname" != ".bare" ]]; then
worktrees+=("$dirname")
fi
done < <(git worktree list 2>/dev/null)
if [ ${#worktrees[@]} -eq 0 ]; then
echo -e "${RED}Error: No worktrees found. Create one first with:${NC}"
echo " git worktree add --force main main"
exit 1
fi
echo -e "${GREEN}Found worktrees:${NC}"
printf ' %s\n' "${worktrees[@]}"
echo ""
# Default to first worktree, or let user specify
target_worktree="${worktrees[0]}"
if [ ! -d "$target_worktree" ]; then
echo -e "${RED}Error: Worktree directory '$target_worktree' doesn't exist${NC}"
exit 1
fi
echo -e "${YELLOW}Will move all files from root into: ${target_worktree}/${NC}"
echo ""
# List what will be moved
echo "Files/directories to be moved:"
shopt -s dotglob nullglob
move_list=()
for item in *; do
# Skip .git, .bare, and all worktree directories
skip=false
if [[ "$item" == ".git" || "$item" == ".bare" ]]; then
skip=true
fi
for wt in "${worktrees[@]}"; do
if [[ "$item" == "$wt" ]]; then
skip=true
fi
done
if [ "$skip" = false ] && [ -e "$item" ]; then
echo " $item"
move_list+=("$item")
fi
done
shopt -u dotglob nullglob
if [ ${#move_list[@]} -eq 0 ]; then
echo -e "${GREEN}No files to move - cleanup already complete!${NC}"
exit 0
fi
echo ""
read -p "Proceed with moving ${#move_list[@]} items? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled."
exit 0
fi
echo ""
echo -e "${GREEN}Moving files to ${target_worktree}/${NC}"
moved=0
failed=0
for item in "${move_list[@]}"; do
if mv "$item" "${target_worktree}/" 2>/dev/null; then
echo -e " ${GREEN}✓${NC} $item"
moved=$((moved + 1))
else
echo -e " ${RED}✗${NC} $item (failed)"
failed=$((failed + 1))
fi
done
echo ""
echo -e "${GREEN}✅ Cleanup complete!${NC}"
echo "Moved: $moved"
if [ $failed -gt 0 ]; then
echo -e "${RED}Failed: $failed${NC}"
fi
echo ""
echo "Repository structure:"
echo " .bare/ # Git repository data"
echo " .git # Pointer to .bare"
for wt in "${worktrees[@]}"; do
echo " ${wt}/ # Worktree"
done
echo ""
echo "Next steps:"
echo " cd ${target_worktree}"
echo " git status"
#!/usr/bin/env bash
set -eo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if we're in a git repository
if [ ! -d .git ]; then
echo -e "${RED}Error: Not in a git repository${NC}"
exit 1
fi
# Check for uncommitted changes
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
echo -e "${YELLOW}Warning: You have uncommitted changes.${NC}"
echo "Refusing to continue"
echo ""
git status --short
exit 1
fi
# Get current branch name
current_branch=$(git symbolic-ref --short HEAD)
echo -e "${GREEN}Current branch: ${current_branch}${NC}"
default_branch="$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)"
if [[ "$default_branch" != "$current_branch" ]]; then
echo -e "${YELLOW}Error: The current branch is not the default branch.${NC}"
echo "Switching to the default branch before continuing."
echo ""
git switch "$default_branch"
exit 1
fi
# Confirm with user
echo ""
echo "This will convert the current repository to use bare + worktrees structure."
echo "The working files will be moved to a '${current_branch}' subdirectory."
read -p "Continue? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled."
exit 0
fi
echo ""
echo -e "${GREEN}Step 1: Configuring bare repo${NC}"
git config core.bare true
echo -e "${GREEN}Step 2: Moving .git to .bare${NC}"
mv .git .bare
echo -e "${GREEN}Step 3: Creating .git pointer file${NC}"
echo "gitdir: ./.bare" > .git
echo -e "${GREEN}Step 4: Configuring remote fetch${NC}"
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
echo -e "${GREEN}Step 5: Enabling reflog for bare repo${NC}"
git config core.logAllRefUpdates true
echo -e "${GREEN}Step 6: Creating worktree for ${current_branch}${NC}"
git worktree add "${current_branch}"
echo -e "${GREEN}Step 7: Moving files to ${current_branch}/ worktree${NC}"
# Move all files except .git, .bare, and the new worktree directory
for item in * .[^.]*; do
if [[ "$item" != ".git" && "$item" != ".bare" && "$item" != "${current_branch}" && "$item" != "." && "$item" != ".." ]]; then
if [ -e "$item" ]; then
mv "$item" "${current_branch}/"
fi
fi
done
echo ""
echo -e "${GREEN}✓ Conversion complete!${NC}"
echo ""
echo "Your repository structure is now:"
echo " .bare/ # Git repository data"
echo " .git # Pointer to .bare"
echo " ${current_branch}/ # Worktree for ${current_branch} branch"
echo ""
echo "To create new worktrees:"
echo " git worktree add <branch-name>"
echo ""
echo "To list worktrees:"
echo " git worktree list"
echo ""
echo "You can now cd into ${current_branch}/ to continue working."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment