Merge pull request #271 from github/dont-clean-pointers
pass pointers straight through 'git lfs clean'
This commit is contained in:
commit
3321cccd1c
@ -2,48 +2,280 @@ package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/bmizerany/assert"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClean(t *testing.T) {
|
||||
func TestCleanSmallFile(t *testing.T) {
|
||||
repo := NewRepository(t, "empty")
|
||||
defer repo.Test()
|
||||
|
||||
content := "HI\n"
|
||||
oid := "f712374589a4f37f0fd6b941a104c7ccf43f68b1fdecb4d5cd88b80acbf98fc2"
|
||||
cmd := repo.Command("clean")
|
||||
cmd.Input = bytes.NewBufferString("whatever\n")
|
||||
cmd.Output = "version https://git-lfs.github.com/spec/v1\n" +
|
||||
"oid sha256:cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411\n" +
|
||||
"size 9"
|
||||
|
||||
path := filepath.Join(repo.Path, ".git", "lfs", "objects")
|
||||
prePushHookFile := filepath.Join(repo.Path, ".git", "hooks", "pre-push")
|
||||
|
||||
cmd := repo.Command("clean", "somefile")
|
||||
cmd.Input = bytes.NewBufferString(content)
|
||||
cmd.Output = `version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:` + oid + `
|
||||
size 3`
|
||||
|
||||
cmd.After(func() {
|
||||
// assert hooks
|
||||
stat, err := os.Stat(prePushHookFile)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, false, stat.IsDir())
|
||||
})
|
||||
|
||||
cmd = repo.Command("clean")
|
||||
cmd.Input = bytes.NewBufferString(content)
|
||||
cmd.Output = `version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:` + oid + `
|
||||
size 3`
|
||||
customHook := []byte("echo 'yo'")
|
||||
cmd.Before(func() {
|
||||
err := ioutil.WriteFile(prePushHookFile, customHook, 0755)
|
||||
assert.Equal(t, nil, err)
|
||||
_, err := os.Open(path)
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
t.Fatalf("'%s' should not exist", path)
|
||||
}
|
||||
})
|
||||
|
||||
file := filepath.Join(path, "cd", "29", "cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411")
|
||||
cmd.After(func() {
|
||||
by, err := ioutil.ReadFile(prePushHookFile)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, string(customHook), string(by))
|
||||
by, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
contents := string(by)
|
||||
if contents != "whatever\n" {
|
||||
t.Fatalf("wrong contents: '%v'", contents)
|
||||
}
|
||||
|
||||
by, err = ioutil.ReadFile(prePushHookFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pattern := "git lfs push"
|
||||
if !bytes.Contains(by, []byte(pattern)) {
|
||||
t.Errorf("hook does not contain %s:\n%s", pattern, string(by))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCleanBigFile(t *testing.T) {
|
||||
repo := NewRepository(t, "empty")
|
||||
defer repo.Test()
|
||||
|
||||
cmd := repo.Command("clean")
|
||||
cmd.Input = randomReader(1024)
|
||||
cmd.Output = "version https://git-lfs.github.com/spec/v1\n" +
|
||||
"oid sha256:7cd8be1d2cd0dd22cd9d229bb6b5785009a05e8b39d405615d882caac56562b5\n" +
|
||||
"size 1024"
|
||||
|
||||
path := filepath.Join(repo.Path, ".git", "lfs", "objects")
|
||||
|
||||
cmd.Before(func() {
|
||||
_, err := os.Open(path)
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
t.Fatalf("'%s' should not exist", path)
|
||||
}
|
||||
})
|
||||
|
||||
file := filepath.Join(path, "7c", "d8", "7cd8be1d2cd0dd22cd9d229bb6b5785009a05e8b39d405615d882caac56562b5")
|
||||
cmd.After(func() {
|
||||
stat, err := os.Stat(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if fileSize := stat.Size(); fileSize != 1024 {
|
||||
t.Fatalf("bad size: %d", fileSize)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCleanPointerWithExtra(t *testing.T) {
|
||||
repo := NewRepository(t, "empty")
|
||||
defer repo.Test()
|
||||
|
||||
cmd := repo.Command("clean")
|
||||
cmd.Input = bytes.NewBufferString("version https://git-lfs.github.com/spec/v1\n" +
|
||||
"oid sha256:7cd8be1d2cd0dd22cd9d229bb6b5785009a05e8b39d405615d882caac56562b5\n" +
|
||||
"size 1024\n\n" +
|
||||
"This is my test pointer. There are many like it, but this one is mine.")
|
||||
cmd.Output = "version https://git-lfs.github.com/spec/v1\n" +
|
||||
"oid sha256:f39333aa7ce293ce27742843a1a1cd6e958eaf6b44cfca9039e6b461088df5ba\n" +
|
||||
"size 201"
|
||||
|
||||
path := filepath.Join(repo.Path, ".git", "lfs", "objects")
|
||||
|
||||
cmd.Before(func() {
|
||||
_, err := os.Open(path)
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
t.Fatalf("'%s' should not exist", path)
|
||||
}
|
||||
})
|
||||
|
||||
file := filepath.Join(path, "f3", "93", "f39333aa7ce293ce27742843a1a1cd6e958eaf6b44cfca9039e6b461088df5ba")
|
||||
cmd.After(func() {
|
||||
stat, err := os.Stat(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if fileSize := stat.Size(); fileSize != 201 {
|
||||
t.Fatalf("bad size: %d", fileSize)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCleanPointerWithWhitespaceAndExtra(t *testing.T) {
|
||||
repo := NewRepository(t, "empty")
|
||||
defer repo.Test()
|
||||
|
||||
head := bytes.NewBufferString("version https://git-lfs.github.com/spec/v1\n" +
|
||||
"oid sha256:7cd8be1d2cd0dd22cd9d229bb6b5785009a05e8b39d405615d882caac56562b5\n" +
|
||||
"size 1024\n" +
|
||||
// extra white space to fill the initial buffer in DecodeFrom()
|
||||
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
|
||||
|
||||
cmd := repo.Command("clean")
|
||||
cmd.Input = io.MultiReader(head, randomReader(1024))
|
||||
cmd.Output = "version https://git-lfs.github.com/spec/v1\n" +
|
||||
"oid sha256:494f6e07ed15b2e31ca60b31ed8c503bd7553dfd4a76ec73692f766bc67f0bec\n" +
|
||||
"size 1537"
|
||||
|
||||
path := filepath.Join(repo.Path, ".git", "lfs", "objects")
|
||||
|
||||
cmd.Before(func() {
|
||||
_, err := os.Open(path)
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
t.Fatalf("'%s' should not exist", path)
|
||||
}
|
||||
})
|
||||
|
||||
file := filepath.Join(path, "49", "4f", "494f6e07ed15b2e31ca60b31ed8c503bd7553dfd4a76ec73692f766bc67f0bec")
|
||||
cmd.After(func() {
|
||||
stat, err := os.Stat(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if fileSize := stat.Size(); fileSize != 1537 {
|
||||
t.Fatalf("bad size: %d", fileSize)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCleanPointer(t *testing.T) {
|
||||
repo := NewRepository(t, "empty")
|
||||
defer repo.Test()
|
||||
|
||||
versions := []string{
|
||||
"http://git-media.io/v/2",
|
||||
"https://hawser.github.com/spec/v1",
|
||||
"https://git-lfs.github.com/spec/v1",
|
||||
}
|
||||
|
||||
for _, v := range versions {
|
||||
cmd := repo.Command("clean")
|
||||
cmd.Input = bytes.NewBufferString("version " + v + "\n" +
|
||||
"oid sha256:cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411\n" +
|
||||
"size 9\n")
|
||||
cmd.Output = "version " + v + "\n" +
|
||||
"oid sha256:cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411\n" +
|
||||
"size 9"
|
||||
|
||||
path := filepath.Join(repo.Path, ".git", "lfs", "objects")
|
||||
|
||||
cmd.Before(func() {
|
||||
_, err := os.Open(path)
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
t.Errorf("'%s' should not exist before version %s", path, v)
|
||||
}
|
||||
})
|
||||
|
||||
cmd.After(func() {
|
||||
dirs, err := ioutil.ReadDir(path)
|
||||
if _, ok := err.(*os.PathError); ok {
|
||||
return // it's ok if this dir does not exist
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error for version %s: %s", v, err)
|
||||
return
|
||||
}
|
||||
|
||||
if dirs == nil || len(dirs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if len(dirs) == 1 && dirs[0].Name() == "logs" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
t.Logf("DIR: %v", dir.Name())
|
||||
}
|
||||
|
||||
t.Errorf("objects were written to .git/lfs/objects")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanWithCustomHook(t *testing.T) {
|
||||
repo := NewRepository(t, "empty")
|
||||
defer repo.Test()
|
||||
|
||||
cmd := repo.Command("clean")
|
||||
cmd.Input = bytes.NewBufferString("whatever\n")
|
||||
cmd.Output = "version https://git-lfs.github.com/spec/v1\n" +
|
||||
"oid sha256:cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411\n" +
|
||||
"size 9"
|
||||
|
||||
path := filepath.Join(repo.Path, ".git", "lfs", "objects")
|
||||
prePushHookFile := filepath.Join(repo.Path, ".git", "hooks", "pre-push")
|
||||
customHook := []byte("test")
|
||||
|
||||
cmd.Before(func() {
|
||||
err := ioutil.WriteFile(prePushHookFile, customHook, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = os.Open(path)
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
t.Fatalf("'%s' should not exist", path)
|
||||
}
|
||||
})
|
||||
|
||||
file := filepath.Join(path, "cd", "29", "cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411")
|
||||
cmd.After(func() {
|
||||
by, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
contents := string(by)
|
||||
if contents != "whatever\n" {
|
||||
t.Fatalf("wrong contents: '%v'", contents)
|
||||
}
|
||||
|
||||
by, err = ioutil.ReadFile(prePushHookFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(by) != string(customHook) {
|
||||
t.Logf(string(by))
|
||||
t.Errorf("Hook does not contain custom hook")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func randomReader(n int64) io.Reader {
|
||||
return io.LimitReader(&randomDataMaker{rand.NewSource(42)}, n)
|
||||
}
|
||||
|
||||
type randomDataMaker struct {
|
||||
src rand.Source
|
||||
}
|
||||
|
||||
func (r *randomDataMaker) Read(p []byte) (n int, err error) {
|
||||
for i := range p {
|
||||
p[i] = byte(r.src.Int63() & 0xff)
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
@ -50,6 +50,11 @@ func cleanCommand(cmd *cobra.Command, args []string) {
|
||||
defer cleaned.Teardown()
|
||||
}
|
||||
|
||||
if cpErr, ok := err.(*pointer.CleanedPointerError); ok {
|
||||
os.Stdout.Write(cpErr.Bytes)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
Panic(err, "Error cleaning asset.")
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package pointer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"github.com/github/git-lfs/lfs"
|
||||
@ -14,6 +15,14 @@ type cleanedAsset struct {
|
||||
*Pointer
|
||||
}
|
||||
|
||||
type CleanedPointerError struct {
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
func (e *CleanedPointerError) Error() string {
|
||||
return "Cannot clean a Git LFS pointer. Skipping."
|
||||
}
|
||||
|
||||
func Clean(reader io.Reader, size int64, cb lfs.CopyCallback) (*cleanedAsset, error) {
|
||||
tmp, err := lfs.TempFile("")
|
||||
if err != nil {
|
||||
@ -27,7 +36,13 @@ func Clean(reader io.Reader, size int64, cb lfs.CopyCallback) (*cleanedAsset, er
|
||||
cb = nil
|
||||
}
|
||||
|
||||
written, err := lfs.CopyWithCallback(writer, reader, size, cb)
|
||||
by, _, err := DecodeFrom(reader)
|
||||
if err == nil && len(by) < 512 {
|
||||
return nil, &CleanedPointerError{by}
|
||||
}
|
||||
|
||||
multi := io.MultiReader(bytes.NewReader(by), reader)
|
||||
written, err := lfs.CopyWithCallback(writer, multi, size, cb)
|
||||
|
||||
pointer := NewPointer(hex.EncodeToString(oidHash.Sum(nil)), written)
|
||||
return &cleanedAsset{tmp, "", pointer}, err
|
||||
|
@ -58,13 +58,20 @@ func Encode(writer io.Writer, pointer *Pointer) (int, error) {
|
||||
}
|
||||
|
||||
func Decode(reader io.Reader) (*Pointer, error) {
|
||||
buf := make([]byte, 200)
|
||||
_, p, err := DecodeFrom(reader)
|
||||
return p, err
|
||||
}
|
||||
|
||||
func DecodeFrom(reader io.Reader) ([]byte, *Pointer, error) {
|
||||
buf := make([]byte, 512)
|
||||
written, err := reader.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return buf, nil, err
|
||||
}
|
||||
|
||||
return decodeKV(bytes.TrimSpace(buf[0:written]))
|
||||
output := buf[0:written]
|
||||
p, err := decodeKV(bytes.TrimSpace(output))
|
||||
return output, p, err
|
||||
}
|
||||
|
||||
func verifyVersion(version string) error {
|
||||
|
Loading…
Reference in New Issue
Block a user