git-lfs/commands/command_checkout.go
Marat Radchenko 482260c7e3 Fix error strings to follow Go guidelines
Error strings should not be capitalized (unless beginning with proper nouns or acronyms) or end with punctuation:
https://github.com/golang/go/wiki/CodeReviewComments#error-strings
2019-10-22 17:33:49 +03:00

171 lines
4.3 KiB
Go

package commands
import (
"fmt"
"os"
"github.com/git-lfs/git-lfs/filepathfilter"
"github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/lfs"
"github.com/git-lfs/git-lfs/tasklog"
"github.com/git-lfs/git-lfs/tq"
"github.com/spf13/cobra"
)
var (
checkoutTo string
checkoutBase bool
checkoutOurs bool
checkoutTheirs bool
)
func checkoutCommand(cmd *cobra.Command, args []string) {
requireInRepo()
stage, err := whichCheckout()
if err != nil {
Exit("Error parsing args: %v", err)
}
if checkoutTo != "" && stage != git.IndexStageDefault {
checkoutConflict(rootedPaths(args)[0], stage)
return
} else if checkoutTo != "" || stage != git.IndexStageDefault {
Exit("--to and exactly one of --theirs, --ours, and --base must be used together")
}
ref, err := git.CurrentRef()
if err != nil {
Panic(err, "Could not checkout")
}
singleCheckout := newSingleCheckout(cfg.Git, "")
if singleCheckout.Skip() {
fmt.Println("Cannot checkout LFS objects, Git LFS is not installed.")
return
}
var totalBytes int64
var pointers []*lfs.WrappedPointer
logger := tasklog.NewLogger(os.Stdout,
tasklog.ForceProgress(cfg.ForceProgress()),
)
meter := tq.NewMeter(cfg)
meter.Direction = tq.Checkout
meter.Logger = meter.LoggerFromEnv(cfg.Os)
logger.Enqueue(meter)
chgitscanner := lfs.NewGitScanner(cfg, func(p *lfs.WrappedPointer, err error) {
if err != nil {
LoggedError(err, "Scanner error: %s", err)
return
}
totalBytes += p.Size
meter.Add(p.Size)
meter.StartTransfer(p.Name)
pointers = append(pointers, p)
})
chgitscanner.Filter = filepathfilter.New(rootedPaths(args), nil)
if err := chgitscanner.ScanTree(ref.Sha); err != nil {
ExitWithError(err)
}
chgitscanner.Close()
meter.Start()
for _, p := range pointers {
singleCheckout.Run(p)
// not strictly correct (parallel) but we don't have a callback & it's just local
// plus only 1 slot in channel so it'll block & be close
meter.TransferBytes("checkout", p.Name, p.Size, totalBytes, int(p.Size))
meter.FinishTransfer(p.Name)
}
meter.Finish()
singleCheckout.Close()
}
func checkoutConflict(file string, stage git.IndexStage) {
singleCheckout := newSingleCheckout(cfg.Git, "")
if singleCheckout.Skip() {
fmt.Println("Cannot checkout LFS objects, Git LFS is not installed.")
return
}
ref, err := git.ResolveRef(fmt.Sprintf(":%d:%s", stage, file))
if err != nil {
Exit("Could not checkout (are you not in the middle of a merge?): %v", err)
}
scanner, err := git.NewObjectScanner(cfg.OSEnv())
if err != nil {
Exit("Could not create object scanner: %v", err)
}
if !scanner.Scan(ref.Sha) {
Exit("Could not find object %q", ref.Sha)
}
ptr, err := lfs.DecodePointer(scanner.Contents())
if err != nil {
Exit("Could not find decoder pointer for object %q: %v", ref.Sha, err)
}
p := &lfs.WrappedPointer{Name: file, Pointer: ptr}
if err := singleCheckout.RunToPath(p, checkoutTo); err != nil {
Exit("Error checking out %v to %q: %v", ref.Sha, checkoutTo, err)
}
singleCheckout.Close()
}
func whichCheckout() (stage git.IndexStage, err error) {
seen := 0
stage = git.IndexStageDefault
if checkoutBase {
seen++
stage = git.IndexStageBase
}
if checkoutOurs {
seen++
stage = git.IndexStageOurs
}
if checkoutTheirs {
seen++
stage = git.IndexStageTheirs
}
if seen > 1 {
return 0, fmt.Errorf("at most one of --base, --theirs, and --ours is allowed")
}
return stage, nil
}
// Parameters are filters
// firstly convert any pathspecs to the root of the repo, in case this is being
// executed in a sub-folder
func rootedPaths(args []string) []string {
pathConverter, err := lfs.NewCurrentToRepoPathConverter(cfg)
if err != nil {
Panic(err, "Could not checkout")
}
rootedpaths := make([]string, 0, len(args))
for _, arg := range args {
rootedpaths = append(rootedpaths, pathConverter.Convert(arg))
}
return rootedpaths
}
func init() {
RegisterCommand("checkout", checkoutCommand, func(cmd *cobra.Command) {
cmd.Flags().StringVar(&checkoutTo, "to", "", "Checkout a conflicted file to this path")
cmd.Flags().BoolVar(&checkoutOurs, "ours", false, "Checkout our version of a conflicted file")
cmd.Flags().BoolVar(&checkoutTheirs, "theirs", false, "Checkout their version of a conflicted file")
cmd.Flags().BoolVar(&checkoutBase, "base", false, "Checkout the base version of a conflicted file")
})
}