c5bfafb83d
We have used the 'git-lfs-pointer(1)' command historically to compare varying implementation of the Git LFS protocol. This has been done by passing both `--file` and `--stdin` to the command, which will in turn compare the contents of both handles and return appropriately whether they are the same or different. One use case that Git LFS pointer does not yet support directly is _checking_ to see whether a given file is a valid Git LFS pointer. This is a different use-case than before, since we aren't doing any comparison, we're simply checking whether the official implementation of Git LFS can parse a given stream as a pointer. As an aside, one way to do this today is the following: $ git lfs clean < a.txt | git lfs pointer --stdin --file my.ptr Where we first generate a pointer of the file 'a.txt' (via `git-lfs-clean(1)`), and then redirect that to `git-lfs-pointer(1)` against our own file. Let's make this above incantation easier to execute by providing a functionally equivalent `--check` option. Running `git lfs pointer --check` (and passing either `--file`, or `--stdin`) will return either 1 or 0 depending on whether or not the given pointer file was invalid or not.
167 lines
3.6 KiB
Go
167 lines
3.6 KiB
Go
package commands
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
"github.com/git-lfs/git-lfs/git"
|
|
"github.com/git-lfs/git-lfs/lfs"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
pointerFile string
|
|
pointerCompare string
|
|
pointerStdin bool
|
|
pointerCheck bool
|
|
)
|
|
|
|
func pointerCommand(cmd *cobra.Command, args []string) {
|
|
comparing := false
|
|
something := false
|
|
buildOid := ""
|
|
compareOid := ""
|
|
|
|
if pointerCheck {
|
|
var r io.ReadCloser
|
|
var err error
|
|
|
|
if len(pointerCompare) > 0 {
|
|
ExitWithError(fmt.Errorf("fatal: cannot combine --check with --compare"))
|
|
}
|
|
|
|
if len(pointerFile) > 0 {
|
|
if pointerStdin {
|
|
ExitWithError(fmt.Errorf("fatal: 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(fmt.Errorf("fatal: must specify either --file or --stdin with --compare"))
|
|
}
|
|
|
|
_, err = lfs.DecodePointer(r)
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
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.Fprintf(os.Stderr, "Git LFS pointer for %s\n\n", pointerFile)
|
|
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.Fprintf(os.Stderr, "\nGit blob OID: %s\n\n", buildOid)
|
|
}
|
|
} 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.Fprintf(os.Stderr, "Pointer from %s\n\n", pointerName)
|
|
|
|
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.Fprintf(os.Stderr, "\nGit blob OID: %s\n", compareOid)
|
|
}
|
|
}
|
|
|
|
if comparing && buildOid != compareOid {
|
|
fmt.Fprintf(os.Stderr, "\nPointers do not match\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if !something {
|
|
Error("Nothing to do!")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func pointerReader() (io.ReadCloser, error) {
|
|
if len(pointerCompare) > 0 {
|
|
if pointerStdin {
|
|
return nil, errors.New("Cannot read from STDIN and --pointer.")
|
|
}
|
|
|
|
return os.Open(pointerCompare)
|
|
}
|
|
|
|
requireStdin("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.")
|
|
})
|
|
}
|