Skip to content

Commit 6203ae7

Browse files
GiteaBotwolfogre
andauthored
Distinguish LFS object errors to ignore missing objects during migration (#31702) (#31745)
Backport #31702 by @wolfogre Fix #31137. Replace #31623 #31697. When migrating LFS objects, if there's any object that failed (like some objects are losted, which is not really critical), Gitea will stop migrating LFS immediately but treat the migration as successful. This PR checks the error according to the [LFS api doc](https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md#successful-responses). > LFS object error codes should match HTTP status codes where possible: > > - 404 - The object does not exist on the server. > - 409 - The specified hash algorithm disagrees with the server's acceptable options. > - 410 - The object was removed by the owner. > - 422 - Validation error. If the error is `404`, it's safe to ignore it and continue migration. Otherwise, stop the migration and mark it as failed to ensure data integrity of LFS objects. And maybe we should also ignore others errors (maybe `410`? I'm not sure what's the difference between "does not exist" and "removed by the owner".), we can add it later when some users report that they have failed to migrate LFS because of an error which should be ignored. Co-authored-by: Jason Song <[email protected]>
1 parent 8591c91 commit 6203ae7

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

modules/lfs/http_client.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,13 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc
136136

137137
for _, object := range result.Objects {
138138
if object.Error != nil {
139-
objectError := errors.New(object.Error.Message)
140-
log.Trace("Error on object %v: %v", object.Pointer, objectError)
139+
log.Trace("Error on object %v: %v", object.Pointer, object.Error)
141140
if uc != nil {
142-
if _, err := uc(object.Pointer, objectError); err != nil {
141+
if _, err := uc(object.Pointer, object.Error); err != nil {
143142
return err
144143
}
145144
} else {
146-
if err := dc(object.Pointer, nil, objectError); err != nil {
145+
if err := dc(object.Pointer, nil, object.Error); err != nil {
147146
return err
148147
}
149148
}

modules/lfs/shared.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
package lfs
55

66
import (
7+
"errors"
8+
"fmt"
79
"time"
10+
11+
"code.gitea.io/gitea/modules/util"
812
)
913

1014
const (
@@ -63,6 +67,39 @@ type ObjectError struct {
6367
Message string `json:"message"`
6468
}
6569

70+
var (
71+
// See https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md#successful-responses
72+
// LFS object error codes should match HTTP status codes where possible:
73+
// 404 - The object does not exist on the server.
74+
// 409 - The specified hash algorithm disagrees with the server's acceptable options.
75+
// 410 - The object was removed by the owner.
76+
// 422 - Validation error.
77+
78+
ErrObjectNotExist = util.ErrNotExist // the object does not exist on the server
79+
ErrObjectHashMismatch = errors.New("the specified hash algorithm disagrees with the server's acceptable options")
80+
ErrObjectRemoved = errors.New("the object was removed by the owner")
81+
ErrObjectValidation = errors.New("validation error")
82+
)
83+
84+
func (e *ObjectError) Error() string {
85+
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
86+
}
87+
88+
func (e *ObjectError) Unwrap() error {
89+
switch e.Code {
90+
case 404:
91+
return ErrObjectNotExist
92+
case 409:
93+
return ErrObjectHashMismatch
94+
case 410:
95+
return ErrObjectRemoved
96+
case 422:
97+
return ErrObjectValidation
98+
default:
99+
return errors.New(e.Message)
100+
}
101+
}
102+
66103
// PointerBlob associates a Git blob with a Pointer.
67104
type PointerBlob struct {
68105
Hash string

modules/repository/repo.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package repository
55

66
import (
77
"context"
8+
"errors"
89
"fmt"
910
"io"
1011
"strings"
@@ -181,6 +182,10 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
181182
downloadObjects := func(pointers []lfs.Pointer) error {
182183
err := lfsClient.Download(ctx, pointers, func(p lfs.Pointer, content io.ReadCloser, objectError error) error {
183184
if objectError != nil {
185+
if errors.Is(objectError, lfs.ErrObjectNotExist) {
186+
log.Warn("Repo[%-v]: Ignore missing LFS object %-v: %v", repo, p, objectError)
187+
return nil
188+
}
184189
return objectError
185190
}
186191

services/repository/migrate.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
169169
lfsClient := lfs.NewClient(endpoint, httpTransport)
170170
if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, repo, gitRepo, lfsClient); err != nil {
171171
log.Error("Failed to store missing LFS objects for repository: %v", err)
172+
return repo, fmt.Errorf("StoreMissingLfsObjectsInRepository: %w", err)
172173
}
173174
}
174175
}

0 commit comments

Comments
 (0)