diff --git a/contrib/windows/README.md b/contrib/windows/README.md new file mode 100644 index 0000000..6946ad2 --- /dev/null +++ b/contrib/windows/README.md @@ -0,0 +1,90 @@ +# Windows Support for `try` + +Alternative shell implementations for Windows users who encounter Ruby compatibility issues. + +## Installation + +### Git Bash / MSYS2 / Cygwin + +**Option A: Download from GitHub** +```bash +mkdir -p ~/.local +curl -o ~/.local/try.sh https://raw.githubusercontent.com/tobi/try/main/contrib/windows/try.sh +``` + +**Option B: Copy from local clone** +```bash +mkdir -p ~/.local +cp /path/to/try/contrib/windows/try.sh ~/.local/try.sh +``` + +**Then add to profile:** +```bash +echo 'source ~/.local/try.sh' >> ~/.bashrc +source ~/.bashrc +``` + +### PowerShell + +> **Note:** `try` is a reserved keyword in PowerShell. Use `& try` to invoke. + +**Option A: Download from GitHub** +```powershell +New-Item -ItemType Directory -Force -Path "$HOME\Documents\WindowsPowerShell" | Out-Null +Invoke-WebRequest -Uri "https://raw.githubusercontent.com/tobi/try/main/contrib/windows/try.ps1" ` + -OutFile "$HOME\Documents\WindowsPowerShell\try.ps1" +``` + +**Option B: Copy from local clone** +```powershell +New-Item -ItemType Directory -Force -Path "$HOME\Documents\WindowsPowerShell" | Out-Null +Copy-Item "C:\path\to\try\contrib\windows\try.ps1" "$HOME\Documents\WindowsPowerShell\try.ps1" +``` + +**Then add to profile:** +```powershell +if (!(Test-Path $PROFILE)) { New-Item -Path $PROFILE -Force | Out-Null } +Add-Content $PROFILE '. "$HOME\Documents\WindowsPowerShell\try.ps1"' +. $PROFILE +``` + +**Profile paths by version:** +- PowerShell 5.x: `$HOME\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1` +- PowerShell 7.x: `$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1` + +The script is stored in `WindowsPowerShell` folder but loaded from your active `$PROFILE`. + +## Usage + +| Command | Description | +|---------|-------------| +| `try` | List recent experiment directories | +| `try ` | Create dated directory and enter it | +| `try ` | Clone repo into dated directory | +| `try clone [name]` | Clone with optional custom name | +| `try . ` | Create worktree from current repo | +| `try ./path [name]` | Create worktree from specified repo | +| `try worktree ` | Create worktree (explicit form) | +| `try --help` | Show help | + +### PowerShell Examples + +```powershell +& try my-experiment +& try https://github.com/user/repo.git +& try . feature-branch +& try .\other-repo experiment +``` + +### Bash Examples + +```bash +try my-experiment +try https://github.com/user/repo.git +try . feature-branch +try ./other-repo experiment +``` + +## Storage + +All directories are created in `~/src/tries` with date prefix (e.g., `2025-12-11-my-experiment`). diff --git a/contrib/windows/try.ps1 b/contrib/windows/try.ps1 new file mode 100644 index 0000000..9a41280 --- /dev/null +++ b/contrib/windows/try.ps1 @@ -0,0 +1,214 @@ +function Invoke-Try { + param( + [string]$Command, + [string]$Arg1, + [string]$Arg2 + ) + + $baseDir = Join-Path $HOME "src\tries" + + # Helper to check Git repo + function Is-GitRepo { + git rev-parse --is-inside-work-tree > $null 2>&1 + return $LASTEXITCODE -eq 0 + } + + # Helper to get unique directory name with versioning + function Get-UniqueDirectoryName { + param([string]$BasePath, [string]$DatePrefix, [string]$Name) + + $initial = "$DatePrefix-$Name" + $fullPath = Join-Path $BasePath $initial + if (!(Test-Path $fullPath)) { return $Name } + + # Check if name ends with digits + if ($Name -match '^(.*?)(\d+)$') { + $stem = $Matches[1] + $num = [int]$Matches[2] + 1 + while ($true) { + $candidate = "$stem$num" + $candidateFull = Join-Path $BasePath "$DatePrefix-$candidate" + if (!(Test-Path $candidateFull)) { return $candidate } + $num++ + } + } else { + # No numeric suffix; use -2 style + $i = 2 + while ($true) { + $candidate = "$Name-$i" + $candidateFull = Join-Path $BasePath "$DatePrefix-$candidate" + if (!(Test-Path $candidateFull)) { return $candidate } + $i++ + } + } + } + + # Help + if ($Command -eq "--help" -or $Command -eq "-h") { + Write-Host "try - A simple temporary workspace manager (PowerShell Version)" + Write-Host "" + Write-Host "Usage (Note: use '&' prefix to avoid keyword conflict):" + Write-Host " & try List up to 10 most recent experiment directories" + Write-Host " & try Create and enter a dated directory for a new project" + Write-Host " & try . Create a dated worktree dir for current repo" + Write-Host " & try .\path\to\repo [name] Use another repo as the worktree source" + Write-Host " & try clone [name] Clone a git repo into a dated directory and enter it" + Write-Host " & try worktree Create a git worktree from current repo and enter it" + Write-Host " & try --help (-h) Show this help message" + Write-Host "" + Write-Host "Directories are stored in: $baseDir" + return + } + + # List recent tries + if ([string]::IsNullOrWhiteSpace($Command)) { + Write-Host "Usage: & try or & try " + Write-Host "Recent tries in $baseDir" + if (!(Test-Path $baseDir)) { New-Item -ItemType Directory -Force -Path $baseDir | Out-Null } + Get-ChildItem -Path $baseDir -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 10 -ExpandProperty Name + return + } + + # Dot notation: try . [name] or try .\path\to\repo [name] + if ($Command -eq "." -or $Command.StartsWith(".\") -or $Command.StartsWith("./")) { + # Determine repo directory + if ($Command -eq ".") { + $repoDir = Get-Location + $customName = $Arg1 + # "try ." requires a name argument (too easy to invoke accidentally) + if ([string]::IsNullOrWhiteSpace($customName)) { + Write-Error "'try .' requires a name argument. Usage: & try . " + return + } + } else { + $repoDir = Resolve-Path $Command -ErrorAction SilentlyContinue + if (-not $repoDir) { + Write-Error "Path not found: $Command" + return + } + $customName = $Arg1 + } + + # Determine base name + if ([string]::IsNullOrWhiteSpace($customName)) { + $baseName = Split-Path $repoDir -Leaf + } else { + $baseName = $customName -replace '\s+', '-' + } + + $dateStr = Get-Date -Format "yyyy-MM-dd" + $baseName = Get-UniqueDirectoryName -BasePath $baseDir -DatePrefix $dateStr -Name $baseName + $targetDir = Join-Path $baseDir "$dateStr-$baseName" + + if (!(Test-Path $baseDir)) { New-Item -ItemType Directory -Force -Path $baseDir | Out-Null } + + # Check if it's a git repo and use worktree + $gitDir = Join-Path $repoDir ".git" + if (Test-Path $gitDir) { + if (!(Test-Path $targetDir)) { + Write-Host "Creating worktree from '$repoDir'..." + git -C $repoDir worktree add --detach $targetDir 2>$null + if ($LASTEXITCODE -ne 0) { + # Fallback: just create directory + New-Item -ItemType Directory -Force -Path $targetDir | Out-Null + } + } + } else { + # Not a git repo, just create directory + if (!(Test-Path $targetDir)) { + New-Item -ItemType Directory -Force -Path $targetDir | Out-Null + } + } + + Set-Location $targetDir + Write-Host "Entered: $targetDir" + return + } + + # Clone + if ($Command -eq "clone") { + $url = $Arg1 + $customName = $Arg2 + if ([string]::IsNullOrWhiteSpace($url)) { + Write-Error "Clone command requires a URL. Usage: & try clone [name]" + return + } + + $repoName = ($url -split "/")[-1] -replace "\.git$", "" + $projectName = if ([string]::IsNullOrWhiteSpace($customName)) { $repoName } else { $customName } + $dateStr = Get-Date -Format "yyyy-MM-dd" + $targetDir = Join-Path $baseDir "$dateStr-$projectName" + + if (!(Test-Path $baseDir)) { New-Item -ItemType Directory -Force -Path $baseDir | Out-Null } + + if (!(Test-Path $targetDir)) { + Write-Host "Cloning $url into $targetDir..." + git clone $url $targetDir + if ($LASTEXITCODE -ne 0) { return } + } else { + Write-Host "Directory $targetDir already exists. Entering it." + } + Set-Location $targetDir + Write-Host "Entered: $targetDir" + return + } + + # Worktree + if ($Command -eq "worktree") { + $wtName = $Arg1 + if ([string]::IsNullOrWhiteSpace($wtName)) { + Write-Error "Worktree command requires a name." + return + } + + if (-not (Is-GitRepo)) { + Write-Error "You must be inside a Git repository to use 'worktree'." + return + } + + $currentRepoRoot = git rev-parse --show-toplevel + $dateStr = Get-Date -Format "yyyy-MM-dd" + $targetDir = Join-Path $baseDir "$dateStr-$wtName" + + if (!(Test-Path $baseDir)) { New-Item -ItemType Directory -Force -Path $baseDir | Out-Null } + + if (!(Test-Path $targetDir)) { + Write-Host "Creating worktree '$wtName' from '$currentRepoRoot'..." + git worktree add $targetDir $wtName + if ($LASTEXITCODE -ne 0) { return } + } else { + Write-Host "Worktree directory $targetDir already exists. Entering it." + } + Set-Location $targetDir + Write-Host "Entered: $targetDir" + return + } + + # New Project or Direct Git URL + $projectName = $Command + if ($projectName -match "^http" -or $projectName -match "^git@") { + $repoName = ($projectName -split "/")[-1] -replace "\.git$", "" + $dateStr = Get-Date -Format "yyyy-MM-dd" + $targetDir = Join-Path $baseDir "$dateStr-$repoName" + + if (!(Test-Path $baseDir)) { New-Item -ItemType Directory -Force -Path $baseDir | Out-Null } + + if (!(Test-Path $targetDir)) { + git clone $projectName $targetDir + } else { + Write-Host "Directory $targetDir already exists." + } + Set-Location $targetDir + return + } + + $dateStr = Get-Date -Format "yyyy-MM-dd" + $targetDir = Join-Path $baseDir "$dateStr-$projectName" + + if (!(Test-Path $targetDir)) { New-Item -ItemType Directory -Force -Path $targetDir | Out-Null } + Set-Location $targetDir + Write-Host "Entered: $targetDir" +} + +# Alias: use "& try" to call +Set-Alias -Name try -Value Invoke-Try -Scope Global \ No newline at end of file diff --git a/contrib/windows/try.sh b/contrib/windows/try.sh new file mode 100644 index 0000000..7cae839 --- /dev/null +++ b/contrib/windows/try.sh @@ -0,0 +1,217 @@ +#!/bin/bash +# Windows Bash (Git Bash / MSYS2) integration for 'try' +# Add this to your ~/.bashrc: +# source /path/to/this/file/try.sh + +try() { + local base_dir="$HOME/src/tries" + local cmd="$1" + local arg1="$2" + local arg2="$3" + + # Helper to get unique directory name with versioning + get_unique_dir_name() { + local base_path="$1" + local date_prefix="$2" + local name="$3" + + local initial="$date_prefix-$name" + local full_path="$base_path/$initial" + if [ ! -d "$full_path" ]; then + echo "$name" + return + fi + + # Check if name ends with digits + if [[ "$name" =~ ^(.*[^0-9])([0-9]+)$ ]]; then + local stem="${BASH_REMATCH[1]}" + local num=$((${BASH_REMATCH[2]} + 1)) + while true; do + local candidate="$stem$num" + local candidate_full="$base_path/$date_prefix-$candidate" + if [ ! -d "$candidate_full" ]; then + echo "$candidate" + return + fi + ((num++)) + done + else + # No numeric suffix; use -2 style + local i=2 + while true; do + local candidate="$name-$i" + local candidate_full="$base_path/$date_prefix-$candidate" + if [ ! -d "$candidate_full" ]; then + echo "$candidate" + return + fi + ((i++)) + done + fi + } + + # Help + if [[ "$cmd" == "--help" ]] || [[ "$cmd" == "-h" ]]; then + echo "try - A simple temporary workspace manager (Bash Version)" + echo "" + echo "Usage:" + echo " try List up to 10 most recent experiment directories" + echo " try Create and enter a dated directory for a new project" + echo " try . Create a dated worktree dir for current repo" + echo " try ./path/to/repo [name] Use another repo as the worktree source" + echo " try clone [name] Clone a git repo into a dated directory and enter it" + echo " try worktree Create a git worktree from current repo and enter it" + echo " try --help (-h) Show this help message" + echo "" + echo "Directories are stored in: $base_dir" + return + fi + + # List recent tries + if [ -z "$cmd" ]; then + echo "Usage: try or try " + echo "Recent tries in $base_dir:" + mkdir -p "$base_dir" + ls -dt "$base_dir"/* 2>/dev/null | head -n 10 | while read -r path; do + basename "$path" + done + return + fi + + # Dot notation: try . [name] or try ./path/to/repo [name] + if [[ "$cmd" == "." ]] || [[ "$cmd" == ./* ]]; then + local repo_dir + local custom_name + + if [[ "$cmd" == "." ]]; then + repo_dir="$(pwd)" + custom_name="$arg1" + # "try ." requires a name argument (too easy to invoke accidentally) + if [ -z "$custom_name" ]; then + echo "Error: 'try .' requires a name argument." + echo "Usage: try . " + return 1 + fi + else + repo_dir="$(cd "$cmd" 2>/dev/null && pwd)" + if [ -z "$repo_dir" ]; then + echo "Error: Path not found: $cmd" + return 1 + fi + custom_name="$arg1" + fi + + # Determine base name + local base_name + if [ -n "$custom_name" ]; then + base_name="${custom_name// /-}" + else + base_name="$(basename "$repo_dir")" + fi + + local date_str=$(date +%Y-%m-%d) + base_name=$(get_unique_dir_name "$base_dir" "$date_str" "$base_name") + local target_dir="$base_dir/$date_str-$base_name" + + mkdir -p "$base_dir" + + # Check if it's a git repo and use worktree + if [ -d "$repo_dir/.git" ]; then + if [ ! -d "$target_dir" ]; then + echo "Creating worktree from '$repo_dir'..." + git -C "$repo_dir" worktree add --detach "$target_dir" 2>/dev/null || mkdir -p "$target_dir" + fi + else + # Not a git repo, just create directory + mkdir -p "$target_dir" + fi + + cd "$target_dir" + echo "Entered: $target_dir" + return + fi + + # Clone + if [[ "$cmd" == "clone" ]]; then + local url="$arg1" + local custom_name="$arg2" + + if [ -z "$url" ]; then + echo "Error: clone command requires a URL." + echo "Usage: try clone [name]" + return 1 + fi + + local repo_name=$(basename "$url" .git) + local project_name="${custom_name:-$repo_name}" + local date_str=$(date +%Y-%m-%d) + local target_dir="$base_dir/$date_str-$project_name" + + mkdir -p "$base_dir" + if [ ! -d "$target_dir" ]; then + echo "Cloning $url into $target_dir..." + git clone "$url" "$target_dir" || return $? + else + echo "Directory $target_dir already exists. Entering it." + fi + cd "$target_dir" + echo "Entered: $target_dir" + return + fi + + # Worktree + if [[ "$cmd" == "worktree" ]]; then + local wt_name="$arg1" + + if [ -z "$wt_name" ]; then + echo "Error: worktree command requires a name." + return 1 + fi + + if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Error: You must be inside a Git repository to use 'worktree'." + return 1 + fi + + local current_repo_root=$(git rev-parse --show-toplevel) + local date_str=$(date +%Y-%m-%d) + local target_dir="$base_dir/$date_str-$wt_name" + + mkdir -p "$base_dir" + if [ ! -d "$target_dir" ]; then + echo "Creating worktree '$wt_name' from '$current_repo_root'..." + git worktree add "$target_dir" "$wt_name" || return $? + else + echo "Worktree directory $target_dir already exists. Entering it." + fi + cd "$target_dir" + echo "Entered: $target_dir" + return + fi + + # New Project or Direct Git URL + local project_name="$cmd" + + # Handle direct Git URL as first argument + if [[ "$project_name" == http* ]] || [[ "$project_name" == git@* ]]; then + local repo_name=$(basename "$project_name" .git) + local date_str=$(date +%Y-%m-%d) + local target_dir="$base_dir/$date_str-$repo_name" + + mkdir -p "$base_dir" + if [ ! -d "$target_dir" ]; then + git clone "$project_name" "$target_dir" + else + echo "Directory $target_dir already exists." + fi + cd "$target_dir" + return + fi + + local date_str=$(date +%Y-%m-%d) + local target_dir="$base_dir/$date_str-$project_name" + + mkdir -p "$target_dir" + cd "$target_dir" + echo "Entered: $target_dir" +}