package commands
import (
"fmt"
"io"
"os"
"github.com/git-lfs/git-lfs/v3/errors"
"github.com/git-lfs/git-lfs/v3/filepathfilter"
"github.com/git-lfs/git-lfs/v3/git"
"github.com/git-lfs/git-lfs/v3/lfs"
"github.com/git-lfs/git-lfs/v3/tools"
"github.com/git-lfs/git-lfs/v3/tools/humanize"
"github.com/git-lfs/git-lfs/v3/tq"
"github.com/git-lfs/git-lfs/v3/tr"
"github.com/spf13/cobra"
)
var (
smudgeSkip = false
)
func delayedSmudge(gf *lfs.GitFilter, s *git.FilterProcessScanner, to io.Writer, from io.Reader, q *tq.TransferQueue, filename string, skip bool, filter *filepathfilter.Filter) (int64, bool, *lfs.Pointer, error) {
ptr, pbuf, perr := lfs.DecodeFrom(from)
if perr != nil {
if err := s.WriteStatus(statusFromErr(nil)); err != nil {
return 0, false, nil, err
}
n, err := tools.Spool(to, pbuf, cfg.TempDir())
if err != nil {
return n, false, nil, errors.Wrap(err, perr.Error())
}
if n != 0 {
return 0, false, nil, errors.NewNotAPointerError(errors.Errorf(
tr.Tr.Get("Unable to parse pointer at: %q", filename),
))
}
return 0, false, nil, nil
}
lfs.LinkOrCopyFromReference(cfg, ptr.Oid, ptr.Size)
path, err := cfg.Filesystem().ObjectPath(ptr.Oid)
if err != nil {
return 0, false, nil, err
}
if !skip && filter.Allows(filename) {
if _, statErr := os.Stat(path); statErr != nil && ptr.Size != 0 {
q.Add(filename, path, ptr.Oid, ptr.Size, false, err)
return 0, true, ptr, nil
}
if err := s.WriteStatus(statusFromErr(nil)); err != nil {
return 0, false, nil, err
}
n, err := gf.Smudge(to, ptr, filename, false, nil, nil)
return n, false, ptr, err
}
if err := s.WriteStatus(statusFromErr(nil)); err != nil {
return 0, false, nil, err
}
n, err := ptr.Encode(to)
return int64(n), false, ptr, err
}
func smudge(gf *lfs.GitFilter, to io.Writer, from io.Reader, filename string, skip bool, filter *filepathfilter.Filter) (int64, error) {
ptr, pbuf, perr := lfs.DecodeFrom(from)
if perr != nil {
n, err := tools.Spool(to, pbuf, cfg.TempDir())
if err != nil {
return 0, errors.Wrap(err, perr.Error())
}
if n != 0 {
return 0, errors.NewNotAPointerError(errors.Errorf(
tr.Tr.Get("Unable to parse pointer at: %q", filename),
))
}
return 0, nil
}
lfs.LinkOrCopyFromReference(cfg, ptr.Oid, ptr.Size)
cb, file, err := gf.CopyCallbackFile("download", filename, 1, 1)
if err != nil {
return 0, err
}
if skip || !filter.Allows(filename) {
n, err := ptr.Encode(to)
return int64(n), err
}
n, err := gf.Smudge(to, ptr, filename, true, getTransferManifestOperationRemote("download", cfg.Remote()), cb)
if file != nil {
file.Close()
}
if err != nil {
ptr.Encode(to)
var oid string = ptr.Oid
if len(oid) >= 7 {
oid = oid[:7]
}
LoggedError(err, tr.Tr.Get("Error downloading object: %s (%s): %s", filename, oid, err))
if !cfg.SkipDownloadErrors() {
os.Exit(2)
}
}
return n, nil
}
func smudgeCommand(cmd *cobra.Command, args []string) {
requireStdin(tr.Tr.Get("This command should be run by the Git 'smudge' filter"))
setupRepository()
installHooks(false)
if !smudgeSkip && cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) {
smudgeSkip = true
}
filter := filepathfilter.New(cfg.FetchIncludePaths(), cfg.FetchExcludePaths(), filepathfilter.GitIgnore)
gitfilter := lfs.NewGitFilter(cfg)
if n, err := smudge(gitfilter, os.Stdout, os.Stdin, smudgeFilename(args), smudgeSkip, filter); err != nil {
if errors.IsNotAPointerError(err) {
fmt.Fprintln(os.Stderr, err.Error())
} else {
Error(err.Error())
}
} else if possiblyMalformedObjectSize(n) {
fmt.Fprintln(os.Stderr, tr.Tr.Get("Possibly malformed smudge on Windows: see `git lfs help smudge` for more info."))
}
}
func smudgeFilename(args []string) string {
if len(args) > 0 {
return args[0]
}
return fmt.Sprintf("<%s>", tr.Tr.Get("unknown file"))
}
func possiblyMalformedObjectSize(n int64) bool {
return n >= 4*humanize.Gibibyte
}
func init() {
RegisterCommand("smudge", smudgeCommand, func(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&smudgeSkip, "skip", "s", false, "")
})
}