package commands import ( "io" "os" "github.com/git-lfs/git-lfs/v3/errors" "github.com/git-lfs/git-lfs/v3/lfs" "github.com/git-lfs/git-lfs/v3/tools" "github.com/git-lfs/git-lfs/v3/tr" "github.com/spf13/cobra" ) // clean cleans an object read from the given `io.Reader`, "from", and writes // out a corresponding pointer to the `io.Writer`, "to". If there were any // errors encountered along the way, they will be returned immediately if the // error is non-fatal, otherwise they will halt using the built in // `commands.Panic`. // // If fileSize is given as a non-negative (>= 0) integer, that value is used // with preference to os.Stat(fileName).Size(). If it is given as negative, the // value from the `stat(1)` call will be used instead. // // If the object read from "from" is _already_ a clean pointer, then it will be // written out verbatim to "to", without trying to make it a pointer again. func clean(gf *lfs.GitFilter, to io.Writer, from io.Reader, fileName string, fileSize int64) (*lfs.Pointer, error) { var cb tools.CopyCallback var file *os.File if len(fileName) > 0 { stat, err := os.Stat(fileName) if err == nil && stat != nil { if fileSize < 0 { fileSize = stat.Size() } localCb, localFile, err := gf.CopyCallbackFile("clean", fileName, 1, 1) if err != nil { Error(err.Error()) } else { cb = localCb file = localFile } } } cleaned, err := gf.Clean(from, fileName, fileSize, cb) if file != nil { file.Close() } if cleaned != nil { defer cleaned.Teardown() } if errors.IsCleanPointerError(err) { // If the contents read from the working directory was _already_ // a pointer, we'll get a `CleanPointerError`, with the context // containing the bytes that we should write back out to Git. _, err = to.Write(errors.GetContext(err, "bytes").([]byte)) return nil, err } if err != nil { ExitWithError(errors.Wrap(err, tr.Tr.Get("Error cleaning Git LFS object"))) } tmpfile := cleaned.Filename mediafile, err := gf.ObjectPath(cleaned.Oid) if err != nil { Panic(err, tr.Tr.Get("Unable to get local media path.")) } if stat, _ := os.Stat(mediafile); stat != nil { if stat.Size() != cleaned.Size && len(cleaned.Pointer.Extensions) == 0 { Exit("%s\n%s\n%s", tr.Tr.Get("Files don't match:"), mediafile, tmpfile) } Debug("%s exists", mediafile) } else { if err := os.Rename(tmpfile, mediafile); err != nil { Panic(err, tr.Tr.Get("Unable to move %s to %s", tmpfile, mediafile)) } Debug(tr.Tr.Get("Writing %s", mediafile)) } _, err = lfs.EncodePointer(to, cleaned.Pointer) return cleaned.Pointer, err } func cleanCommand(cmd *cobra.Command, args []string) { requireStdin(tr.Tr.Get("This command should be run by the Git 'clean' filter")) setupRepository() installHooks(false) var fileName string if len(args) > 0 { fileName = args[0] } gitfilter := lfs.NewGitFilter(cfg) ptr, err := clean(gitfilter, os.Stdout, os.Stdin, fileName, -1) if err != nil { Error(err.Error()) } if ptr != nil && possiblyMalformedObjectSize(ptr.Size) { Error(tr.Tr.Get("Possibly malformed conversion on Windows, see `git lfs help smudge` for more details.")) } } func init() { RegisterCommand("clean", cleanCommand, nil) }