@@ -105,30 +105,25 @@ async function copyRepoContents(sourceRepoPath: string, workspacePath: string):
105105 }
106106}
107107
108- async function overlayGitWorkingTreeChanges ( sourceRepoPath : string , workspacePath : string ) : Promise < void > {
109- const rawStatus = await execFileStdout ( "git" , [ "status" , "--porcelain" ] , sourceRepoPath ) ;
110- const entries = rawStatus
111- . split ( "\n" )
112- . map ( ( line ) => line . trimEnd ( ) )
113- . filter ( Boolean ) ;
114-
115- for ( const entry of entries ) {
116- const status = entry . slice ( 0 , 2 ) ;
117- const rawPath = entry . slice ( 3 ) ;
118- const path = rawPath . includes ( " -> " ) ? rawPath . split ( " -> " ) . at ( - 1 ) ! : rawPath ;
119- if ( ! path || path . startsWith ( ".git" ) || path . startsWith ( ".devagent-runner" ) || path . startsWith ( "node_modules" ) ) {
120- continue ;
121- }
122-
123- const sourcePath = join ( sourceRepoPath , path ) ;
124- const workspacePathTarget = join ( workspacePath , path ) ;
125- if ( status . includes ( "D" ) ) {
126- await rm ( workspacePathTarget , { recursive : true , force : true } ) ;
127- continue ;
128- }
129-
130- await mkdir ( dirname ( workspacePathTarget ) , { recursive : true } ) ;
131- await cp ( sourcePath , workspacePathTarget , { recursive : true } ) ;
108+ async function hasDirtyWorkingTree ( sourceRepoPath : string ) : Promise < boolean > {
109+ try {
110+ const rawStatus = await execFileStdout ( "git" , [ "status" , "--porcelain" ] , sourceRepoPath ) ;
111+ return rawStatus
112+ . split ( "\n" )
113+ . map ( ( line ) => line . trimEnd ( ) )
114+ . some ( ( line ) => {
115+ if ( ! line ) return false ;
116+ const rawPath = line . slice ( 3 ) ;
117+ const path = rawPath . includes ( " -> " ) ? rawPath . split ( " -> " ) . at ( - 1 ) ! : rawPath ;
118+ return Boolean (
119+ path &&
120+ ! path . startsWith ( ".git" ) &&
121+ ! path . startsWith ( ".devagent-runner" ) &&
122+ ! path . startsWith ( "node_modules" ) ,
123+ ) ;
124+ } ) ;
125+ } catch {
126+ return false ;
132127 }
133128}
134129
@@ -248,7 +243,6 @@ export class FileSystemWorkspaceManager implements WorkspaceManager {
248243 ? [ "worktree" , "add" , workspacePath , spec . workBranch ]
249244 : [ "worktree" , "add" , "-B" , spec . workBranch , workspacePath , spec . baseRef ?? "HEAD" ] ;
250245 await execFileAsync ( "git" , worktreeArgs , spec . sourceRepoPath ) ;
251- await overlayGitWorkingTreeChanges ( spec . sourceRepoPath , workspacePath ) ;
252246 await linkSharedDependencies ( spec . sourceRepoPath , workspacePath ) ;
253247 return { workspacePath } ;
254248 } catch {
@@ -353,6 +347,20 @@ export class LocalRunner implements RunnerClient {
353347 }
354348 } ;
355349
350+ if (
351+ request . workspace . isolation === "git-worktree" &&
352+ await hasDirtyWorkingTree ( request . workspace . sourceRepoPath )
353+ ) {
354+ onEvent ( {
355+ protocolVersion : PROTOCOL_VERSION ,
356+ type : "log" ,
357+ at : new Date ( ) . toISOString ( ) ,
358+ taskId : request . taskId ,
359+ stream : "stdout" ,
360+ message : "Runner ignored local uncommitted source-repo changes and used a clean isolated workspace." ,
361+ } ) ;
362+ }
363+
356364 const handle = await adapter . launch ( request , workspacePath , artifactDir , onEvent ) ;
357365 const metadata : RunMetadata = {
358366 runId : handle . id ,
0 commit comments