git-lfs/commands/command_pointer.go
Chris Darroch 2830d39ef4 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-29 22:32:58 -08:00

179 lines
4.3 KiB
Go

package commands
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/git-lfs/git-lfs/v3/git"
"github.com/git-lfs/git-lfs/v3/lfs"
"github.com/git-lfs/git-lfs/v3/tr"
"github.com/spf13/cobra"
)
var (
pointerFile string
pointerCompare string
pointerStdin bool
pointerCheck bool
pointerStrict bool
pointerNoStrict bool
)
func pointerCommand(cmd *cobra.Command, args []string) {
comparing := false
something := false
buildOid := ""
compareOid := ""
if pointerCheck {
var r io.ReadCloser
var err error
if pointerStrict && pointerNoStrict {
ExitWithError(errors.New(tr.Tr.Get("Cannot combine --strict with --no-strict")))
}
if len(pointerCompare) > 0 {
ExitWithError(errors.New(tr.Tr.Get("Cannot combine --check with --compare")))
}
if len(pointerFile) > 0 {
if pointerStdin {
ExitWithError(errors.New(tr.Tr.Get("With --check, --file cannot be combined with --stdin")))
}
r, err = os.Open(pointerFile)
if err != nil {
ExitWithError(err)
}
} else if pointerStdin {
r = ioutil.NopCloser(os.Stdin)
} else {
ExitWithError(errors.New(tr.Tr.Get("Must specify either --file or --stdin with --compare")))
}
p, err := lfs.DecodePointer(r)
if err != nil {
os.Exit(1)
}
if pointerStrict && !p.Canonical {
os.Exit(2)
}
r.Close()
return
}
if len(pointerCompare) > 0 || pointerStdin {
comparing = true
}
if len(pointerFile) > 0 {
something = true
buildFile, err := os.Open(pointerFile)
if err != nil {
Error(err.Error())
os.Exit(1)
}
oidHash := sha256.New()
size, err := io.Copy(oidHash, buildFile)
buildFile.Close()
if err != nil {
Error(err.Error())
os.Exit(1)
}
ptr := lfs.NewPointer(hex.EncodeToString(oidHash.Sum(nil)), size, nil)
fmt.Fprint(os.Stderr, tr.Tr.Get("Git LFS pointer for %s", pointerFile), "\n\n")
buf := &bytes.Buffer{}
lfs.EncodePointer(io.MultiWriter(os.Stdout, buf), ptr)
if comparing {
buildOid, err = git.HashObject(bytes.NewReader(buf.Bytes()))
if err != nil {
Error(err.Error())
os.Exit(1)
}
fmt.Fprint(os.Stderr, "\n", tr.Tr.Get("Git blob OID: %s", buildOid), "\n\n")
}
} else {
comparing = false
}
if len(pointerCompare) > 0 || pointerStdin {
something = true
compFile, err := pointerReader()
if err != nil {
Error(err.Error())
os.Exit(1)
}
buf := &bytes.Buffer{}
tee := io.TeeReader(compFile, buf)
_, err = lfs.DecodePointer(tee)
compFile.Close()
pointerName := "STDIN"
if !pointerStdin {
pointerName = pointerCompare
}
fmt.Fprint(os.Stderr, tr.Tr.Get("Pointer from %s", pointerName), "\n\n")
if err != nil {
Error(err.Error())
os.Exit(1)
}
fmt.Fprintf(os.Stderr, buf.String())
if comparing {
compareOid, err = git.HashObject(bytes.NewReader(buf.Bytes()))
if err != nil {
Error(err.Error())
os.Exit(1)
}
fmt.Fprint(os.Stderr, "\n", tr.Tr.Get("Git blob OID: %s", compareOid), "\n")
}
}
if comparing && buildOid != compareOid {
fmt.Fprint(os.Stderr, "\n", tr.Tr.Get("Pointers do not match"), "\n")
os.Exit(1)
}
if !something {
Error(tr.Tr.Get("Nothing to do!"))
os.Exit(1)
}
}
func pointerReader() (io.ReadCloser, error) {
if len(pointerCompare) > 0 {
if pointerStdin {
return nil, errors.New(tr.Tr.Get("cannot read from STDIN and --pointer"))
}
return os.Open(pointerCompare)
}
requireStdin(tr.Tr.Get("The --stdin flag expects a pointer file from STDIN."))
return os.Stdin, nil
}
func init() {
RegisterCommand("pointer", pointerCommand, func(cmd *cobra.Command) {
cmd.Flags().StringVarP(&pointerFile, "file", "f", "", "Path to a local file to generate the pointer from.")
cmd.Flags().StringVarP(&pointerCompare, "pointer", "p", "", "Path to a local file containing a pointer built by another Git LFS implementation.")
cmd.Flags().BoolVarP(&pointerStdin, "stdin", "", false, "Read a pointer built by another Git LFS implementation through STDIN.")
cmd.Flags().BoolVarP(&pointerCheck, "check", "", false, "Check whether the given file is a Git LFS pointer.")
cmd.Flags().BoolVarP(&pointerStrict, "strict", "", false, "Check whether the given Git LFS pointer is canonical.")
cmd.Flags().BoolVarP(&pointerNoStrict, "no-strict", "", false, "Don't check whether the given Git LFS pointer is canonical.")
})
}