github.com/purpleclay/gitz@v0.8.2-0.20240515052600-43f80eea2fe1/push.go (about)

     1  package git
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  // PushOption provides a way of setting specific options during a git
     9  // push operation. Each supported option can customize the way in which
    10  // references are pushed back to the remote
    11  type PushOption func(*pushOptions)
    12  
    13  type pushOptions struct {
    14  	All         bool
    15  	Config      []string
    16  	Delete      bool
    17  	PushOptions []string
    18  	Tags        bool
    19  	RefSpecs    []string
    20  }
    21  
    22  // WithAllBranches will push all locally created branch references
    23  // back to the remote
    24  func WithAllBranches() PushOption {
    25  	return func(opts *pushOptions) {
    26  		opts.All = true
    27  	}
    28  }
    29  
    30  // WithAllTags will push all locally created tag references back
    31  // to the remote
    32  func WithAllTags() PushOption {
    33  	return func(opts *pushOptions) {
    34  		opts.Tags = true
    35  	}
    36  }
    37  
    38  // WithDeleteRefSpecs will trigger the deletion of any named references
    39  // when pushed back to the remote
    40  func WithDeleteRefSpecs(refs ...string) PushOption {
    41  	return func(opts *pushOptions) {
    42  		opts.Delete = true
    43  		opts.RefSpecs = trim(refs...)
    44  	}
    45  }
    46  
    47  // WithPushConfig allows temporary git config to be set while pushing
    48  // changes to the remote. Config set using this approach will override
    49  // any config defined within existing git config files. Config must be
    50  // provided as key value pairs, mismatched config will result in an
    51  // [ErrMissingConfigValue] error. Any invalid paths will result in an
    52  // [ErrInvalidConfigPath] error
    53  func WithPushConfig(kv ...string) PushOption {
    54  	return func(opts *pushOptions) {
    55  		opts.Config = trim(kv...)
    56  	}
    57  }
    58  
    59  // WithPushOptions allows any number of aribitrary strings to be pushed
    60  // to the remote server. All options are transmitted in their received
    61  // order. A server must have the git config setting receive.advertisePushOptions
    62  // set to true to receive push options
    63  func WithPushOptions(options ...string) PushOption {
    64  	return func(opts *pushOptions) {
    65  		opts.PushOptions = trim(options...)
    66  	}
    67  }
    68  
    69  // WithRefSpecs allows local references to be cherry-picked and
    70  // pushed back to the remote. A reference (or refspec) can be as
    71  // simple as a name, where git will automatically resolve any
    72  // ambiguity, or as explicit as providing a source and destination
    73  // for each local reference within the remote. Check out the official
    74  // git documentation on how to write a more complex [refspec]
    75  //
    76  // [refspec]: https://git-scm.com/docs/git-push#Documentation/git-push.txt-ltrefspecgt82308203
    77  func WithRefSpecs(refs ...string) PushOption {
    78  	return func(opts *pushOptions) {
    79  		opts.RefSpecs = trim(refs...)
    80  	}
    81  }
    82  
    83  // Push (or upload) all local changes to the remote repository.
    84  // By default, changes associated with the current branch will
    85  // be pushed back to the remote. Options can be provided to
    86  // configure branch and tag push semantics
    87  func (c *Client) Push(opts ...PushOption) (string, error) {
    88  	options := &pushOptions{}
    89  	for _, opt := range opts {
    90  		opt(options)
    91  	}
    92  
    93  	cfg, err := ToInlineConfig(options.Config...)
    94  	if err != nil {
    95  		return "", err
    96  	}
    97  
    98  	var buf strings.Builder
    99  	buf.WriteString("git")
   100  
   101  	if len(cfg) > 0 {
   102  		buf.WriteString(" ")
   103  		buf.WriteString(strings.Join(cfg, " "))
   104  	}
   105  	buf.WriteString(" push")
   106  
   107  	for _, po := range options.PushOptions {
   108  		buf.WriteString(" --push-option=" + po)
   109  	}
   110  
   111  	if options.All {
   112  		buf.WriteString(" --all")
   113  	} else if options.Tags {
   114  		buf.WriteString(" --tags")
   115  	} else if len(options.RefSpecs) > 0 {
   116  		buf.WriteString(" origin ")
   117  		if options.Delete {
   118  			buf.WriteString("--delete ")
   119  		}
   120  
   121  		buf.WriteString(strings.Join(options.RefSpecs, " "))
   122  	} else {
   123  		out, err := c.exec("git branch --show-current")
   124  		if err != nil {
   125  			return out, err
   126  		}
   127  		buf.WriteString(fmt.Sprintf(" origin %s", out))
   128  	}
   129  
   130  	return c.exec(buf.String())
   131  }
   132  
   133  // PushRef will push an individual reference to the remote repository
   134  // Deprecated: use [Push] instead
   135  func (c *Client) PushRef(ref string) (string, error) {
   136  	return c.exec(fmt.Sprintf("git push origin %s", ref))
   137  }