git-lfs/commands/command_fsck.go

131 lines
2.7 KiB
Go
Raw Normal View History

2015-05-04 05:36:34 +00:00
package commands
import (
"crypto/sha256"
"encoding/hex"
"io"
"os"
"path/filepath"
2016-11-15 17:01:18 +00:00
"github.com/git-lfs/git-lfs/config"
"github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/lfs"
"github.com/spf13/cobra"
2015-05-04 05:36:34 +00:00
)
var (
2015-05-08 16:35:19 +00:00
fsckDryRun bool
2015-05-04 05:36:34 +00:00
)
func doFsck() (bool, error) {
requireInRepo()
2015-05-04 05:36:34 +00:00
ref, err := git.CurrentRef()
if err != nil {
return false, err
2015-05-04 05:36:34 +00:00
}
// The LFS scanner methods return unexported *lfs.wrappedPointer objects.
// All we care about is the pointer OID and file name
pointerIndex := make(map[string]string)
gitscanner := lfs.NewGitScanner()
defer gitscanner.Close()
pointerCh, err := gitscanner.ScanRefWithDeleted(ref.Sha)
2015-05-04 05:36:34 +00:00
if err != nil {
return false, err
2015-05-04 05:36:34 +00:00
}
2016-11-16 20:25:05 +00:00
for p := range pointerCh.Results {
pointerIndex[p.Oid] = p.Name
}
2016-11-16 20:25:05 +00:00
if err := pointerCh.Wait(); err != nil {
return false, err
}
2016-11-18 18:48:35 +00:00
p2, err := gitscanner.ScanIndex("HEAD")
2015-05-04 05:36:34 +00:00
if err != nil {
return false, err
2015-05-04 05:36:34 +00:00
}
2016-11-18 18:48:35 +00:00
for p := range p2.Results {
pointerIndex[p.Oid] = p.Name
}
2015-05-04 05:36:34 +00:00
2016-11-18 18:48:35 +00:00
if err := p2.Wait(); err != nil {
return false, err
}
ok := true
for oid, name := range pointerIndex {
path := lfs.LocalMediaPathReadOnly(oid)
2015-05-04 05:36:34 +00:00
Debug("Examining %v (%v)", name, path)
2015-05-04 05:36:34 +00:00
f, err := os.Open(path)
if pErr, pOk := err.(*os.PathError); pOk {
Print("Object %s (%s) could not be checked: %s", name, oid, pErr.Err)
ok = false
continue
}
2015-05-04 05:36:34 +00:00
if err != nil {
return false, err
2015-05-04 05:36:34 +00:00
}
oidHash := sha256.New()
_, err = io.Copy(oidHash, f)
f.Close()
if err != nil {
return false, err
2015-05-04 05:36:34 +00:00
}
recalculatedOid := hex.EncodeToString(oidHash.Sum(nil))
if recalculatedOid != oid {
ok = false
Print("Object %s (%s) is corrupt", name, oid)
if fsckDryRun {
continue
2015-05-08 16:35:19 +00:00
}
badDir := filepath.Join(config.LocalGitStorageDir, "lfs", "bad")
if err := os.MkdirAll(badDir, 0755); err != nil {
return false, err
}
badFile := filepath.Join(badDir, oid)
if err := os.Rename(path, badFile); err != nil {
return false, err
}
Print(" moved to %s", badFile)
2015-05-04 05:36:34 +00:00
}
}
return ok, nil
2015-05-04 05:36:34 +00:00
}
// TODO(zeroshirts): 'git fsck' reports status (percentage, current#/total) as
// it checks... we should do the same, as we are rehashing potentially gigs and
// gigs of content.
//
// NOTE(zeroshirts): Ideally git would have hooks for fsck such that we could
// chain a lfs-fsck, but I don't think it does.
func fsckCommand(cmd *cobra.Command, args []string) {
lfs.InstallHooks(false)
ok, err := doFsck()
2015-05-04 05:36:34 +00:00
if err != nil {
Panic(err, "Error checking Git LFS files")
}
if ok {
Print("Git LFS fsck OK")
2015-05-04 05:36:34 +00:00
}
}
func init() {
RegisterCommand("fsck", fsckCommand, func(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&fsckDryRun, "dry-run", "d", false, "List corrupt objects without deleting them.")
})
2015-05-04 05:36:34 +00:00
}