github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/commands/command_smudge.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "github.com/git-lfs/git-lfs/errors" 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/tools" 13 "github.com/git-lfs/git-lfs/tools/humanize" 14 "github.com/git-lfs/git-lfs/tq" 15 "github.com/spf13/cobra" 16 ) 17 18 var ( 19 // smudgeSkip is a command-line flag belonging to the "git-lfs smudge" 20 // command specifying whether to skip the smudge process. 21 smudgeSkip = false 22 ) 23 24 // delayedSmudge performs a 'delayed' smudge, adding the LFS pointer to the 25 // `*tq.TransferQueue` "q" if the file is not present locally, passes the given 26 // filepathfilter, and is not skipped. If the pointer is malformed, or already 27 // exists, it streams the contents to be written into the working copy to "to". 28 // 29 // delayedSmudge returns the number of bytes written, whether the checkout was 30 // delayed, the *lfs.Pointer that was smudged, and an error, if one occurred. 31 func delayedSmudge(gf *lfs.GitFilter, s *git.FilterProcessScanner, to io.Writer, from io.Reader, q *tq.TransferQueue, filename string, skip bool, filter *filepathfilter.Filter) (int64, bool, *lfs.Pointer, error) { 32 ptr, pbuf, perr := lfs.DecodeFrom(from) 33 if perr != nil { 34 // Write 'statusFromErr(nil)', even though 'perr != nil', since 35 // we are about to write non-delayed smudged contents to "to". 36 if err := s.WriteStatus(statusFromErr(nil)); err != nil { 37 return 0, false, nil, err 38 } 39 40 n, err := tools.Spool(to, pbuf, cfg.TempDir()) 41 if err != nil { 42 return n, false, nil, errors.Wrap(err, perr.Error()) 43 } 44 45 if n != 0 { 46 return 0, false, nil, errors.NewNotAPointerError(errors.Errorf( 47 "Unable to parse pointer at: %q", filename, 48 )) 49 } 50 return 0, false, nil, nil 51 } 52 53 lfs.LinkOrCopyFromReference(cfg, ptr.Oid, ptr.Size) 54 55 path, err := cfg.Filesystem().ObjectPath(ptr.Oid) 56 if err != nil { 57 return 0, false, nil, err 58 } 59 60 if !skip && filter.Allows(filename) { 61 if _, statErr := os.Stat(path); statErr != nil { 62 q.Add(filename, path, ptr.Oid, ptr.Size) 63 return 0, true, ptr, nil 64 } 65 66 // Write 'statusFromErr(nil)', since the object is already 67 // present in the local cache, we will write the object's 68 // contents without delaying. 69 if err := s.WriteStatus(statusFromErr(nil)); err != nil { 70 return 0, false, nil, err 71 } 72 73 n, err := gf.Smudge(to, ptr, filename, false, nil, nil) 74 return n, false, ptr, err 75 } 76 77 if err := s.WriteStatus(statusFromErr(nil)); err != nil { 78 return 0, false, nil, err 79 } 80 81 n, err := ptr.Encode(to) 82 return int64(n), false, ptr, err 83 } 84 85 // smudge smudges the given `*lfs.Pointer`, "ptr", and writes its objects 86 // contents to the `io.Writer`, "to". 87 // 88 // If the encoded LFS pointer is not parse-able as a pointer, the contents of 89 // that file will instead be spooled to a temporary location on disk and then 90 // copied out back to Git. If the pointer file is empty, an empty file will be 91 // written with no error. 92 // 93 // If the smudged object did not "pass" the include and exclude filterset, it 94 // will not be downloaded, and the object will remain a pointer on disk, as if 95 // the smudge filter had not been applied at all. 96 // 97 // Any errors encountered along the way will be returned immediately if they 98 // were non-fatal, otherwise execution will halt and the process will be 99 // terminated by using the `commands.Panic()` func. 100 func smudge(gf *lfs.GitFilter, to io.Writer, from io.Reader, filename string, skip bool, filter *filepathfilter.Filter) (int64, error) { 101 ptr, pbuf, perr := lfs.DecodeFrom(from) 102 if perr != nil { 103 n, err := tools.Spool(to, pbuf, cfg.TempDir()) 104 if err != nil { 105 return 0, errors.Wrap(err, perr.Error()) 106 } 107 108 if n != 0 { 109 return 0, errors.NewNotAPointerError(errors.Errorf( 110 "Unable to parse pointer at: %q", filename, 111 )) 112 } 113 return 0, nil 114 } 115 116 lfs.LinkOrCopyFromReference(cfg, ptr.Oid, ptr.Size) 117 cb, file, err := gf.CopyCallbackFile("download", filename, 1, 1) 118 if err != nil { 119 return 0, err 120 } 121 122 download := !skip 123 if download { 124 download = filter.Allows(filename) 125 } 126 127 n, err := gf.Smudge(to, ptr, filename, download, getTransferManifest(), cb) 128 if file != nil { 129 file.Close() 130 } 131 132 if err != nil { 133 ptr.Encode(to) 134 // Download declined error is ok to skip if we weren't requesting download 135 if !(errors.IsDownloadDeclinedError(err) && !download) { 136 var oid string = ptr.Oid 137 if len(oid) >= 7 { 138 oid = oid[:7] 139 } 140 141 LoggedError(err, "Error downloading object: %s (%s): %s", filename, oid, err) 142 if !cfg.SkipDownloadErrors() { 143 os.Exit(2) 144 } 145 } 146 } 147 148 return n, nil 149 } 150 151 func smudgeCommand(cmd *cobra.Command, args []string) { 152 requireStdin("This command should be run by the Git 'smudge' filter") 153 installHooks(false) 154 155 if !smudgeSkip && cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) { 156 smudgeSkip = true 157 } 158 filter := filepathfilter.New(cfg.FetchIncludePaths(), cfg.FetchExcludePaths()) 159 gitfilter := lfs.NewGitFilter(cfg) 160 161 if n, err := smudge(gitfilter, os.Stdout, os.Stdin, smudgeFilename(args), smudgeSkip, filter); err != nil { 162 if errors.IsNotAPointerError(err) { 163 fmt.Fprintln(os.Stderr, err.Error()) 164 } else { 165 Error(err.Error()) 166 } 167 } else if possiblyMalformedObjectSize(n) { 168 fmt.Fprintln(os.Stderr, "Possibly malformed smudge on Windows: see `git lfs help smudge` for more info.") 169 } 170 } 171 172 func smudgeFilename(args []string) string { 173 if len(args) > 0 { 174 return args[0] 175 } 176 return "<unknown file>" 177 } 178 179 func possiblyMalformedObjectSize(n int64) bool { 180 return n > 4*humanize.Gigabyte 181 } 182 183 func init() { 184 RegisterCommand("smudge", smudgeCommand, func(cmd *cobra.Command) { 185 cmd.Flags().BoolVarP(&smudgeSkip, "skip", "s", false, "") 186 }) 187 }