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}