Last active
June 27, 2026 17:06
-
-
Save mattr-/e6fa24005475884e951a0c32c4e486df to your computer and use it in GitHub Desktop.
git worktree conversion scripts
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 | |
| 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" |
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 | |
| 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