git-lfs/commands/command_pointer.go

179 lines
4.3 KiB
Go
Raw Normal View History

2015-04-22 17:41:41 +00:00
package commands
import (
"bytes"
"crypto/sha256"
"encoding/hex"
2015-04-22 19:36:16 +00:00
"errors"
2015-04-22 17:41:41 +00:00
"fmt"
"io"
"io/ioutil"
2015-04-22 17:41:41 +00:00
"os"
2015-05-13 19:43:41 +00:00
"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"
2015-04-22 17:41:41 +00:00
)
var (
pointerFile string
pointerCompare string
pointerStdin bool
pointerCheck bool
pointerStrict bool
pointerNoStrict bool
2015-04-22 17:41:41 +00:00
)
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
}
2015-04-22 19:36:16 +00:00
if len(pointerCompare) > 0 || pointerStdin {
2015-04-22 17:41:41 +00:00
comparing = true
}
2015-04-22 19:17:53 +00:00
if len(pointerFile) > 0 {
2015-04-22 17:41:41 +00:00
something = true
2015-04-22 19:17:53 +00:00
buildFile, err := os.Open(pointerFile)
2015-04-22 17:41:41 +00:00
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)
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.Fprint(os.Stderr, tr.Tr.Get("Git LFS pointer for %s", pointerFile), "\n\n")
2015-04-22 17:41:41 +00:00
buf := &bytes.Buffer{}
2015-04-24 18:17:11 +00:00
lfs.EncodePointer(io.MultiWriter(os.Stdout, buf), ptr)
2015-04-22 17:41:41 +00:00
if comparing {
buildOid, err = git.HashObject(bytes.NewReader(buf.Bytes()))
if err != nil {
Error(err.Error())
os.Exit(1)
}
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.Fprint(os.Stderr, "\n", tr.Tr.Get("Git blob OID: %s", buildOid), "\n\n")
2015-04-22 17:41:41 +00:00
}
} else {
comparing = false
}
2015-04-22 19:36:16 +00:00
if len(pointerCompare) > 0 || pointerStdin {
2015-04-22 17:41:41 +00:00
something = true
2015-04-22 19:36:16 +00:00
compFile, err := pointerReader()
2015-04-22 17:41:41 +00:00
if err != nil {
Error(err.Error())
os.Exit(1)
}
buf := &bytes.Buffer{}
tee := io.TeeReader(compFile, buf)
2015-04-24 18:17:11 +00:00
_, err = lfs.DecodePointer(tee)
2015-04-22 17:41:41 +00:00
compFile.Close()
2015-04-22 19:36:16 +00:00
pointerName := "STDIN"
if !pointerStdin {
pointerName = pointerCompare
}
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.Fprint(os.Stderr, tr.Tr.Get("Pointer from %s", pointerName), "\n\n")
2015-04-22 17:41:41 +00:00
if err != nil {
Error(err.Error())
os.Exit(1)
}
fmt.Fprintf(os.Stderr, buf.String())
2015-04-22 17:41:41 +00:00
if comparing {
compareOid, err = git.HashObject(bytes.NewReader(buf.Bytes()))
if err != nil {
Error(err.Error())
os.Exit(1)
}
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.Fprint(os.Stderr, "\n", tr.Tr.Get("Git blob OID: %s", compareOid), "\n")
2015-04-22 17:41:41 +00:00
}
}
if comparing && buildOid != compareOid {
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.Fprint(os.Stderr, "\n", tr.Tr.Get("Pointers do not match"), "\n")
2015-04-22 17:41:41 +00:00
os.Exit(1)
}
if !something {
Error(tr.Tr.Get("Nothing to do!"))
2015-04-22 17:41:41 +00:00
os.Exit(1)
}
}
2015-04-22 19:36:16 +00:00
func pointerReader() (io.ReadCloser, error) {
if len(pointerCompare) > 0 {
if pointerStdin {
return nil, errors.New(tr.Tr.Get("cannot read from STDIN and --pointer"))
2015-04-22 19:36:16 +00:00
}
return os.Open(pointerCompare)
}
requireStdin(tr.Tr.Get("The --stdin flag expects a pointer file from STDIN."))
2015-04-22 19:36:16 +00:00
return os.Stdin, nil
}
2015-04-22 17:41:41 +00:00
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.")
})
2015-04-22 17:41:41 +00:00
}