diff --git a/pkg/git_repo/base.go b/pkg/git_repo/base.go index 04204e3216..df85eb8fa4 100644 --- a/pkg/git_repo/base.go +++ b/pkg/git_repo/base.go @@ -225,7 +225,31 @@ func (repo *Base) createPatch(ctx context.Context, repoPath, gitDir, repoID, wor var desc *true_git.PatchDescriptor if hasSubmodules { + var retryCount int + + TryCreatePatch: desc, err = true_git.PatchWithSubmodules(ctx, fileHandler, gitDir, workTreeCacheDir, true_git.PatchOptions(opts)) + + if true_git.IsCommitsNotPresentError(err) && retryCount == 0 { + logboek.Context(ctx).Default().LogF("Detected not present commits when creating patch: %s\n", err) + logboek.Context(ctx).Default().LogF("Will switch worktree to original commit %q and retry\n", opts.FromCommit) + + if err := fileHandler.Truncate(0); err != nil { + return nil, fmt.Errorf("unable to truncate file %s: %w", tmpFile, err) + } + if _, err := fileHandler.Seek(0, 0); err != nil { + return nil, fmt.Errorf("unable to reset file %s: %w", tmpFile, err) + } + + if err := true_git.WithWorkTree(ctx, gitDir, workTreeCacheDir, opts.FromCommit, true_git.WithWorkTreeOptions{HasSubmodules: true}, func(workTreeDir string) error { + return nil + }); err != nil { + return nil, fmt.Errorf("unable to switch worktree to commit %q: %w", opts.FromCommit, err) + } + + retryCount++ + goto TryCreatePatch + } } else { desc, err = true_git.Patch(ctx, fileHandler, gitDir, true_git.PatchOptions(opts)) } diff --git a/pkg/true_git/diff_parser.go b/pkg/true_git/diff_parser.go index fdbdf9ab77..eba2b0a869 100644 --- a/pkg/true_git/diff_parser.go +++ b/pkg/true_git/diff_parser.go @@ -2,6 +2,7 @@ package true_git import ( "bytes" + "errors" "fmt" "io" "os" @@ -13,6 +14,12 @@ import ( "github.com/werf/werf/pkg/util" ) +var ErrCommitsNotPresent = errors.New("commits not present") + +func IsCommitsNotPresentError(err error) bool { + return err != nil && strings.HasSuffix(err.Error(), ErrCommitsNotPresent.Error()) +} + func makeDiffParser(out io.Writer, pathScope string, pathMatcher path_matcher.PathMatcher, fileRenames map[string]string) *diffParser { return &diffParser{ PathScope: pathScope, @@ -379,7 +386,7 @@ func (p *diffParser) handleModifyFilePathB(line string) error { func (p *diffParser) handleSubmoduleLine(line string) error { p.state = unrecognized if strings.HasSuffix(line, " (commits not present)") { - return fmt.Errorf("cannot handle \"commits not present\" in git diff line %q, check specified submodule commits are correct", line) + return fmt.Errorf("cannot handle git diff line %q, check specified commits are correct: %w", line, ErrCommitsNotPresent) } return nil }