@@ -11,6 +11,7 @@ import (
1111 "path/filepath"
1212 "sort"
1313 "strings"
14+ "time"
1415
1516 "dagger.io/dagger"
1617 "github.com/dagger/container-use/environment"
@@ -420,5 +421,110 @@ func (r *Repository) Apply(ctx context.Context, id string, w io.Writer) error {
420421 return err
421422 }
422423
423- return RunInteractiveGitCommand (ctx , r .userRepoPath , w , "merge" , "--autostash" , "--squash" , "--" , "container-use/" + envInfo .ID )
424+ // Create patch directory if it doesn't exist
425+ configPath := os .ExpandEnv ("$HOME/.config/container-use" )
426+ patchDir := filepath .Join (configPath , "patches" )
427+ if err := os .MkdirAll (patchDir , 0755 ); err != nil {
428+ return fmt .Errorf ("failed to create patch directory: %w" , err )
429+ }
430+
431+ // Create a unique patch filename using timestamp and environment ID
432+ patchFile := filepath .Join (patchDir , fmt .Sprintf ("user-changes-%s-%d.patch" , envInfo .ID , time .Now ().Unix ()))
433+
434+ // Check if there are any unstaged changes
435+ diffCmd := exec .CommandContext (ctx , "git" , "diff" )
436+ diffCmd .Dir = r .userRepoPath
437+ diffOutput , err := diffCmd .Output ()
438+ if err != nil {
439+ return fmt .Errorf ("failed to check for unstaged changes: %w" , err )
440+ }
441+
442+ hasUnstagedChanges := len (diffOutput ) > 0
443+
444+ if hasUnstagedChanges {
445+ // Create a patch of only unstaged changes
446+ fmt .Fprintf (w , "Saving unstaged user changes to %s...\n " , patchFile )
447+
448+ // Create the patch from unstaged changes only
449+ patchCmd := exec .CommandContext (ctx , "git" , "diff" )
450+ patchCmd .Dir = r .userRepoPath
451+ patchOutput , err := patchCmd .Output ()
452+ if err != nil {
453+ return fmt .Errorf ("failed to create patch: %w" , err )
454+ }
455+
456+ // Write patch to file
457+ if err := os .WriteFile (patchFile , patchOutput , 0644 ); err != nil {
458+ return fmt .Errorf ("failed to write patch file: %w" , err )
459+ }
460+
461+ // Reset to clean state
462+ fmt .Fprintf (w , "Resetting to clean state...\n " )
463+ if err := RunInteractiveGitCommand (ctx , r .userRepoPath , w , "reset" , "--hard" , "HEAD" ); err != nil {
464+ return fmt .Errorf ("failed to reset: %w" , err )
465+ }
466+ }
467+
468+ // Apply the merge without autostash
469+ fmt .Fprintf (w , "Applying environment changes...\n " )
470+ if err := RunInteractiveGitCommand (ctx , r .userRepoPath , w , "merge" , "--squash" , "--" , "container-use/" + envInfo .ID ); err != nil {
471+ // If merge fails, try to restore user changes
472+ if hasUnstagedChanges {
473+ fmt .Fprintf (w , "Merge failed, restoring user changes...\n " )
474+ applyCmd := exec .CommandContext (ctx , "git" , "apply" , patchFile )
475+ applyCmd .Dir = r .userRepoPath
476+ applyCmd .Stdout = w
477+ applyCmd .Stderr = w
478+ applyCmd .Run () // Ignore error as patch might partially apply
479+ }
480+ return fmt .Errorf ("failed to merge: %w" , err )
481+ }
482+
483+ // Apply user changes back
484+ if hasUnstagedChanges {
485+ fmt .Fprintf (w , "Restoring user changes...\n " )
486+
487+ // 1. Temporarily commit the agent's changes
488+ commitCmd := exec .CommandContext (ctx , "git" , "commit" , "-m" , "temp: agent changes" )
489+ commitCmd .Dir = r .userRepoPath
490+ if err := commitCmd .Run (); err != nil {
491+ fmt .Fprintf (w , "Warning: Failed to commit agent changes: %v\n " , err )
492+ return nil
493+ }
494+
495+ // 2. Apply the user's patch
496+ applyCmd := exec .CommandContext (ctx , "git" , "apply" , patchFile )
497+ applyCmd .Dir = r .userRepoPath
498+ applyCmd .Stdout = w
499+ applyCmd .Stderr = w
500+ if err := applyCmd .Run (); err != nil {
501+ fmt .Fprintf (w , "Warning: Failed to apply some user changes. Patch saved at: %s\n " , patchFile )
502+ fmt .Fprintf (w , "You can manually apply it with: git apply %s\n " , patchFile )
503+ // Try to recover by doing soft reset
504+ resetCmd := exec .CommandContext (ctx , "git" , "reset" , "--soft" , "HEAD~1" )
505+ resetCmd .Dir = r .userRepoPath
506+ resetCmd .Run ()
507+ return nil
508+ }
509+
510+ // 3. Reset to unstage everything
511+ resetCmd := exec .CommandContext (ctx , "git" , "reset" )
512+ resetCmd .Dir = r .userRepoPath
513+ if err := resetCmd .Run (); err != nil {
514+ fmt .Fprintf (w , "Warning: Failed to reset: %v\n " , err )
515+ }
516+
517+ // 4. Soft reset to bring agent changes back to staging
518+ softResetCmd := exec .CommandContext (ctx , "git" , "reset" , "--soft" , "HEAD~1" )
519+ softResetCmd .Dir = r .userRepoPath
520+ if err := softResetCmd .Run (); err != nil {
521+ fmt .Fprintf (w , "Warning: Failed to restore agent changes to staging: %v\n " , err )
522+ }
523+
524+ // Clean up patch file on successful application
525+ os .Remove (patchFile )
526+ fmt .Fprintf (w , "User changes successfully restored as unstaged changes.\n " )
527+ }
528+
529+ return nil
424530}
0 commit comments