2016-12-14 22:17:24 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2016-12-14 22:29:03 +00:00
|
|
|
"fmt"
|
2016-12-14 22:17:24 +00:00
|
|
|
"io"
|
2016-12-14 22:29:03 +00:00
|
|
|
"os"
|
2016-12-14 22:17:24 +00:00
|
|
|
"sync"
|
|
|
|
|
2016-12-14 22:29:03 +00:00
|
|
|
"github.com/git-lfs/git-lfs/errors"
|
2017-08-02 18:48:48 +00:00
|
|
|
"github.com/git-lfs/git-lfs/git"
|
2016-12-14 22:17:24 +00:00
|
|
|
"github.com/git-lfs/git-lfs/lfs"
|
2017-08-02 18:48:48 +00:00
|
|
|
"github.com/git-lfs/git-lfs/subprocess"
|
2016-12-16 17:35:38 +00:00
|
|
|
"github.com/git-lfs/git-lfs/tq"
|
2016-12-14 22:17:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Handles the process of checking out a single file, and updating the git
|
|
|
|
// index.
|
2017-01-04 21:46:30 +00:00
|
|
|
func newSingleCheckout() *singleCheckout {
|
2016-12-14 22:17:24 +00:00
|
|
|
// Get a converter from repo-relative to cwd-relative
|
|
|
|
// Since writing data & calling git update-index must be relative to cwd
|
|
|
|
pathConverter, err := lfs.NewRepoToCurrentPathConverter()
|
|
|
|
if err != nil {
|
|
|
|
Panic(err, "Could not convert file paths")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &singleCheckout{
|
|
|
|
gitIndexer: &gitIndexer{},
|
|
|
|
pathConverter: pathConverter,
|
2017-01-04 23:10:30 +00:00
|
|
|
manifest: getTransferManifest(),
|
2016-12-14 22:17:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type singleCheckout struct {
|
|
|
|
gitIndexer *gitIndexer
|
|
|
|
pathConverter lfs.PathConverter
|
2016-12-16 17:35:38 +00:00
|
|
|
manifest *tq.Manifest
|
2016-12-14 22:17:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *singleCheckout) Run(p *lfs.WrappedPointer) {
|
2016-12-14 22:29:03 +00:00
|
|
|
// Check the content - either missing or still this pointer (not exist is ok)
|
|
|
|
filepointer, err := lfs.DecodePointerFromFile(p.Name)
|
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
|
if errors.IsNotAPointerError(err) {
|
|
|
|
// File has non-pointer content, leave it alone
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-12-14 22:17:24 +00:00
|
|
|
LoggedError(err, "Checkout error: %s", err)
|
2016-12-14 22:29:03 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if filepointer != nil && filepointer.Oid != p.Oid {
|
|
|
|
// User has probably manually reset a file to another commit
|
|
|
|
// while leaving it a pointer; don't mess with this
|
|
|
|
return
|
2016-12-14 22:17:24 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 22:29:03 +00:00
|
|
|
cwdfilepath := c.pathConverter.Convert(p.Name)
|
|
|
|
|
|
|
|
err = lfs.PointerSmudgeToFile(cwdfilepath, p.Pointer, false, c.manifest, nil)
|
|
|
|
if err != nil {
|
|
|
|
if errors.IsDownloadDeclinedError(err) {
|
|
|
|
// acceptable error, data not local (fetch not run or include/exclude)
|
|
|
|
LoggedError(err, "Skipped checkout for %q, content not local. Use fetch to download.", p.Name)
|
|
|
|
} else {
|
|
|
|
FullError(fmt.Errorf("Could not check out %q", p.Name))
|
2016-12-14 22:17:24 +00:00
|
|
|
}
|
2016-12-14 22:29:03 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// errors are only returned when the gitIndexer is starting a new cmd
|
|
|
|
if err := c.gitIndexer.Add(cwdfilepath); err != nil {
|
|
|
|
Panic(err, "Could not update the index")
|
2016-12-14 22:17:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *singleCheckout) Close() {
|
|
|
|
if err := c.gitIndexer.Close(); err != nil {
|
|
|
|
LoggedError(err, "Error updating the git index:\n%s", c.gitIndexer.Output())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't fire up the update-index command until we have at least one file to
|
|
|
|
// give it. Otherwise git interprets the lack of arguments to mean param-less update-index
|
|
|
|
// which can trigger entire working copy to be re-examined, which triggers clean filters
|
|
|
|
// and which has unexpected side effects (e.g. downloading filtered-out files)
|
|
|
|
type gitIndexer struct {
|
2017-08-02 18:48:48 +00:00
|
|
|
cmd *subprocess.Cmd
|
2016-12-14 22:17:24 +00:00
|
|
|
input io.WriteCloser
|
|
|
|
output bytes.Buffer
|
|
|
|
mu sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *gitIndexer) Add(path string) error {
|
|
|
|
i.mu.Lock()
|
|
|
|
defer i.mu.Unlock()
|
|
|
|
|
|
|
|
if i.cmd == nil {
|
|
|
|
// Fire up the update-index command
|
2017-08-26 10:39:42 +00:00
|
|
|
cmd := git.UpdateIndexFromStdin()
|
|
|
|
cmd.Stdout = &i.output
|
|
|
|
cmd.Stderr = &i.output
|
|
|
|
stdin, err := cmd.StdinPipe()
|
2016-12-14 22:17:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-26 10:39:42 +00:00
|
|
|
err = cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
i.cmd = cmd
|
2016-12-14 22:17:24 +00:00
|
|
|
i.input = stdin
|
|
|
|
}
|
|
|
|
|
|
|
|
i.input.Write([]byte(path + "\n"))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *gitIndexer) Output() string {
|
|
|
|
return i.output.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *gitIndexer) Close() error {
|
|
|
|
i.mu.Lock()
|
|
|
|
defer i.mu.Unlock()
|
|
|
|
|
|
|
|
if i.input != nil {
|
|
|
|
i.input.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
if i.cmd != nil {
|
|
|
|
return i.cmd.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|