github.com/git-lfs/git-lfs@v2.5.2+incompatible/commands/command_pull.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/git-lfs/git-lfs/filepathfilter"
    10  	"github.com/git-lfs/git-lfs/git"
    11  	"github.com/git-lfs/git-lfs/lfs"
    12  	"github.com/git-lfs/git-lfs/tasklog"
    13  	"github.com/git-lfs/git-lfs/tq"
    14  	"github.com/rubyist/tracerx"
    15  	"github.com/spf13/cobra"
    16  )
    17  
    18  func pullCommand(cmd *cobra.Command, args []string) {
    19  	requireGitVersion()
    20  	requireInRepo()
    21  
    22  	if len(args) > 0 {
    23  		// Remote is first arg
    24  		if err := cfg.SetValidRemote(args[0]); err != nil {
    25  			Exit("Invalid remote name %q: %s", args[0], err)
    26  		}
    27  	}
    28  
    29  	includeArg, excludeArg := getIncludeExcludeArgs(cmd)
    30  	filter := buildFilepathFilter(cfg, includeArg, excludeArg)
    31  	pull(filter)
    32  }
    33  
    34  func pull(filter *filepathfilter.Filter) {
    35  	ref, err := git.CurrentRef()
    36  	if err != nil {
    37  		Panic(err, "Could not pull")
    38  	}
    39  
    40  	pointers := newPointerMap()
    41  	logger := tasklog.NewLogger(os.Stdout)
    42  	meter := tq.NewMeter()
    43  	meter.Logger = meter.LoggerFromEnv(cfg.Os)
    44  	logger.Enqueue(meter)
    45  	remote := cfg.Remote()
    46  	singleCheckout := newSingleCheckout(cfg.Git, remote)
    47  	q := newDownloadQueue(singleCheckout.Manifest(), remote, tq.WithProgress(meter))
    48  	gitscanner := lfs.NewGitScanner(func(p *lfs.WrappedPointer, err error) {
    49  		if err != nil {
    50  			LoggedError(err, "Scanner error: %s", err)
    51  			return
    52  		}
    53  
    54  		if pointers.Seen(p) {
    55  			return
    56  		}
    57  
    58  		// no need to download objects that exist locally already
    59  		lfs.LinkOrCopyFromReference(cfg, p.Oid, p.Size)
    60  		if cfg.LFSObjectExists(p.Oid, p.Size) {
    61  			singleCheckout.Run(p)
    62  			return
    63  		}
    64  
    65  		meter.Add(p.Size)
    66  		tracerx.Printf("fetch %v [%v]", p.Name, p.Oid)
    67  		pointers.Add(p)
    68  		q.Add(downloadTransfer(p))
    69  	})
    70  
    71  	gitscanner.Filter = filter
    72  
    73  	dlwatch := q.Watch()
    74  	var wg sync.WaitGroup
    75  	wg.Add(1)
    76  
    77  	go func() {
    78  		for t := range dlwatch {
    79  			for _, p := range pointers.All(t.Oid) {
    80  				singleCheckout.Run(p)
    81  			}
    82  		}
    83  		wg.Done()
    84  	}()
    85  
    86  	processQueue := time.Now()
    87  	if err := gitscanner.ScanTree(ref.Sha); err != nil {
    88  		singleCheckout.Close()
    89  		ExitWithError(err)
    90  	}
    91  
    92  	meter.Start()
    93  	gitscanner.Close()
    94  	q.Wait()
    95  	wg.Wait()
    96  	tracerx.PerformanceSince("process queue", processQueue)
    97  
    98  	singleCheckout.Close()
    99  
   100  	success := true
   101  	for _, err := range q.Errors() {
   102  		success = false
   103  		FullError(err)
   104  	}
   105  
   106  	if !success {
   107  		c := getAPIClient()
   108  		e := c.Endpoints.Endpoint("download", remote)
   109  		Exit("error: failed to fetch some objects from '%s'", e.Url)
   110  	}
   111  
   112  	if singleCheckout.Skip() {
   113  		fmt.Println("Skipping object checkout, Git LFS is not installed.")
   114  	}
   115  }
   116  
   117  // tracks LFS objects being downloaded, according to their unique OIDs.
   118  type pointerMap struct {
   119  	pointers map[string][]*lfs.WrappedPointer
   120  	mu       sync.Mutex
   121  }
   122  
   123  func newPointerMap() *pointerMap {
   124  	return &pointerMap{pointers: make(map[string][]*lfs.WrappedPointer)}
   125  }
   126  
   127  func (m *pointerMap) Seen(p *lfs.WrappedPointer) bool {
   128  	m.mu.Lock()
   129  	defer m.mu.Unlock()
   130  	if existing, ok := m.pointers[p.Oid]; ok {
   131  		m.pointers[p.Oid] = append(existing, p)
   132  		return true
   133  	}
   134  	return false
   135  }
   136  
   137  func (m *pointerMap) Add(p *lfs.WrappedPointer) {
   138  	m.mu.Lock()
   139  	defer m.mu.Unlock()
   140  	m.pointers[p.Oid] = append(m.pointers[p.Oid], p)
   141  }
   142  
   143  func (m *pointerMap) All(oid string) []*lfs.WrappedPointer {
   144  	m.mu.Lock()
   145  	defer m.mu.Unlock()
   146  	pointers := m.pointers[oid]
   147  	delete(m.pointers, oid)
   148  	return pointers
   149  }
   150  
   151  func init() {
   152  	RegisterCommand("pull", pullCommand, func(cmd *cobra.Command) {
   153  		cmd.Flags().StringVarP(&includeArg, "include", "I", "", "Include a list of paths")
   154  		cmd.Flags().StringVarP(&excludeArg, "exclude", "X", "", "Exclude a list of paths")
   155  	})
   156  }