identity_actions.go

  1package identity
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"github.com/MichaelMure/git-bug/entity"
  8	"github.com/MichaelMure/git-bug/repository"
  9	"github.com/pkg/errors"
 10)
 11
 12// Fetch retrieve updates from a remote
 13// This does not change the local identities state
 14func Fetch(repo repository.Repo, remote string) (string, error) {
 15	remoteRefSpec := fmt.Sprintf(identityRemoteRefPattern, remote)
 16	fetchRefSpec := fmt.Sprintf("%s*:%s*", identityRefPattern, remoteRefSpec)
 17
 18	return repo.FetchRefs(remote, fetchRefSpec)
 19}
 20
 21// Push update a remote with the local changes
 22func Push(repo repository.Repo, remote string) (string, error) {
 23	return repo.PushRefs(remote, identityRefPattern+"*")
 24}
 25
 26// Pull will do a Fetch + MergeAll
 27// This function will return an error if a merge fail
 28func Pull(repo repository.ClockedRepo, remote string) error {
 29	_, err := Fetch(repo, remote)
 30	if err != nil {
 31		return err
 32	}
 33
 34	for merge := range MergeAll(repo, remote) {
 35		if merge.Err != nil {
 36			return merge.Err
 37		}
 38		if merge.Status == entity.MergeStatusInvalid {
 39			return errors.Errorf("merge failure: %s", merge.Reason)
 40		}
 41	}
 42
 43	return nil
 44}
 45
 46// MergeAll will merge all the available remote identity
 47func MergeAll(repo repository.ClockedRepo, remote string) <-chan entity.MergeResult {
 48	out := make(chan entity.MergeResult)
 49
 50	go func() {
 51		defer close(out)
 52
 53		remoteRefSpec := fmt.Sprintf(identityRemoteRefPattern, remote)
 54		remoteRefs, err := repo.ListRefs(remoteRefSpec)
 55
 56		if err != nil {
 57			out <- entity.MergeResult{Err: err}
 58			return
 59		}
 60
 61		for _, remoteRef := range remoteRefs {
 62			refSplitted := strings.Split(remoteRef, "/")
 63			id := refSplitted[len(refSplitted)-1]
 64
 65			remoteIdentity, err := read(repo, remoteRef)
 66
 67			if err != nil {
 68				out <- entity.NewMergeInvalidStatus(id, errors.Wrap(err, "remote identity is not readable").Error())
 69				continue
 70			}
 71
 72			// Check for error in remote data
 73			if err := remoteIdentity.Validate(); err != nil {
 74				out <- entity.NewMergeInvalidStatus(id, errors.Wrap(err, "remote identity is invalid").Error())
 75				continue
 76			}
 77
 78			localRef := identityRefPattern + remoteIdentity.Id().String()
 79			localExist, err := repo.RefExist(localRef)
 80
 81			if err != nil {
 82				out <- entity.NewMergeError(err, id)
 83				continue
 84			}
 85
 86			// the identity is not local yet, simply create the reference
 87			if !localExist {
 88				err := repo.CopyRef(remoteRef, localRef)
 89
 90				if err != nil {
 91					out <- entity.NewMergeError(err, id)
 92					return
 93				}
 94
 95				out <- entity.NewMergeStatus(entity.MergeStatusNew, id, remoteIdentity)
 96				continue
 97			}
 98
 99			localIdentity, err := read(repo, localRef)
100
101			if err != nil {
102				out <- entity.NewMergeError(errors.Wrap(err, "local identity is not readable"), id)
103				return
104			}
105
106			updated, err := localIdentity.Merge(repo, remoteIdentity)
107
108			if err != nil {
109				out <- entity.NewMergeInvalidStatus(id, errors.Wrap(err, "merge failed").Error())
110				return
111			}
112
113			if updated {
114				out <- entity.NewMergeStatus(entity.MergeStatusUpdated, id, localIdentity)
115			} else {
116				out <- entity.NewMergeStatus(entity.MergeStatusNothing, id, localIdentity)
117			}
118		}
119	}()
120
121	return out
122}