git-lfs/lfs/gitfilter_smudge.go

209 lines
6.1 KiB
Go
Raw Normal View History

package lfs
2013-10-04 15:22:32 +00:00
import (
2015-03-22 16:01:26 +00:00
"fmt"
2013-10-04 15:22:32 +00:00
"io"
"os"
2015-03-20 17:30:24 +00:00
"path/filepath"
2015-05-13 19:43:41 +00:00
"github.com/git-lfs/git-lfs/v3/config"
"github.com/git-lfs/git-lfs/v3/errors"
"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"
2021-12-14 16:05:11 +00:00
"github.com/git-lfs/git-lfs/v3/tr"
"github.com/rubyist/tracerx"
2013-10-04 15:22:32 +00:00
)
func (f *GitFilter) SmudgeToFile(filename string, ptr *Pointer, download bool, manifest *tq.Manifest, cb tools.CopyCallback) error {
tools.MkdirAll(filepath.Dir(filename), f.cfg)
if stat, _ := os.Stat(filename); stat != nil && stat.Mode()&0200 == 0 {
if err := os.Chmod(filename, stat.Mode()|0200); err != nil {
return errors.Wrap(err,
2021-12-14 16:05:11 +00:00
tr.Tr.Get("Could not restore write permission"))
}
// When we're done, return the file back to its normal
// permission bits.
defer os.Chmod(filename, stat.Mode())
}
abs, err := filepath.Abs(filename)
if err != nil {
2021-12-14 16:05:11 +00:00
return errors.New(tr.Tr.Get("could not produce absolute path for %q", filename))
}
file, err := os.Create(abs)
if err != nil {
2021-12-14 16:05:11 +00:00
return errors.New(tr.Tr.Get("could not create working directory file: %v", err))
}
defer file.Close()
2017-10-24 17:42:00 +00:00
if _, err := f.Smudge(file, ptr, filename, download, manifest, cb); err != nil {
2016-08-18 20:20:33 +00:00
if errors.IsDownloadDeclinedError(err) {
// write placeholder data instead
file.Seek(0, io.SeekStart)
ptr.Encode(file)
return err
} else {
2021-12-14 16:05:11 +00:00
return errors.New(tr.Tr.Get("could not write working directory file: %v", err))
}
}
return nil
}
func (f *GitFilter) Smudge(writer io.Writer, ptr *Pointer, workingfile string, download bool, manifest *tq.Manifest, cb tools.CopyCallback) (int64, error) {
2017-10-25 17:31:15 +00:00
mediafile, err := f.ObjectPath(ptr.Oid)
2014-06-05 19:05:02 +00:00
if err != nil {
return 0, err
2014-06-05 19:05:02 +00:00
}
2013-10-04 15:22:32 +00:00
LinkOrCopyFromReference(f.cfg, ptr.Oid, ptr.Size)
2016-01-31 21:06:40 +00:00
stat, statErr := os.Stat(mediafile)
2015-04-28 15:49:46 +00:00
if statErr == nil && stat != nil {
fileSize := stat.Size()
if fileSize != ptr.Size {
2015-04-28 15:49:46 +00:00
tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
os.RemoveAll(mediafile)
stat = nil
}
}
var n int64
if ptr.Size == 0 {
return 0, nil
} else if statErr != nil || stat == nil {
if download {
2017-10-24 17:42:00 +00:00
n, err = f.downloadFile(writer, ptr, workingfile, mediafile, manifest, cb)
} else {
return 0, errors.NewDownloadDeclinedError(statErr, tr.Tr.Get("smudge filter"))
}
} else {
2017-10-24 17:42:00 +00:00
n, err = f.readLocalFile(writer, ptr, mediafile, workingfile, cb)
}
2015-08-21 18:31:06 +00:00
if err != nil {
return 0, errors.NewSmudgeError(err, ptr.Oid, mediafile)
}
2015-05-12 08:45:06 +00:00
return n, nil
}
func (f *GitFilter) downloadFile(writer io.Writer, ptr *Pointer, workingfile, mediafile string, manifest *tq.Manifest, cb tools.CopyCallback) (int64, error) {
avoid extra message format parsing where possible Because the tr.Tr.Get() family of methods insert arguments into printf(3)-style format strings after translating the format string, we can in a few cases drop a surrounding call to fmt.Sprintf() or a similar method, as those now take no arguments and so are redundant. Moreover, this will help us avoid situations where either the translated string or the argument values interpolated by tr.Tr.Get() produce an output string which itself happens to contain character sequences that resemble Go format specifiers (e.g., "%s", "%d", etc.) In such cases passing the string at runtime to a method such as fmt.Fprintf() will result in the output containing a warning such as "%!s(MISSING)", which is not ideal. Note that in one case, in lfs/attribute.go, we can now also simplify the format string to use standard format specifiers instead of double-escaped ones (e.g., "%%q") since we can just allow tr.Tr.Get() to do the interpolation. We also take the opportunity to remove explicit leading or trailing newlines from translation messages wherever it is possible to convert the surrounding call to fmt.Print(), fmt.Fprint(), fmt.Println(), or fmt.Fprintln(). Finally, in the commands/run.go file, we can replace two calls to fmt.Fprintf() with fmt.Println() because they are just printing output to os.Stdout, not os.Stderr, and in the lfs/extension.go file, we can make better use of fmt.Errorf(). Note that at least one of these messages is not yet actually passed as a translation string, but we will address that issue in a subsequent commit.
2022-01-27 02:00:03 +00:00
fmt.Fprintln(os.Stderr, tr.Tr.Get("Downloading %s (%s)", workingfile, humanize.FormatBytes(uint64(ptr.Size))))
2014-08-08 17:31:33 +00:00
// NOTE: if given, "cb" is a tools.CopyCallback which writes updates
// to the logpath specified by GIT_LFS_PROGRESS.
//
// Either way, forward it into the *tq.TransferQueue so that updates are
// sent over correctly.
q := tq.NewTransferQueue(tq.Download, manifest, f.cfg.Remote(),
tq.WithProgressCallback(cb),
tq.RemoteRef(f.RemoteRef()),
)
q.Add(filepath.Base(workingfile), mediafile, ptr.Oid, ptr.Size, false, nil)
q.Wait()
if errs := q.Errors(); len(errs) > 0 {
var multiErr error
for _, e := range errs {
if multiErr != nil {
multiErr = fmt.Errorf("%v\n%v", multiErr, e)
} else {
multiErr = e
}
}
2021-12-14 16:05:11 +00:00
return 0, errors.Wrapf(multiErr, tr.Tr.Get("Error downloading %s (%s)", workingfile, ptr.Oid))
}
2017-10-24 17:42:00 +00:00
return f.readLocalFile(writer, ptr, mediafile, workingfile, nil)
}
func (f *GitFilter) readLocalFile(writer io.Writer, ptr *Pointer, mediafile string, workingfile string, cb tools.CopyCallback) (int64, error) {
reader, err := tools.RobustOpen(mediafile)
if err != nil {
2021-12-14 16:05:11 +00:00
return 0, errors.Wrapf(err, tr.Tr.Get("error opening media file"))
}
defer reader.Close()
if ptr.Size == 0 {
if stat, _ := os.Stat(mediafile); stat != nil {
ptr.Size = stat.Size()
}
}
2015-07-24 04:53:36 +00:00
if len(ptr.Extensions) > 0 {
2017-10-24 17:42:00 +00:00
registeredExts := f.cfg.Extensions()
extensions := make(map[string]config.Extension)
2015-07-24 04:53:36 +00:00
for _, ptrExt := range ptr.Extensions {
ext, ok := registeredExts[ptrExt.Name]
if !ok {
2021-12-14 16:05:11 +00:00
err := errors.New(tr.Tr.Get("extension '%s' is not configured", ptrExt.Name))
return 0, errors.Wrap(err, tr.Tr.Get("smudge filter"))
2015-07-24 04:53:36 +00:00
}
ext.Priority = ptrExt.Priority
extensions[ext.Name] = ext
}
exts, err := config.SortExtensions(extensions)
2015-07-24 04:53:36 +00:00
if err != nil {
return 0, errors.Wrap(err, tr.Tr.Get("smudge filter"))
2015-07-24 04:53:36 +00:00
}
// pipe extensions in reverse order
var extsR []config.Extension
for i := range exts {
2015-07-24 04:53:36 +00:00
ext := exts[len(exts)-1-i]
extsR = append(extsR, ext)
}
request := &pipeRequest{"smudge", reader, workingfile, extsR}
response, err := pipeExtensions(f.cfg, request)
2015-07-24 04:53:36 +00:00
if err != nil {
return 0, errors.Wrap(err, tr.Tr.Get("smudge filter"))
2015-07-24 04:53:36 +00:00
}
actualExts := make(map[string]*pipeExtResult)
for _, result := range response.results {
actualExts[result.name] = result
}
// verify name, order, and oids
oid := response.results[0].oidIn
if ptr.Oid != oid {
err = errors.New(tr.Tr.Get("actual OID %s during smudge does not match expected %s", oid, ptr.Oid))
return 0, errors.Wrap(err, tr.Tr.Get("smudge filter"))
2015-07-24 04:53:36 +00:00
}
for _, expected := range ptr.Extensions {
actual := actualExts[expected.Name]
if actual.name != expected.Name {
2021-12-14 16:05:11 +00:00
err = errors.New(tr.Tr.Get("actual extension name '%s' does not match expected '%s'", actual.name, expected.Name))
return 0, errors.Wrap(err, tr.Tr.Get("smudge filter"))
2015-07-24 04:53:36 +00:00
}
if actual.oidOut != expected.Oid {
err = errors.New(tr.Tr.Get("actual OID %s for extension '%s' does not match expected %s", actual.oidOut, expected.Name, expected.Oid))
return 0, errors.Wrap(err, tr.Tr.Get("smudge filter"))
2015-07-24 04:53:36 +00:00
}
}
// setup reader
reader, err = os.Open(response.file.Name())
if err != nil {
2021-12-14 16:05:11 +00:00
return 0, errors.Wrapf(err, tr.Tr.Get("Error opening smudged file: %s", err))
2015-07-24 04:53:36 +00:00
}
defer reader.Close()
}
n, err := tools.CopyWithCallback(writer, reader, ptr.Size, cb)
2015-07-24 04:53:36 +00:00
if err != nil {
2021-12-14 16:05:11 +00:00
return n, errors.Wrapf(err, tr.Tr.Get("Error reading from media file: %s", err))
2015-07-24 04:53:36 +00:00
}
return n, nil
2013-10-04 15:22:32 +00:00
}