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

     1  package git
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  )
     7  
     8  // FetchOption provides a way for setting specific options while fetching changes
     9  // from the remote. Each supported option can customize how changes are fetched
    10  // from the remote
    11  type FetchOption func(*fetchOptions)
    12  
    13  type fetchOptions struct {
    14  	All      bool
    15  	Config   []string
    16  	Depth    int
    17  	Force    bool
    18  	NoTags   bool
    19  	RefSpecs []string
    20  	Tags     bool
    21  }
    22  
    23  func (o fetchOptions) String() string {
    24  	var buf strings.Builder
    25  
    26  	if o.All {
    27  		buf.WriteString(" --all")
    28  	}
    29  
    30  	if o.Depth > 0 {
    31  		buf.WriteString(" --depth ")
    32  		buf.WriteString(strconv.Itoa(o.Depth))
    33  	}
    34  
    35  	if o.Tags {
    36  		buf.WriteString(" --tags")
    37  	}
    38  
    39  	if o.Force {
    40  		buf.WriteString(" --force")
    41  	}
    42  
    43  	if o.NoTags {
    44  		buf.WriteString(" --no-tags")
    45  	}
    46  
    47  	if len(o.RefSpecs) > 0 {
    48  		buf.WriteString(" origin ")
    49  		buf.WriteString(strings.Join(o.RefSpecs, " "))
    50  	}
    51  
    52  	return buf.String()
    53  }
    54  
    55  // WithFetchConfig allows temporary git config to be set while fetching
    56  // changes from the remote. Config set using this approach will override
    57  // any config defined within existing git config files. Config must be
    58  // provided as key value pairs, mismatched config will result in an
    59  // [ErrMissingConfigValue] error. Any invalid paths will result in an
    60  // [ErrInvalidConfigPath] error
    61  func WithFetchConfig(kv ...string) FetchOption {
    62  	return func(opts *fetchOptions) {
    63  		opts.Config = trim(kv...)
    64  	}
    65  }
    66  
    67  // WithAll will fetch the latest changes from all tracked remotes
    68  func WithAll() FetchOption {
    69  	return func(opts *fetchOptions) {
    70  		opts.All = true
    71  	}
    72  }
    73  
    74  // WithTags will fetch all tags from the remote into local tag
    75  // references with the same name
    76  func WithTags() FetchOption {
    77  	return func(opts *fetchOptions) {
    78  		opts.Tags = true
    79  	}
    80  }
    81  
    82  // WithDepthTo will limit the number of commits to be fetched from the
    83  // remotes history to the specified depth. If fetching into a shallow
    84  // clone of a repository, this can be used to shorten or deepen the
    85  // existing history
    86  func WithDepthTo(depth int) FetchOption {
    87  	return func(opts *fetchOptions) {
    88  		opts.Depth = depth
    89  	}
    90  }
    91  
    92  // WithForce will force the fetching of a remote branch into a local
    93  // branch with a different name (or refspec). Default behavior within
    94  // git prevents such an operation. Typically used in conjunction with
    95  // the [WithFetchRefSpecs] option
    96  func WithForce() FetchOption {
    97  	return func(opts *fetchOptions) {
    98  		opts.Force = true
    99  	}
   100  }
   101  
   102  // WithIgnoreTags disables local tracking of tags from the remote
   103  func WithIgnoreTags() FetchOption {
   104  	return func(opts *fetchOptions) {
   105  		opts.NoTags = true
   106  	}
   107  }
   108  
   109  // WithFetchRefSpecs allows remote references to be cherry-picked and
   110  // fetched into the current repository (working copy). A reference
   111  // (or refspec) can be as simple as a name, where git will automatically
   112  // resolve any ambiguity, or as explicit as providing a source and destination
   113  // for reference within the remote. Check out the official git documentation
   114  // on how to write a more complex [refspec]
   115  // [refspec]: https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt-ltrefspecgt
   116  func WithFetchRefSpecs(refs ...string) FetchOption {
   117  	return func(opts *fetchOptions) {
   118  		opts.RefSpecs = trim(refs...)
   119  	}
   120  }
   121  
   122  // Fetch all remote changes from a remote repository without integrating (merging)
   123  // them into the current repository (working directory). Ensures the current repository
   124  // only tracks the latest remote changes
   125  func (c *Client) Fetch(opts ...FetchOption) (string, error) {
   126  	options := &fetchOptions{}
   127  	for _, opt := range opts {
   128  		opt(options)
   129  	}
   130  
   131  	cfg, err := ToInlineConfig(options.Config...)
   132  	if err != nil {
   133  		return "", err
   134  	}
   135  
   136  	var buf strings.Builder
   137  	buf.WriteString("git")
   138  
   139  	if len(cfg) > 0 {
   140  		buf.WriteString(" ")
   141  		buf.WriteString(strings.Join(cfg, " "))
   142  	}
   143  
   144  	buf.WriteString(" fetch")
   145  	buf.WriteString(options.String())
   146  	return c.exec(buf.String())
   147  }