github.com/inteleradmedicalsystems/go-codecommit@v0.1.0/pkg/codecommit/git.go (about)

     1  package codecommit
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  	"time"
     8  
     9  	log "github.com/sirupsen/logrus"
    10  	"gopkg.in/src-d/go-git.v4"
    11  	"gopkg.in/src-d/go-git.v4/plumbing"
    12  	"gopkg.in/src-d/go-git.v4/plumbing/object"
    13  	"gopkg.in/src-d/go-git.v4/plumbing/transport"
    14  )
    15  
    16  //RepoWrapper wraps basic go-git comands
    17  type RepoWrapper struct {
    18  }
    19  
    20  //Clone a Git repo, return true if the repo is up to date or was from an empty clone.
    21  func (r *RepoWrapper) Clone(cloneURL string, destDir string) (*git.Repository, bool, error) {
    22  	log.Debugf("Cloning Git repo %s, dest %s", cloneURL, destDir)
    23  
    24  	cloneOpts := &git.CloneOptions{
    25  		URL: cloneURL,
    26  	}
    27  
    28  	repo, err := git.PlainClone(destDir, false, cloneOpts)
    29  	if err != nil {
    30  		switch err {
    31  		case transport.ErrEmptyRemoteRepository:
    32  			return repo, true, nil
    33  		case git.NoErrAlreadyUpToDate:
    34  			return repo, true, nil
    35  		default:
    36  			return nil, false, err
    37  		}
    38  	}
    39  	return repo, false, nil
    40  }
    41  
    42  //Pull a Git repo from path
    43  func (r *RepoWrapper) Pull(path string) error {
    44  	repo, err := r.repo(path)
    45  	if err != nil {
    46  		return err
    47  	}
    48  	return r.PullR(repo)
    49  }
    50  
    51  //PullR a Git repo.
    52  func (r *RepoWrapper) PullR(repo *git.Repository) error {
    53  	w, err := repo.Worktree()
    54  	if err != nil {
    55  		return err
    56  	}
    57  	err = w.Pull(&git.PullOptions{RemoteName: "origin"})
    58  	switch err {
    59  	case transport.ErrEmptyRemoteRepository:
    60  		log.Warnf("Warning: %s", err)
    61  		return nil
    62  	case git.NoErrAlreadyUpToDate:
    63  		return nil
    64  	default:
    65  		return err
    66  	}
    67  }
    68  
    69  //Push a Git repo from path
    70  func (r *RepoWrapper) Push(path string) error {
    71  	repo, err := r.repo(path)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	return r.PushR(repo)
    76  }
    77  
    78  //PushR a Git repo.
    79  func (r *RepoWrapper) PushR(repo *git.Repository) error {
    80  	err := repo.Push(&git.PushOptions{})
    81  	switch err {
    82  	case git.NoErrAlreadyUpToDate:
    83  		log.Warnf("Warning: %s", err)
    84  		return nil
    85  	default:
    86  		return err
    87  	}
    88  }
    89  
    90  //Commit to a Git repo
    91  func (r *RepoWrapper) Commit(w *git.Worktree, name, email, message string, force bool) (*plumbing.Hash, error) {
    92  	status, err := w.Status()
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	if !status.IsClean() || force {
    98  		log.Info(status.String())
    99  		commit, err := w.Commit(message, &git.CommitOptions{
   100  			Author: &object.Signature{
   101  				Name:  name,
   102  				Email: email,
   103  				When:  time.Now(),
   104  			},
   105  		})
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		log.Infof("Committed %s", commit)
   110  		return &commit, nil
   111  	}
   112  	log.Infof("no changes to commit")
   113  	return nil, nil
   114  }
   115  
   116  //GetDestPath returns
   117  //get the dest from either last element of args or
   118  //the basename of the url (with the .git suffix stripped).
   119  func (r *RepoWrapper) GetDestPath(path string) string {
   120  	return strings.Replace(filepath.Base(path), ".git", "", -1)
   121  }
   122  
   123  func (r *RepoWrapper) manifestMap(destPrefix string, repo *git.Repository) (map[string]*object.File, error) {
   124  	m := make(map[string]*object.File)
   125  	ref, err := repo.Head()
   126  	if err != nil {
   127  		if err != plumbing.ErrReferenceNotFound {
   128  			return m, err
   129  		}
   130  	} else {
   131  		c, err := repo.CommitObject(ref.Hash())
   132  		if err != nil {
   133  			return m, err
   134  		}
   135  		fIter, err := c.Files()
   136  		if err != nil {
   137  			return m, err
   138  		}
   139  		err = fIter.ForEach(func(f *object.File) error {
   140  			log.Debugf("repoFile:%s", f.Name)
   141  			if strings.HasPrefix(f.Name, destPrefix) {
   142  				m[f.Name] = f
   143  			}
   144  			return nil
   145  		})
   146  		if err != nil {
   147  			return m, err
   148  		}
   149  	}
   150  	return m, nil
   151  }
   152  
   153  func (r *RepoWrapper) addRemove(destPrefix string, boundary string, repo *git.Repository, fm map[string]os.FileInfo) (*git.Worktree, error) {
   154  	w, err := repo.Worktree()
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	mm, err := r.manifestMap(destPrefix, repo)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	log.Debugf("%v", mm)
   164  
   165  	for fn := range mm {
   166  		target := fn[len(destPrefix)+1:]
   167  		_, ok := fm[target]
   168  		log.Debugf("Target %v, fn %v, destPrefix %v, boundary %v", target, fn, destPrefix, boundary)
   169  		if !ok && strings.HasPrefix(target, boundary) {
   170  			log.Infof("Removing %v", fn)
   171  			w.Remove(fn)
   172  		}
   173  	}
   174  
   175  	for fn, f := range fm {
   176  		if f.Mode().IsRegular() {
   177  			target := filepath.Join(destPrefix, fn)
   178  			_, ok := mm[target]
   179  			if !ok {
   180  				// Only log file addition for files that are not in the Git cache.
   181  				log.Infof("Adding %v", target)
   182  			}
   183  			// Always call Add()
   184  			w.Add(target)
   185  		}
   186  	}
   187  	return w, nil
   188  }
   189  
   190  func (r *RepoWrapper) repo(path string) (*git.Repository, error) {
   191  	return git.PlainOpen(path)
   192  }