func readyAndMissingPointers(allpointers []*lfs.WrappedPointer, include, exclude []string) ([]*lfs.WrappedPointer, []*lfs.WrappedPointer, int64) { size := int64(0) seen := make(map[string]bool, len(allpointers)) missing := make([]*lfs.WrappedPointer, 0, len(allpointers)) ready := make([]*lfs.WrappedPointer, 0, len(allpointers)) for _, p := range allpointers { // Filtered out by --include or --exclude if !lfs.FilenamePassesIncludeExcludeFilter(p.Name, include, exclude) { continue } // no need to download the same object multiple times if seen[p.Oid] { continue } seen[p.Oid] = true // no need to download objects that exist locally already lfs.LinkOrCopyFromReference(p.Oid, p.Size) if lfs.ObjectExistsOfSize(p.Oid, p.Size) { ready = append(ready, p) continue } missing = append(missing, p) size += p.Size } return ready, missing, size }
func smudgeCommand(cmd *cobra.Command, args []string) { requireStdin("This command should be run by the Git 'smudge' filter") lfs.InstallHooks(false) // keeps the initial buffer from lfs.DecodePointer b := &bytes.Buffer{} r := io.TeeReader(os.Stdin, b) ptr, err := lfs.DecodePointer(r) if err != nil { mr := io.MultiReader(b, os.Stdin) _, err := io.Copy(os.Stdout, mr) if err != nil { Panic(err, "Error writing data to stdout:") } return } lfs.LinkOrCopyFromReference(ptr.Oid, ptr.Size) if smudgeInfo { localPath, err := lfs.LocalMediaPath(ptr.Oid) if err != nil { Exit(err.Error()) } stat, err := os.Stat(localPath) if err != nil { Print("%d --", ptr.Size) } else { Print("%d %s", stat.Size(), localPath) } return } filename := smudgeFilename(args, err) cb, file, err := lfs.CopyCallbackFile("smudge", filename, 1, 1) if err != nil { Error(err.Error()) } download := lfs.FilenamePassesIncludeExcludeFilter(filename, cfg.FetchIncludePaths(), cfg.FetchExcludePaths()) if smudgeSkip || cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) { download = false } err = ptr.Smudge(os.Stdout, filename, download, TransferManifest(), cb) if file != nil { file.Close() } if err != nil { ptr.Encode(os.Stdout) // Download declined error is ok to skip if we weren't requesting download if !(errors.IsDownloadDeclinedError(err) && !download) { LoggedError(err, "Error downloading object: %s (%s)", filename, ptr.Oid) if !cfg.SkipDownloadErrors() { os.Exit(2) } } } }
// Fetch and report completion of each OID to a channel (optional, pass nil to skip) // Returns true if all completed with no errors, false if errors were written to stderr/log func fetchAndReportToChan(pointers []*lfs.WrappedPointer, include, exclude []string, out chan<- *lfs.WrappedPointer) bool { totalSize := int64(0) for _, p := range pointers { totalSize += p.Size } q := lfs.NewDownloadQueue(len(pointers), totalSize, false) if out != nil { dlwatch := q.Watch() go func() { // fetch only reports single OID, but OID *might* be referenced by multiple // WrappedPointers if same content is at multiple paths, so map oid->slice oidToPointers := make(map[string][]*lfs.WrappedPointer, len(pointers)) for _, pointer := range pointers { plist := oidToPointers[pointer.Oid] oidToPointers[pointer.Oid] = append(plist, pointer) } for oid := range dlwatch { plist, ok := oidToPointers[oid] if !ok { continue } for _, p := range plist { out <- p } } close(out) }() } for _, p := range pointers { // Only add to download queue if local file is not the right size already // This avoids previous case of over-reporting a requirement for files we already have // which would only be skipped by PointerSmudgeObject later passFilter := lfs.FilenamePassesIncludeExcludeFilter(p.Name, include, exclude) lfs.LinkOrCopyFromReference(p.Oid, p.Size) if !lfs.ObjectExistsOfSize(p.Oid, p.Size) && passFilter { tracerx.Printf("fetch %v [%v]", p.Name, p.Oid) q.Add(lfs.NewDownloadable(p)) } else { // Ensure progress matches q.Skip(p.Size) if !passFilter { tracerx.Printf("Skipping %v [%v], include/exclude filters applied", p.Name, p.Oid) } else { tracerx.Printf("Skipping %v [%v], already exists", p.Name, p.Oid) } // If we already have it, or it won't be fetched // report it to chan immediately to support pull/checkout if out != nil { out <- p } } } processQueue := time.Now() q.Wait() tracerx.PerformanceSince("process queue", processQueue) ok := true for _, err := range q.Errors() { ok = false ExitWithError(err) } return ok }