# Gitea Workspace Git Operations # # Workspace-level git integration with Gitea # # Version: 1.0.0 # Selective imports (ADR-025 Phase 3 Layer 2). use domain/gitea/api_client.nu [create-repository get-gitea-config get-repository] # Initialize workspace as git repository export def init-workspace-git [ workspace_path: string workspace_name: string --remote: string = "gitea" # gitea, github, gitlab, none --private: bool = true ] -> bool { let ws_path = $workspace_path | path expand if not ($ws_path | path exists) { error make { msg: $"Workspace path does not exist: ($ws_path)" } } # Initialize git repository cd $ws_path ^git init # Configure git ^git config user.name "Provisioning System" ^git config user.email "provisioning@system.local" # Create initial commit ^git add . ^git commit -m "Initial workspace commit" # Create main branch ^git branch -M main # Add remote if requested if $remote == "gitea" { let config = get-gitea-config let org = $config.repositories.workspaces_org # Create repository on Gitea let repo = create-repository $org $workspace_name $"Workspace: ($workspace_name)" $private let repo_url = $repo.clone_url # Add remote ^git remote add origin $repo_url # Push initial commit ^git push -u origin main print $"✓ Workspace initialized with Gitea remote: ($repo_url)" } else if $remote != "none" { print $"⚠️ Remote type '($remote)' not yet implemented" } true } # Create repository on Gitea for workspace export def create-workspace-repo [ workspace_name: string --description: string = "" --private: bool = true ] -> record { let config = get-gitea-config let org = $config.repositories.workspaces_org let desc = if ($description | is-empty) { $"Provisioning workspace: ($workspace_name)" } else { $description } create-repository $org $workspace_name $desc $private } # Clone workspace from Gitea export def clone-workspace [ workspace_identifier: string # org/workspace-name or just workspace-name destination_path: string --branch: string = "main" ] -> bool { let config = get-gitea-config # Parse workspace identifier let parts = $workspace_identifier | split row "/" let repo_name = if ($parts | length) == 2 { $parts.1 } else { $workspace_identifier } let org = if ($parts | length) == 2 { $parts.0 } else { $config.repositories.workspaces_org } # Get repository info let repo = get-repository $org $repo_name let clone_url = $repo.clone_url # Clone repository ^git clone --branch $branch $clone_url $destination_path print $"✓ Workspace cloned from ($clone_url)" true } # Push workspace changes export def push-workspace [ workspace_path: string commit_message?: string --branch: string = "main" --add-all: bool = true ] -> bool { let ws_path = $workspace_path | path expand cd $ws_path # Check if git repository if not ($"($ws_path)/.git" | path exists) { error make { msg: "Not a git repository" help: "Initialize with 'workspace init --git'" } } # Add changes if $add_all { ^git add . } # Check if there are changes let status = ^git status --porcelain | complete if ($status.stdout | is-empty) { print "No changes to commit" return false } # Commit changes let msg = if ($commit_message | is-empty) { let timestamp = date now | format date "%Y-%m-%d %H:%M:%S" $"Workspace update: ($timestamp)" } else { $commit_message } ^git commit -m $msg # Push to remote ^git push origin $branch print $"✓ Workspace changes pushed to ($branch)" true } # Pull workspace changes export def pull-workspace [ workspace_path: string --branch: string = "main" ] -> bool { let ws_path = $workspace_path | path expand cd $ws_path # Check if git repository if not ($"($ws_path)/.git" | path exists) { error make { msg: "Not a git repository" } } # Pull changes ^git pull origin $branch print $"✓ Workspace updated from ($branch)" true } # Create branch export def create-workspace-branch [ workspace_path: string branch_name: string --from-branch: string = "main" ] -> bool { let ws_path = $workspace_path | path expand cd $ws_path # Create local branch ^git checkout -b $branch_name $from_branch # Push to remote ^git push -u origin $branch_name print $"✓ Branch created: ($branch_name)" true } # Switch branch export def switch-workspace-branch [ workspace_path: string branch_name: string ] -> bool { let ws_path = $workspace_path | path expand cd $ws_path # Switch branch ^git checkout $branch_name print $"✓ Switched to branch: ($branch_name)" true } # List branches export def list-workspace-branches [ workspace_path: string --remote: bool = false ] -> list { let ws_path = $workspace_path | path expand cd $ws_path if $remote { ^git branch -r | lines | each {|line| $line | str trim | str replace "origin/" ""} } else { ^git branch | lines | each {|line| $line | str trim | str replace "* " ""} } } # Delete branch export def delete-workspace-branch [ workspace_path: string branch_name: string --remote: bool = false --force: bool = false ] -> bool { let ws_path = $workspace_path | path expand cd $ws_path # Delete local branch if $force { ^git branch -D $branch_name } else { ^git branch -d $branch_name } # Delete remote branch if $remote { ^git push origin --delete $branch_name } print $"✓ Branch deleted: ($branch_name)" true } # Sync workspace (pull + push) export def sync-workspace [ workspace_path: string --branch: string = "main" --commit-message?: string ] -> record { let ws_path = $workspace_path | path expand # Pull latest changes pull-workspace $ws_path --branch $branch # Push local changes let pushed = push-workspace $ws_path $commit_message --branch $branch { pulled: true pushed: $pushed branch: $branch } } # Get workspace git status export def get-workspace-git-status [ workspace_path: string ] -> record { let ws_path = $workspace_path | path expand cd $ws_path # Check if git repository if not ($"($ws_path)/.git" | path exists) { return { is_git_repo: false branch: null status: null remote: null } } # Get current branch let branch = ^git branch --show-current | str trim # Get status let status = ^git status --porcelain | lines # Get remote let remote_result = (do { ^git remote get-url origin } | complete) let remote = if $remote_result.exit_code == 0 { $remote_result.stdout | str trim } else { null } # Get last commit let commit_result = (do { ^git log -1 --format="%H|%an|%ae|%at|%s" } | complete) let last_commit = if $commit_result.exit_code == 0 { $commit_result.stdout | str trim } else { null } let commit_info = if ($last_commit | is-not-empty) { let parts = $last_commit | split row "|" { hash: $parts.0 author_name: $parts.1 author_email: $parts.2 timestamp: ($parts.3 | into int) message: $parts.4 } } else { null } { is_git_repo: true branch: $branch status: $status remote: $remote last_commit: $commit_info has_changes: ($status | length) > 0 } } # Get workspace remote info export def get-workspace-remote-info [ workspace_path: string ] -> record { let ws_path = $workspace_path | path expand cd $ws_path let remote_result = (do { ^git remote get-url origin } | complete) if $remote_result.exit_code != 0 { return {has_remote: false} } let remote_url = $remote_result.stdout | str trim # Parse Gitea URL let config = get-gitea-config let base_url = if $config.mode == "local" { $"http://localhost:($config.local.port)" } else { $config.remote.url } let is_gitea = $remote_url | str starts-with $base_url if $is_gitea { # Extract owner and repo from URL let parts = $remote_url | str replace $"($base_url)/" "" | str replace ".git" "" | split row "/" { has_remote: true type: "gitea" url: $remote_url owner: $parts.0 repo: $parts.1 } } else { { has_remote: true type: "external" url: $remote_url } } } # Check if workspace has uncommitted changes export def has-uncommitted-changes [ workspace_path: string ] -> bool { let status = get-workspace-git-status $workspace_path $status.has_changes } # Get workspace diff export def get-workspace-diff [ workspace_path: string --staged: bool = false ] -> string { let ws_path = $workspace_path | path expand cd $ws_path if $staged { ^git diff --staged } else { ^git diff } } # Stash workspace changes export def stash-workspace-changes [ workspace_path: string message?: string ] -> bool { let ws_path = $workspace_path | path expand cd $ws_path let msg = if ($message | is-empty) { "Workspace stash" } else { $message } ^git stash push -m $msg print "✓ Changes stashed" true } # Pop stashed changes export def pop-workspace-stash [ workspace_path: string ] -> bool { let ws_path = $workspace_path | path expand cd $ws_path ^git stash pop print "✓ Stash applied" true } # List workspace stashes export def list-workspace-stashes [ workspace_path: string ] -> list { let ws_path = $workspace_path | path expand cd $ws_path ^git stash list | lines }