github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/commands/command_pre_push.go (about) 1 package commands 2 3 import ( 4 "bufio" 5 "io" 6 "os" 7 "strings" 8 9 "github.com/git-lfs/git-lfs/git" 10 "github.com/rubyist/tracerx" 11 "github.com/spf13/cobra" 12 ) 13 14 var ( 15 prePushDryRun = false 16 prePushDeleteBranch = strings.Repeat("0", 40) 17 ) 18 19 // prePushCommand is run through Git's pre-push hook. The pre-push hook passes 20 // two arguments on the command line: 21 // 22 // 1. Name of the remote to which the push is being done 23 // 2. URL to which the push is being done 24 // 25 // The hook receives commit information on stdin in the form: 26 // <local ref> <local sha1> <remote ref> <remote sha1> 27 // 28 // In the typical case, prePushCommand will get a list of git objects being 29 // pushed by using the following: 30 // 31 // git rev-list --objects <local sha1> ^<remote sha1> 32 // 33 // If any of those git objects are associated with Git LFS objects, those 34 // objects will be pushed to the Git LFS API. 35 // 36 // In the case of pushing a new branch, the list of git objects will be all of 37 // the git objects in this branch. 38 // 39 // In the case of deleting a branch, no attempts to push Git LFS objects will be 40 // made. 41 func prePushCommand(cmd *cobra.Command, args []string) { 42 if len(args) == 0 { 43 Print("This should be run through Git's pre-push hook. Run `git lfs update` to install it.") 44 os.Exit(1) 45 } 46 47 requireGitVersion() 48 49 // Remote is first arg 50 if err := cfg.SetValidRemote(args[0]); err != nil { 51 Exit("Invalid remote name %q: %s", args[0], err) 52 } 53 54 ctx := newUploadContext(prePushDryRun) 55 updates := prePushRefs(os.Stdin) 56 if err := uploadForRefUpdates(ctx, updates, false); err != nil { 57 ExitWithError(err) 58 } 59 } 60 61 // prePushRefs parses commit information that the pre-push git hook receives: 62 // 63 // <local ref> <local sha1> <remote ref> <remote sha1> 64 // 65 // Each line describes a proposed update of the remote ref at the remote sha to 66 // the local sha. Multiple updates can be received on multiple lines (such as 67 // from 'git push --all'). These updates are typically received over STDIN. 68 func prePushRefs(r io.Reader) []*git.RefUpdate { 69 scanner := bufio.NewScanner(r) 70 refs := make([]*git.RefUpdate, 0, 1) 71 72 // We can be passed multiple lines of refs 73 for scanner.Scan() { 74 line := strings.TrimSpace(scanner.Text()) 75 if len(line) == 0 { 76 continue 77 } 78 79 tracerx.Printf("pre-push: %s", line) 80 81 left, right := decodeRefs(line) 82 if left.Sha == prePushDeleteBranch { 83 continue 84 } 85 86 refs = append(refs, git.NewRefUpdate(cfg.Git, cfg.PushRemote(), left, right)) 87 } 88 89 return refs 90 } 91 92 // decodeRefs pulls the sha1s out of the line read from the pre-push 93 // hook's stdin. 94 func decodeRefs(input string) (*git.Ref, *git.Ref) { 95 refs := strings.Split(strings.TrimSpace(input), " ") 96 for len(refs) < 4 { 97 refs = append(refs, "") 98 } 99 100 leftRef := git.ParseRef(refs[0], refs[1]) 101 rightRef := git.ParseRef(refs[2], refs[3]) 102 return leftRef, rightRef 103 } 104 105 func init() { 106 RegisterCommand("pre-push", prePushCommand, func(cmd *cobra.Command) { 107 cmd.Flags().BoolVarP(&prePushDryRun, "dry-run", "d", false, "Do everything except actually send the updates") 108 }) 109 }