diff options
Diffstat (limited to 'entity/dag/entity_actions.go')
-rw-r--r-- | entity/dag/entity_actions.go | 87 |
1 files changed, 58 insertions, 29 deletions
diff --git a/entity/dag/entity_actions.go b/entity/dag/entity_actions.go index 8dcf91e6..83ff7ddc 100644 --- a/entity/dag/entity_actions.go +++ b/entity/dag/entity_actions.go @@ -9,6 +9,7 @@ import ( "github.com/MichaelMure/git-bug/repository" ) +// ListLocalIds list all the available local Entity's Id func ListLocalIds(typename string, repo repository.RepoData) ([]entity.Id, error) { refs, err := repo.ListRefs(fmt.Sprintf("refs/%s/", typename)) if err != nil { @@ -56,6 +57,21 @@ func Pull(def Definition, repo repository.ClockedRepo, remote string) error { return nil } +// MergeAll will merge all the available remote Entity: +// +// Multiple scenario exist: +// 1. if the remote Entity doesn't exist locally, it's created +// --> emit entity.MergeStatusNew +// 2. if the remote and local Entity have the same state, nothing is changed +// --> emit entity.MergeStatusNothing +// 3. if the local Entity has new commits but the remote don't, nothing is changed +// --> emit entity.MergeStatusNothing +// 4. if the remote has new commit, the local bug is updated to match the same history +// (fast-forward update) +// --> emit entity.MergeStatusUpdated +// 5. if both local and remote Entity have new commits (that is, we have a concurrent edition), +// a merge commit with an empty operationPack is created to join both branch and form a DAG. +// --> emit entity.MergeStatusUpdated func MergeAll(def Definition, repo repository.ClockedRepo, remote string) <-chan entity.MergeResult { out := make(chan entity.MergeResult) @@ -81,6 +97,8 @@ func MergeAll(def Definition, repo repository.ClockedRepo, remote string) <-chan return out } +// merge perform a merge to make sure a local Entity is up to date. +// See MergeAll for more details. func merge(def Definition, repo repository.ClockedRepo, remoteRef string) entity.MergeResult { id := entity.RefToId(remoteRef) @@ -102,36 +120,24 @@ func merge(def Definition, repo repository.ClockedRepo, remoteRef string) entity localRef := fmt.Sprintf("refs/%s/%s", def.namespace, id.String()) + // SCENARIO 1 + // if the remote Entity doesn't exist locally, it's created + localExist, err := repo.RefExist(localRef) if err != nil { return entity.NewMergeError(err, id) } - // the bug is not local yet, simply create the reference if !localExist { + // the bug is not local yet, simply create the reference err := repo.CopyRef(remoteRef, localRef) if err != nil { return entity.NewMergeError(err, id) } - return entity.NewMergeStatus(entity.MergeStatusNew, id, remoteEntity) + return entity.NewMergeNewStatus(id, remoteEntity) } - // var updated bool - // err = repo.MergeRef(localRef, remoteRef, func() repository.Hash { - // updated = true - // - // }) - // if err != nil { - // return entity.NewMergeError(err, id) - // } - // - // if updated { - // return entity.NewMergeStatus(entity.MergeStatusUpdated, id, ) - // } else { - // return entity.NewMergeStatus(entity.MergeStatusNothing, id, ) - // } - localCommit, err := repo.ResolveRef(localRef) if err != nil { return entity.NewMergeError(err, id) @@ -142,18 +148,38 @@ func merge(def Definition, repo repository.ClockedRepo, remoteRef string) entity return entity.NewMergeError(err, id) } + // SCENARIO 2 + // if the remote and local Entity have the same state, nothing is changed + if localCommit == remoteCommit { // nothing to merge - return entity.NewMergeStatus(entity.MergeStatusNothing, id, remoteEntity) + return entity.NewMergeNothingStatus(id) } - // fast-forward is possible if otherRef include ref + // SCENARIO 3 + // if the local Entity has new commits but the remote don't, nothing is changed + + localCommits, err := repo.ListCommits(localRef) + if err != nil { + return entity.NewMergeError(err, id) + } + + for _, hash := range localCommits { + if hash == localCommit { + return entity.NewMergeNothingStatus(id) + } + } + + // SCENARIO 4 + // if the remote has new commit, the local bug is updated to match the same history + // (fast-forward update) remoteCommits, err := repo.ListCommits(remoteRef) if err != nil { return entity.NewMergeError(err, id) } + // fast-forward is possible if otherRef include ref fastForwardPossible := false for _, hash := range remoteCommits { if hash == localCommit { @@ -167,9 +193,13 @@ func merge(def Definition, repo repository.ClockedRepo, remoteRef string) entity if err != nil { return entity.NewMergeError(err, id) } - return entity.NewMergeStatus(entity.MergeStatusUpdated, id, remoteEntity) + return entity.NewMergeUpdatedStatus(id, remoteEntity) } + // SCENARIO 5 + // if both local and remote Entity have new commits (that is, we have a concurrent edition), + // a merge commit with an empty operationPack is created to join both branch and form a DAG. + // fast-forward is not possible, we need to create a merge commit // For simplicity when reading and to have clocks that record this change, we store // an empty operationPack. @@ -180,6 +210,7 @@ func merge(def Definition, repo repository.ClockedRepo, remoteRef string) entity return entity.NewMergeError(err, id) } + // TODO: pack clock // err = localEntity.packClock.Witness(remoteEntity.packClock.Time()) // if err != nil { // return entity.NewMergeError(err, id) @@ -199,27 +230,25 @@ func merge(def Definition, repo repository.ClockedRepo, remoteRef string) entity Operations: nil, CreateTime: 0, EditTime: editTime, + // TODO: pack clock // PackTime: packTime, } - treeHash, err := opp.Write(def, repo) - if err != nil { - return entity.NewMergeError(err, id) - } - - // Create the merge commit with two parents - newHash, err := repo.StoreCommit(treeHash, localCommit, remoteCommit) + commitHash, err := opp.Write(def, repo, localCommit, remoteCommit) if err != nil { return entity.NewMergeError(err, id) } // finally update the ref - err = repo.UpdateRef(localRef, newHash) + err = repo.UpdateRef(localRef, commitHash) if err != nil { return entity.NewMergeError(err, id) } - return entity.NewMergeStatus(entity.MergeStatusUpdated, id, localEntity) + // Note: we don't need to update localEntity state (lastCommit, operations...) as we + // discard it entirely anyway. + + return entity.NewMergeUpdatedStatus(id, localEntity) } func Remove() error { |