package commands import ( "os" "github.com/git-lfs/git-lfs/errors" "github.com/git-lfs/git-lfs/git" "github.com/git-lfs/git-lfs/lfs" "github.com/rubyist/tracerx" "github.com/spf13/cobra" ) var ( pushDryRun = false pushObjectIDs = false pushAll = false useStdin = false // shares some global vars and functions with command_pre_push.go ) // pushCommand pushes local objects to a Git LFS server. It takes two // arguments: // // ` ` // // Remote must be a remote name, not a URL // // pushCommand calculates the git objects to send by comparing the range // of commits between the local and remote git servers. func pushCommand(cmd *cobra.Command, args []string) { if len(args) == 0 { Print("Specify a remote and a remote branch name (`git lfs push origin master`)") os.Exit(1) } requireGitVersion() // Remote is first arg if err := cfg.SetValidRemote(args[0]); err != nil { Exit("Invalid remote name %q: %s", args[0], err) } ctx := newUploadContext(pushDryRun) if pushObjectIDs { if len(args) < 2 { Print("Usage: git lfs push --object-id [lfs-object-id] ...") return } uploadsWithObjectIDs(ctx, args[1:]) } else { if len(args) < 1 { Print("Usage: git lfs push --dry-run [ref]") return } uploadsBetweenRefAndRemote(ctx, args[1:]) } } func uploadsBetweenRefAndRemote(ctx *uploadContext, refnames []string) { tracerx.Printf("Upload refs %v to remote %v", refnames, ctx.Remote) gitscanner, err := ctx.buildGitScanner() if err != nil { ExitWithError(err) } defer gitscanner.Close() updates, err := lfsPushRefs(refnames, pushAll) if err != nil { Error(err.Error()) Exit("Error getting local refs.") } for _, update := range updates { if err = uploadLeftOrAll(gitscanner, ctx, update); err != nil { Print("Error scanning for Git LFS files in the %q ref", update.Left().Name) ExitWithError(err) } } ctx.Await() } func uploadsWithObjectIDs(ctx *uploadContext, oids []string) { for _, oid := range oids { mp, err := ctx.gitfilter.ObjectPath(oid) if err != nil { ExitWithError(errors.Wrap(err, "Unable to find local media path:")) } stat, err := os.Stat(mp) if err != nil { ExitWithError(errors.Wrap(err, "Unable to stat local media path")) } uploadPointers(ctx, &lfs.WrappedPointer{ Name: mp, Pointer: &lfs.Pointer{ Oid: oid, Size: stat.Size(), }, }) } ctx.Await() } // lfsPushRefs returns valid ref updates from the given ref and --all arguments. // Either one or more refs can be explicitly specified, or --all indicates all // local refs are pushed. func lfsPushRefs(refnames []string, pushAll bool) ([]*refUpdate, error) { localrefs, err := git.LocalRefs() if err != nil { return nil, err } if pushAll && len(refnames) == 0 { refs := make([]*refUpdate, len(localrefs)) for i, lr := range localrefs { refs[i] = newRefUpdate(cfg.Git, lr, nil) } return refs, nil } reflookup := make(map[string]*git.Ref, len(localrefs)) for _, ref := range localrefs { reflookup[ref.Name] = ref } refs := make([]*refUpdate, len(refnames)) for i, name := range refnames { if left, ok := reflookup[name]; ok { refs[i] = newRefUpdate(cfg.Git, left, nil) } else { left := &git.Ref{Name: name, Type: git.RefTypeOther, Sha: name} refs[i] = newRefUpdate(cfg.Git, left, nil) } } return refs, nil } func init() { RegisterCommand("push", pushCommand, func(cmd *cobra.Command) { cmd.Flags().BoolVarP(&pushDryRun, "dry-run", "d", false, "Do everything except actually send the updates") cmd.Flags().BoolVarP(&pushObjectIDs, "object-id", "o", false, "Push LFS object ID(s)") cmd.Flags().BoolVarP(&pushAll, "all", "a", false, "Push all objects for the current ref to the remote.") }) }