@@ -10,10 +10,9 @@ import (
1010 "strings"
1111 "sync"
1212
13- "github.com/loft-sh/devspace/pkg/util/encoding"
14-
1513 "github.com/loft-sh/devspace/pkg/devspace/config/constants"
1614 "github.com/loft-sh/devspace/pkg/devspace/config/versions/latest"
15+ "github.com/loft-sh/devspace/pkg/util/encoding"
1716 "github.com/loft-sh/devspace/pkg/util/git"
1817 "github.com/loft-sh/devspace/pkg/util/log"
1918 "github.com/mitchellh/go-homedir"
@@ -146,12 +145,18 @@ func DownloadDependency(ctx context.Context, workingDirectory string, source *la
146145
147146 // Git pull
148147 if ! source .DisablePull && source .Revision == "" {
149- err = repo .Pull (ctx )
150- if err != nil {
151- log .Warn (err )
152- }
148+ if err := repo .Pull (ctx ); err != nil {
149+ log .Infof ("Git pull failed for dependency '%s': %v" , gitPath , err )
150+ log .Infof ("Attempting automatic recovery by re-cloning" )
151+
152+ if err := recloneDependency (ctx , localPath , gitCloneOptions , gitPath , log ); err != nil {
153+ return "" , errors .Wrap (err , "git pull failed and automatic recovery failed" )
154+ }
153155
154- log .Debugf ("Pulled %s" , gitPath )
156+ log .Donef ("Successfully recovered dependency '%s'" , gitPath )
157+ } else {
158+ log .Debugf ("Pulled %s" , gitPath )
159+ }
155160 }
156161
157162 // Resolve local source
@@ -257,3 +262,41 @@ func GetDependencyID(source *latest.SourceConfig) (string, error) {
257262func isURL (path string ) bool {
258263 return strings .HasPrefix (path , "http://" ) || strings .HasPrefix (path , "https://" )
259264}
265+
266+ func recloneDependency (ctx context.Context , localPath string , gitCloneOptions git.CloneOptions , gitPath string , log log.Logger ) error {
267+ // Clone to temporary location to avoid losing old version if clone fails
268+ tmpPath := localPath + ".tmp-reclone"
269+
270+ // Clean up any leftover tmp directory from previous failed attempts
271+ _ = os .RemoveAll (tmpPath )
272+
273+ // Create new repository in tmp location
274+ repo , err := git .NewGitCLIRepository (ctx , tmpPath )
275+ if err != nil {
276+ return errors .Wrap (err , "create temporary git repository" )
277+ }
278+
279+ // Try to clone
280+ err = repo .Clone (ctx , gitCloneOptions )
281+ if err != nil {
282+ gitCloneOptions .URL = switchURLType (gitPath )
283+ err = repo .Clone (ctx , gitCloneOptions )
284+ if err != nil {
285+ _ = os .RemoveAll (tmpPath )
286+ return errors .Wrap (err , "clone repository" )
287+ }
288+ }
289+
290+ // Clone succeeded - now replace old version atomically
291+ if err := os .RemoveAll (localPath ); err != nil {
292+ _ = os .RemoveAll (tmpPath )
293+ return errors .Wrap (err , "remove old dependency cache" )
294+ }
295+
296+ // Atomic rename (both paths in same directory, so same filesystem)
297+ if err := os .Rename (tmpPath , localPath ); err != nil {
298+ return errors .Wrap (err , "move new dependency into place" )
299+ }
300+
301+ return nil
302+ }
0 commit comments