diff --git a/cmd/restic/cmd_check.go b/cmd/restic/cmd_check.go index 1bc9da687..093a1ac99 100644 --- a/cmd/restic/cmd_check.go +++ b/cmd/restic/cmd_check.go @@ -221,11 +221,15 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error { errorsFound := false suggestIndexRebuild := false + mixedFound := false for _, hint := range hints { switch hint.(type) { case *checker.ErrDuplicatePacks, *checker.ErrOldIndexFormat: Printf("%v\n", hint) suggestIndexRebuild = true + case *checker.ErrMixedPack: + Printf("%v\n", hint) + mixedFound = true default: Warnf("error: %v\n", hint) errorsFound = true @@ -235,6 +239,9 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error { if suggestIndexRebuild { Printf("This is non-critical, you can run `restic rebuild-index' to correct this\n") } + if mixedFound { + Printf("Mixed packs with tree and data blobs are non-critical, you can run `restic prune` to correct this.\n") + } if len(errs) > 0 { for _, err := range errs { diff --git a/internal/checker/checker.go b/internal/checker/checker.go index b972408f5..e479f0aff 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -66,6 +66,15 @@ func (e *ErrDuplicatePacks) Error() string { return fmt.Sprintf("pack %v contained in several indexes: %v", e.PackID, e.Indexes) } +// ErrMixedPack is returned when a pack is found that contains both tree and data blobs. +type ErrMixedPack struct { + PackID restic.ID +} + +func (e *ErrMixedPack) Error() string { + return fmt.Sprintf("pack %v contains a mix of tree and data blobs", e.PackID.Str()) +} + // ErrOldIndexFormat is returned when an index with the old format is // found. type ErrOldIndexFormat struct { @@ -141,6 +150,11 @@ func (c *Checker) LoadIndex(ctx context.Context) (hints []error, errs []error) { Indexes: packToIndex[packID], }) } + if c.masterIndex.IsMixedPack(packID) { + hints = append(hints, &ErrMixedPack{ + PackID: packID, + }) + } } err = c.repo.SetIndex(c.masterIndex) diff --git a/internal/checker/checker_test.go b/internal/checker/checker_test.go index 975415a87..c82375e3c 100644 --- a/internal/checker/checker_test.go +++ b/internal/checker/checker_test.go @@ -63,6 +63,14 @@ func checkData(chkr *checker.Checker) []error { ) } +func assertOnlyMixedPackHints(t *testing.T, hints []error) { + for _, err := range hints { + if _, ok := err.(*checker.ErrMixedPack); !ok { + t.Fatalf("expected mixed pack hint, got %v", err) + } + } +} + func TestCheckRepo(t *testing.T) { repodir, cleanup := test.Env(t, checkerTestData) defer cleanup() @@ -74,9 +82,9 @@ func TestCheckRepo(t *testing.T) { if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } - - if len(hints) > 0 { - t.Errorf("expected no hints, got %v: %v", len(hints), hints) + assertOnlyMixedPackHints(t, hints) + if len(hints) == 0 { + t.Fatal("expected mixed pack warnings, got none") } test.OKs(t, checkPacks(chkr)) @@ -100,10 +108,7 @@ func TestMissingPack(t *testing.T) { if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } - - if len(hints) > 0 { - t.Errorf("expected no hints, got %v: %v", len(hints), hints) - } + assertOnlyMixedPackHints(t, hints) errs = checkPacks(chkr) @@ -136,10 +141,7 @@ func TestUnreferencedPack(t *testing.T) { if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } - - if len(hints) > 0 { - t.Errorf("expected no hints, got %v: %v", len(hints), hints) - } + assertOnlyMixedPackHints(t, hints) errs = checkPacks(chkr) @@ -181,10 +183,7 @@ func TestUnreferencedBlobs(t *testing.T) { if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } - - if len(hints) > 0 { - t.Errorf("expected no hints, got %v: %v", len(hints), hints) - } + assertOnlyMixedPackHints(t, hints) test.OKs(t, checkPacks(chkr)) test.OKs(t, checkStruct(chkr)) @@ -269,9 +268,7 @@ func TestModifiedIndex(t *testing.T) { t.Logf("found expected error %v", err) } - if len(hints) > 0 { - t.Errorf("expected no hints, got %v: %v", len(hints), hints) - } + assertOnlyMixedPackHints(t, hints) } var checkerDuplicateIndexTestData = filepath.Join("testdata", "duplicate-packs-in-index-test-repo.tar.gz") @@ -421,10 +418,7 @@ func TestCheckerNoDuplicateTreeDecodes(t *testing.T) { if len(errs) > 0 { t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } - - if len(hints) > 0 { - t.Errorf("expected no hints, got %v: %v", len(hints), hints) - } + assertOnlyMixedPackHints(t, hints) test.OKs(t, checkPacks(chkr)) test.OKs(t, checkStruct(chkr)) @@ -572,8 +566,10 @@ func loadBenchRepository(t *testing.B) (*checker.Checker, restic.Repository, fun t.Fatalf("expected no errors, got %v: %v", len(errs), errs) } - if len(hints) > 0 { - t.Errorf("expected no hints, got %v: %v", len(hints), hints) + for _, err := range hints { + if _, ok := err.(*checker.ErrMixedPack); !ok { + t.Fatalf("expected mixed pack hint, got %v", err) + } } return chkr, repo, cleanup }