From 79faa386a78d51f2aeb39f84b90d713d27dca798 Mon Sep 17 00:00:00 2001 From: Ian Gregory Date: Mon, 22 Jun 2026 12:26:59 -0400 Subject: [PATCH] Make conflicts detectable with `errors.Is` --- applier.go | 10 +++++----- error.go | 17 +++++++++++++++++ graphql_applier.go | 13 ++++++------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/applier.go b/applier.go index 4730d70..0cca53b 100644 --- a/applier.go +++ b/applier.go @@ -91,7 +91,7 @@ func (a *Applier) applyCreate(ctx context.Context, f *gitdiff.File) (*github.Tre return nil, err } if exists { - return nil, errors.New("existing entry for new file") + return nil, ErrNewFileAlreadyExists } c, err := base64Apply(nil, f) @@ -119,7 +119,7 @@ func (a *Applier) applyDelete(ctx context.Context, f *gitdiff.File) (*github.Tre if !exists { // because the rest of application is strict, return an error if the // file was already deleted, since it indicates a conflict of some kind - return nil, errors.New("missing entry for deleted file") + return nil, ErrNoSuchFileToDelete } data, _, err := a.client.Git.GetBlobRaw(ctx, a.owner, a.repo, entry.GetSHA()) @@ -128,7 +128,7 @@ func (a *Applier) applyDelete(ctx context.Context, f *gitdiff.File) (*github.Tre } if err := gitdiff.Apply(ioutil.Discard, bytes.NewReader(data), f); err != nil { - return nil, err + return nil, wrapGitdiffApplyError(err) } path := f.OldName @@ -147,7 +147,7 @@ func (a *Applier) applyModify(ctx context.Context, f *gitdiff.File) (*github.Tre return nil, err } if !exists { - return nil, errors.New("no entry for modified file") + return nil, ErrNoSuchFileToModify } path := f.NewName @@ -346,7 +346,7 @@ func base64Apply(data []byte, f *gitdiff.File) (string, error) { enc := base64.NewEncoder(base64.StdEncoding, &b) if err := gitdiff.Apply(enc, bytes.NewReader(data), f); err != nil { - return "", err + return "", wrapGitdiffApplyError(err) } if err := enc.Close(); err != nil { return "", fmt.Errorf("base64 encoding failed: %w", err) diff --git a/error.go b/error.go index ba3ca64..6cf3275 100644 --- a/error.go +++ b/error.go @@ -3,8 +3,25 @@ package patch2pr import ( "errors" "fmt" + + "github.com/bluekeyes/go-gitdiff/gitdiff" +) + +var ( + ErrConflict = errors.New("conflict prevented applying patch") + + ErrNewFileAlreadyExists = fmt.Errorf("%w: existing entry for new file", ErrConflict) + ErrNoSuchFileToDelete = fmt.Errorf("%w: missing entry for deleted file", ErrConflict) + ErrNoSuchFileToModify = fmt.Errorf("%w: no entry for modified file", ErrConflict) ) +func wrapGitdiffApplyError(err error) error { + if errors.Is(err, &gitdiff.Conflict{}) { + return fmt.Errorf("%w: gitdiff apply failed: %w", ErrConflict, err) + } + return fmt.Errorf("gitdiff apply failed: %w", err) +} + func unsupported(msg string, args ...any) error { return unsupportedErr{reason: fmt.Sprintf(msg, args...)} } diff --git a/graphql_applier.go b/graphql_applier.go index fc4474c..e902437 100644 --- a/graphql_applier.go +++ b/graphql_applier.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/base64" - "errors" "fmt" "io/ioutil" "os" @@ -122,12 +121,12 @@ func (a *GraphQLApplier) applyCreate(ctx context.Context, f *gitdiff.File) error return err } if exists { - return errors.New("existing entry for new file") + return ErrNewFileAlreadyExists } var b bytes.Buffer if err := gitdiff.Apply(&b, bytes.NewReader(nil), f); err != nil { - return err + return wrapGitdiffApplyError(err) } a.changes[f.NewName] = pendingChange{Content: b.Bytes()} @@ -143,11 +142,11 @@ func (a *GraphQLApplier) applyDelete(ctx context.Context, f *gitdiff.File) error if !exists { // because the rest of application is strict, return an error if the // file was already deleted, since it indicates a conflict of some kind - return errors.New("missing entry for deleted file") + return ErrNoSuchFileToDelete } if err := gitdiff.Apply(ioutil.Discard, bytes.NewReader(data), f); err != nil { - return err + return wrapGitdiffApplyError(err) } a.changes[f.OldName] = pendingChange{IsDelete: true} @@ -160,13 +159,13 @@ func (a *GraphQLApplier) applyModify(ctx context.Context, f *gitdiff.File) error return err } if !exists { - return errors.New("no entry for modified file") + return ErrNoSuchFileToModify } if len(f.TextFragments) > 0 || f.BinaryFragment != nil { var b bytes.Buffer if err := gitdiff.Apply(&b, bytes.NewReader(data), f); err != nil { - return err + return wrapGitdiffApplyError(err) } data = b.Bytes() }