diff --git a/src/worker/git.nim b/src/worker/git.nim index b52f4e3..dbdc244 100644 --- a/src/worker/git.nim +++ b/src/worker/git.nim @@ -7,8 +7,9 @@ import std/[os, osproc, strutils, strformat, streams] import ./types import ./utils -proc runGit*(args: varargs[string], workDir: string = ""): tuple[output: string, exitCode: int] = +proc runGit*(args: openArray[string], workDir = ""): tuple[output: string, exitCode: int] = ## Run a git command and return output + exit code + ## Note: uses openArray instead of varargs to avoid binding issues var cmd = "git" var fullArgs: seq[string] = @[] @@ -25,7 +26,7 @@ proc runGit*(args: varargs[string], workDir: string = ""): tuple[output: string, result.exitCode = process.waitForExit() process.close() -proc runGitCheck*(args: varargs[string], workDir: string = ""): string = +proc runGitCheck*(args: openArray[string], workDir = ""): string = ## Run git command, raise GitError on failure let (output, code) = runGit(args, workDir) if code != 0: @@ -38,16 +39,16 @@ proc createWorktree*(taskId: string, fromBranch: string = "origin/integration"): let worktree = worktreePath(taskId) # Fetch latest (warn but continue on failure - may be offline) - let (fetchOut, fetchCode) = runGit("fetch", "origin") + let (fetchOut, fetchCode) = runGit(["fetch", "origin"]) if fetchCode != 0: logWarn("createWorktree", "fetch failed, continuing with local state: " & fetchOut.strip()) # Create branch from base - discard runGitCheck("branch", branch, fromBranch) + discard runGitCheck(["branch", branch, fromBranch]) # Create worktree createDir(parentDir(worktree)) - discard runGitCheck("worktree", "add", worktree, branch) + discard runGitCheck(["worktree", "add", worktree, branch]) return (branch, worktree) @@ -55,33 +56,33 @@ proc removeWorktree*(taskId: string) = ## Remove a worktree let worktree = worktreePath(taskId) if dirExists(worktree): - let (output, code) = runGit("worktree", "remove", "--force", worktree) + let (output, code) = runGit(["worktree", "remove", "--force", worktree]) if code != 0: logWarn("removeWorktree", "failed for " & taskId & ": " & output.strip()) proc removeBranch*(taskId: string, remote: bool = true) = ## Remove feature branch let branch = branchName(taskId) - let (localOut, localCode) = runGit("branch", "-D", branch) + let (localOut, localCode) = runGit(["branch", "-D", branch]) if localCode != 0: logWarn("removeBranch", "local delete failed for " & branch & ": " & localOut.strip()) if remote: - let (remoteOut, remoteCode) = runGit("push", "origin", "--delete", branch) + let (remoteOut, remoteCode) = runGit(["push", "origin", "--delete", branch]) if remoteCode != 0: logWarn("removeBranch", "remote delete failed for " & branch & ": " & remoteOut.strip()) proc rebaseOnIntegration*(worktree: string): bool = ## Rebase worktree on integration branch. Returns false on conflict. - let (fetchOut, fetchCode) = runGit("fetch", "origin", workDir = worktree) + let (fetchOut, fetchCode) = runGit(["fetch", "origin"], workDir = worktree) if fetchCode != 0: logWarn("rebaseOnIntegration", "fetch failed, using local state: " & fetchOut.strip()) - let (output, code) = runGit("rebase", "origin/integration", workDir = worktree) + let (output, code) = runGit(["rebase", "origin/integration"], workDir = worktree) if code != 0: if "conflict" in output.toLowerAscii(): return false # Other error - abort and raise - discard runGit("rebase", "--abort", workDir = worktree) + discard runGit(["rebase", "--abort"], workDir = worktree) raise newException(GitError, &"Rebase failed: {output}") return true @@ -93,7 +94,7 @@ proc isRebaseInProgress*(worktree: string): bool = proc pushBranch*(worktree: string, branch: string) = ## Push branch to origin - discard runGitCheck("push", "-u", "origin", branch, workDir = worktree) + discard runGitCheck(["push", "-u", "origin", branch], workDir = worktree) proc mergeToIntegration*(taskId: string, maxRetries: int = 3): bool = ## Merge feature branch to integration with retry loop @@ -101,23 +102,23 @@ proc mergeToIntegration*(taskId: string, maxRetries: int = 3): bool = for attempt in 1..maxRetries: # Fetch latest - discard runGitCheck("fetch", "origin", "integration", branch) + discard runGitCheck(["fetch", "origin", "integration", branch]) # Checkout and reset integration - discard runGitCheck("checkout", "integration") - discard runGitCheck("reset", "--hard", "origin/integration") + discard runGitCheck(["checkout", "integration"]) + discard runGitCheck(["reset", "--hard", "origin/integration"]) # Merge - let (mergeOutput, mergeCode) = runGit("merge", "--no-ff", branch, - "-m", &"Merge {branch}") + let (mergeOutput, mergeCode) = runGit(["merge", "--no-ff", branch, + "-m", &"Merge {branch}"]) if mergeCode != 0: if "conflict" in mergeOutput.toLowerAscii(): - discard runGit("merge", "--abort") + discard runGit(["merge", "--abort"]) return false raise newException(GitError, &"Merge failed: {mergeOutput}") # Push - let (pushOutput, pushCode) = runGit("push", "origin", "integration") + let (pushOutput, pushCode) = runGit(["push", "origin", "integration"]) if pushCode == 0: return true @@ -132,16 +133,15 @@ proc mergeToIntegration*(taskId: string, maxRetries: int = 3): bool = proc getConflictedFiles*(worktree: string): seq[string] = ## Get list of files with conflicts - let (output, _) = runGit("diff", "--name-only", "--diff-filter=U", - workDir = worktree) + let (output, _) = runGit(["diff", "--name-only", "--diff-filter=U"], workDir = worktree) for line in output.splitLines(): if line.strip() != "": result.add(line.strip()) proc getBranchStatus*(worktree: string): tuple[ahead, behind: int] = ## Get commits ahead/behind integration - let (output, code) = runGit("rev-list", "--left-right", "--count", - "origin/integration...HEAD", workDir = worktree) + let (output, code) = runGit(["rev-list", "--left-right", "--count", + "origin/integration...HEAD"], workDir = worktree) if code != 0: return (0, 0)