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"
|
2019-01-29 22:39:09 +00:00
|
|
|
"io/ioutil"
|
2015-04-22 17:41:41 +00:00
|
|
|
"os"
|
2015-05-13 19:43:41 +00:00
|
|
|
|
2021-09-01 19:41:10 +00:00
|
|
|
"github.com/git-lfs/git-lfs/v3/git"
|
|
|
|
"github.com/git-lfs/git-lfs/v3/lfs"
|
2021-12-13 19:24:21 +00:00
|
|
|
"github.com/git-lfs/git-lfs/v3/tr"
|
2016-05-23 18:02:27 +00:00
|
|
|
"github.com/spf13/cobra"
|
2015-04-22 17:41:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2021-06-02 21:10:40 +00:00
|
|
|
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 := ""
|
|
|
|
|
2019-01-29 22:39:09 +00:00
|
|
|
if pointerCheck {
|
|
|
|
var r io.ReadCloser
|
|
|
|
var err error
|
|
|
|
|
2021-06-02 21:10:40 +00:00
|
|
|
if pointerStrict && pointerNoStrict {
|
2021-12-13 19:24:21 +00:00
|
|
|
ExitWithError(errors.New(tr.Tr.Get("Cannot combine --strict with --no-strict")))
|
2021-06-02 21:10:40 +00:00
|
|
|
}
|
|
|
|
|
2019-01-29 22:39:09 +00:00
|
|
|
if len(pointerCompare) > 0 {
|
2021-12-13 19:24:21 +00:00
|
|
|
ExitWithError(errors.New(tr.Tr.Get("Cannot combine --check with --compare")))
|
2019-01-29 22:39:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(pointerFile) > 0 {
|
|
|
|
if pointerStdin {
|
2021-12-13 19:24:21 +00:00
|
|
|
ExitWithError(errors.New(tr.Tr.Get("With --check, --file cannot be combined with --stdin")))
|
2019-01-29 22:39:09 +00:00
|
|
|
}
|
|
|
|
r, err = os.Open(pointerFile)
|
|
|
|
if err != nil {
|
|
|
|
ExitWithError(err)
|
|
|
|
}
|
|
|
|
} else if pointerStdin {
|
|
|
|
r = ioutil.NopCloser(os.Stdin)
|
|
|
|
} else {
|
2021-12-13 19:24:21 +00:00
|
|
|
ExitWithError(errors.New(tr.Tr.Get("Must specify either --file or --stdin with --compare")))
|
2019-01-29 22:39:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-02 21:10:40 +00:00
|
|
|
p, err := lfs.DecodePointer(r)
|
2019-01-29 22:39:09 +00:00
|
|
|
if err != nil {
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2021-06-02 21:10:40 +00:00
|
|
|
if pointerStrict && !p.Canonical {
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
2019-01-29 22:39:09 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2015-07-14 14:49:13 +00:00
|
|
|
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 {
|
2017-08-22 17:33:19 +00:00
|
|
|
buildOid, err = git.HashObject(bytes.NewReader(buf.Bytes()))
|
2017-08-02 17:21:43 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2016-03-22 09:50:40 +00:00
|
|
|
fmt.Fprintf(os.Stderr, buf.String())
|
2015-04-22 17:41:41 +00:00
|
|
|
if comparing {
|
2017-08-22 17:33:19 +00:00
|
|
|
compareOid, err = git.HashObject(bytes.NewReader(buf.Bytes()))
|
2017-08-02 17:21:43 +00:00
|
|
|
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 {
|
2021-12-13 19:24:21 +00:00
|
|
|
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 {
|
2021-12-13 19:24:21 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2021-12-13 19:24:21 +00:00
|
|
|
requireStdin(tr.Tr.Get("The --stdin flag expects a pointer file from STDIN."))
|
2015-04-24 20:24:32 +00:00
|
|
|
|
2015-04-22 19:36:16 +00:00
|
|
|
return os.Stdin, nil
|
|
|
|
}
|
|
|
|
|
2015-04-22 17:41:41 +00:00
|
|
|
func init() {
|
2016-09-01 16:09:38 +00:00
|
|
|
RegisterCommand("pointer", pointerCommand, func(cmd *cobra.Command) {
|
2016-08-10 15:33:25 +00:00
|
|
|
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.")
|
2019-01-29 22:39:09 +00:00
|
|
|
cmd.Flags().BoolVarP(&pointerCheck, "check", "", false, "Check whether the given file is a Git LFS pointer.")
|
2021-06-02 21:10:40 +00:00
|
|
|
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.")
|
2016-08-10 15:33:25 +00:00
|
|
|
})
|
2015-04-22 17:41:41 +00:00
|
|
|
}
|