aboutsummaryrefslogblamecommitdiffstats
path: root/config/config.go
blob: bffb125d637215411c6856d649d09b8fb3db8ff8 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                        

              
        
               

                

                                                         


       



                                                                     
 
 
                                                




                                 
     
                                                                      


                                                                          

 
                                               
                                                                   
                    


                           
                                        




                                                                                   





                                                           
                                      















                                                          

 




































































                                                                    
                                                                 


                          
                       

                              











                                                          
                              
                                                                                      


                  
 





























                                                          
// Package config storage is the implementation of git config for go-git
package config

import (
	"bytes"
	"errors"
	"fmt"

	"gopkg.in/src-d/go-git.v4/plumbing/format/config"
)

const (
	// DefaultFetchRefSpec is the default refspec used for fetch.
	DefaultFetchRefSpec = "+refs/heads/*:refs/remotes/%s/*"
	// DefaultPushRefSpec is the default refspec used for push.
	DefaultPushRefSpec = "refs/heads/*:refs/heads/*"
)

// ConfigStorer generic storage of Config object
type ConfigStorer interface {
	Config() (*Config, error)
	SetConfig(*Config) error
}

var (
	ErrInvalid               = errors.New("config invalid remote")
	ErrRemoteConfigNotFound  = errors.New("remote config not found")
	ErrRemoteConfigEmptyURL  = errors.New("remote config: empty URL")
	ErrRemoteConfigEmptyName = errors.New("remote config: empty name")
)

// Config contains the repository configuration
// https://www.kernel.org/pub/software/scm/git/docs/git-config.html
type Config struct {
	Core struct {
		IsBare bool
	}
	Remotes map[string]*RemoteConfig

	// contains the raw information of a config file, the main goal is preserve
	// the parsed information from the original format, to avoid miss not
	// supported properties
	raw *config.Config
}

// NewConfig returns a new empty Config
func NewConfig() *Config {
	return &Config{
		Remotes: make(map[string]*RemoteConfig, 0),
		raw:     config.New(),
	}
}

// Validate validate the fields and set the default values
func (c *Config) Validate() error {
	for name, r := range c.Remotes {
		if r.Name != name {
			return ErrInvalid
		}

		if err := r.Validate(); err != nil {
			return err
		}
	}

	return nil
}

const (
	remoteSection = "remote"
	coreSection   = "core"
	fetchKey      = "fetch"
	urlKey        = "url"
	bareKey       = "bare"
)

// Unmarshal parses a git-config file and stores it
func (c *Config) Unmarshal(b []byte) error {
	r := bytes.NewBuffer(b)
	d := config.NewDecoder(r)

	c.raw = config.New()
	if err := d.Decode(c.raw); err != nil {
		return err
	}

	c.unmarshalCore()
	c.unmarshalRemotes()
	return nil
}

func (c *Config) unmarshalCore() {
	s := c.raw.Section(coreSection)
	if s.Options.Get(bareKey) == "true" {
		c.Core.IsBare = true
	}
}

func (c *Config) unmarshalRemotes() {
	s := c.raw.Section(remoteSection)
	for _, sub := range s.Subsections {
		r := &RemoteConfig{}
		r.unmarshal(sub)

		c.Remotes[r.Name] = r
	}
}

// Marshal returns Config encoded as a git-config file
func (c *Config) Marshal() ([]byte, error) {
	c.marshalCore()
	c.marshalRemotes()

	buf := bytes.NewBuffer(nil)
	if err := config.NewEncoder(buf).Encode(c.raw); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func (c *Config) marshalCore() {
	s := c.raw.Section(coreSection)
	s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare))
}

func (c *Config) marshalRemotes() {
	s := c.raw.Section(remoteSection)
	s.Subsections = make(config.Subsections, len(c.Remotes))

	var i int
	for _, r := range c.Remotes {
		s.Subsections[i] = r.marshal()
		i++
	}
}

// RemoteConfig contains the configuration for a given repository
type RemoteConfig struct {
	Name  string
	URL   string
	Fetch []RefSpec

	raw *config.Subsection
}

// Validate validate the fields and set the default values
func (c *RemoteConfig) Validate() error {
	if c.Name == "" {
		return ErrRemoteConfigEmptyName
	}

	if c.URL == "" {
		return ErrRemoteConfigEmptyURL
	}

	if len(c.Fetch) == 0 {
		c.Fetch = []RefSpec{RefSpec(fmt.Sprintf(DefaultFetchRefSpec, c.Name))}
	}

	return nil
}

func (c *RemoteConfig) unmarshal(s *config.Subsection) {
	c.raw = s

	fetch := []RefSpec{}
	for _, f := range c.raw.Options.GetAll(fetchKey) {
		rs := RefSpec(f)
		if rs.IsValid() {
			fetch = append(fetch, rs)
		}
	}

	c.Name = c.raw.Name
	c.URL = c.raw.Option(urlKey)
	c.Fetch = fetch
}

func (c *RemoteConfig) marshal() *config.Subsection {
	if c.raw == nil {
		c.raw = &config.Subsection{}
	}

	c.raw.Name = c.Name
	c.raw.SetOption(urlKey, c.URL)
	for _, rs := range c.Fetch {
		c.raw.SetOption(fetchKey, rs.String())
	}

	return c.raw
}