Skip to content
This repository was archived by the owner on Aug 24, 2024. It is now read-only.

Add migration3 #5

Merged
merged 9 commits into from
Apr 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ go 1.14
require (
github.com/99designs/keyring v1.1.5
github.com/blang/semver v3.5.1+incompatible
github.com/blevesearch/bleve v1.0.14
github.com/dustin/go-humanize v1.0.0
github.com/fatih/color v1.9.0
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.2.0
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.6.1
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57
golang.org/x/text v0.3.3
)
85 changes: 85 additions & 0 deletions go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions migration1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ git-bug v0.7.0
Changes:
- Added functionality to delete bugs
- Added functionality to set the author of a bug
- Exposed formatVersion of OperationPack

Usage: This version is used to migrate the legacyAuthor from older versions to the newer versions of git-bug.
4 changes: 3 additions & 1 deletion migration1/after/identity/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (

const formatVersion = 1

var ErrIncorrectIdentityFormatVersion = fmt.Errorf("unknown format version")

// Version is a complete set of information about an Identity at a point in time.
type Version struct {
// The lamport time at which this version become effective
Expand Down Expand Up @@ -102,7 +104,7 @@ func (v *Version) UnmarshalJSON(data []byte) error {
}

if aux.FormatVersion != formatVersion {
return fmt.Errorf("unknown format version %v", aux.FormatVersion)
return ErrIncorrectIdentityFormatVersion
}

v.time = aux.Time
Expand Down
17 changes: 12 additions & 5 deletions migration1/migration1.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package migration1
import (
"fmt"

"github.com/pkg/errors"

afterbug "github.com/MichaelMure/git-bug-migration/migration1/after/bug"
afteridentity "github.com/MichaelMure/git-bug-migration/migration1/after/identity"
afterrepo "github.com/MichaelMure/git-bug-migration/migration1/after/repository"
Expand All @@ -28,7 +30,7 @@ func (m *Migration1) Run(repoPath string) error {
func (m *Migration1) migrate(repo afterrepo.ClockedRepo) error {
err := m.readIdentities(repo)
if err != nil {
fmt.Printf("Error while applying migration")
fmt.Printf("Error while applying migration\n")
// stop the migration
return nil
}
Expand All @@ -37,7 +39,7 @@ func (m *Migration1) migrate(repo afterrepo.ClockedRepo) error {
for streamedBug := range afterbug.ReadAllLocal(repo) {
if streamedBug.Err != nil {
if streamedBug.Err != afterbug.ErrInvalidFormatVersion {
fmt.Printf("Got error when reading bug: %q\n", streamedBug.Err)
fmt.Printf("got error when reading bug, assuming data is already migrated: %q\n", streamedBug.Err)
} else {
fmt.Printf("skipping bug, already updated\n")
}
Expand Down Expand Up @@ -77,9 +79,14 @@ func (m *Migration1) migrate(repo afterrepo.ClockedRepo) error {

func (m *Migration1) readIdentities(repo afterrepo.ClockedRepo) error {
for streamedIdentity := range afteridentity.ReadAllLocal(repo) {
if streamedIdentity.Err != nil {
fmt.Printf("Got error when reading identity: %q", streamedIdentity.Err)
return streamedIdentity.Err
if err := streamedIdentity.Err; err != nil {
if errors.Is(err, afteridentity.ErrIncorrectIdentityFormatVersion) {
fmt.Print("skipping identity, already updated\n")
continue
} else {
fmt.Printf("Got error when reading identity: %q", streamedIdentity.Err)
return streamedIdentity.Err
}
}
m.allIdentities = append(m.allIdentities, streamedIdentity.Identity)
}
Expand Down
7 changes: 7 additions & 0 deletions migration3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### Vendor Information
git-bug 11f3991e2be17a5e0740c429f48961bfe468c23a

Changes:
- Exposed formatVersion of OperationPack
- Removed error wrapping for bug and identity decoding
- Added custom error message for invalid formatVersion
177 changes: 177 additions & 0 deletions migration3/after/bug/bug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Package bug contains the bug data model and low-level related functions
package bug

import (
"fmt"

"github.com/MichaelMure/git-bug-migration/migration3/after/entity"
"github.com/MichaelMure/git-bug-migration/migration3/after/entity/dag"
"github.com/MichaelMure/git-bug-migration/migration3/after/identity"
"github.com/MichaelMure/git-bug-migration/migration3/after/repository"
)

var _ Interface = &Bug{}
var _ entity.Interface = &Bug{}

// 1: original format
// 2: no more legacy identities
// 3: Ids are generated from the create operation serialized data instead of from the first git commit
// 4: with DAG entity framework
const formatVersion = 4

var def = dag.Definition{
Typename: "bug",
Namespace: "bugs",
OperationUnmarshaler: operationUnmarshaller,
FormatVersion: formatVersion,
}

var ClockLoader = dag.ClockLoader(def)

// Bug hold the data of a bug thread, organized in a way close to
// how it will be persisted inside Git. This is the data structure
// used to merge two different version of the same Bug.
type Bug struct {
*dag.Entity
}

// NewBug create a new Bug
func NewBug() *Bug {
return &Bug{
Entity: dag.New(def),
}
}

// Read will read a bug from a repository
func Read(repo repository.ClockedRepo, id entity.Id) (*Bug, error) {
e, err := dag.Read(def, repo, identity.NewSimpleResolver(repo), id)
if err != nil {
return nil, err
}
return &Bug{Entity: e}, nil
}

// ReadWithResolver will read a bug from its Id, with a custom identity.Resolver
func ReadWithResolver(repo repository.ClockedRepo, identityResolver identity.Resolver, id entity.Id) (*Bug, error) {
e, err := dag.Read(def, repo, identityResolver, id)
if err != nil {
return nil, err
}
return &Bug{Entity: e}, nil
}

type StreamedBug struct {
Bug *Bug
Err error
}

// ReadAll read and parse all local bugs
func ReadAll(repo repository.ClockedRepo) <-chan StreamedBug {
return readAll(repo, identity.NewSimpleResolver(repo))
}

// ReadAllWithResolver read and parse all local bugs
func ReadAllWithResolver(repo repository.ClockedRepo, identityResolver identity.Resolver) <-chan StreamedBug {
return readAll(repo, identityResolver)
}

// Read and parse all available bug with a given ref prefix
func readAll(repo repository.ClockedRepo, identityResolver identity.Resolver) <-chan StreamedBug {
out := make(chan StreamedBug)

go func() {
defer close(out)

for streamedEntity := range dag.ReadAll(def, repo, identityResolver) {
if streamedEntity.Err != nil {
out <- StreamedBug{
Err: streamedEntity.Err,
}
} else {
out <- StreamedBug{
Bug: &Bug{Entity: streamedEntity.Entity},
}
}
}
}()

return out
}

// ListLocalIds list all the available local bug ids
func ListLocalIds(repo repository.Repo) ([]entity.Id, error) {
return dag.ListLocalIds(def, repo)
}

// Validate check if the Bug data is valid
func (bug *Bug) Validate() error {
if err := bug.Entity.Validate(); err != nil {
return err
}

// The very first Op should be a CreateOp
firstOp := bug.FirstOp()
if firstOp == nil || firstOp.Type() != CreateOp {
return fmt.Errorf("first operation should be a Create op")
}

// Check that there is no more CreateOp op
for i, op := range bug.Operations() {
if i == 0 {
continue
}
if op.Type() == CreateOp {
return fmt.Errorf("only one Create op allowed")
}
}

return nil
}

// Append add a new Operation to the Bug
func (bug *Bug) Append(op Operation) {
bug.Entity.Append(op)
}

// Operations return the ordered operations
func (bug *Bug) Operations() []Operation {
source := bug.Entity.Operations()
result := make([]Operation, len(source))
for i, op := range source {
result[i] = op.(Operation)
}
return result
}

// Compile a bug in a easily usable snapshot
func (bug *Bug) Compile() Snapshot {
snap := Snapshot{
id: bug.Id(),
Status: OpenStatus,
}

for _, op := range bug.Operations() {
op.Apply(&snap)
snap.Operations = append(snap.Operations, op)
}

return snap
}

// Lookup for the very first operation of the bug.
// For a valid Bug, this operation should be a CreateOp
func (bug *Bug) FirstOp() Operation {
if fo := bug.Entity.FirstOp(); fo != nil {
return fo.(Operation)
}
return nil
}

// Lookup for the very last operation of the bug.
// For a valid Bug, should never be nil
func (bug *Bug) LastOp() Operation {
if lo := bug.Entity.LastOp(); lo != nil {
return lo.(Operation)
}
return nil
}
77 changes: 77 additions & 0 deletions migration3/after/bug/bug_actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package bug

import (
"github.com/pkg/errors"

"github.com/MichaelMure/git-bug-migration/migration3/after/entity"
"github.com/MichaelMure/git-bug-migration/migration3/after/entity/dag"
"github.com/MichaelMure/git-bug-migration/migration3/after/identity"
"github.com/MichaelMure/git-bug-migration/migration3/after/repository"
)

// Fetch retrieve updates from a remote
// This does not change the local bugs state
func Fetch(repo repository.Repo, remote string) (string, error) {
return dag.Fetch(def, repo, remote)
}

// Push update a remote with the local changes
func Push(repo repository.Repo, remote string) (string, error) {
return dag.Push(def, repo, remote)
}

// Pull will do a Fetch + MergeAll
// This function will return an error if a merge fail
func Pull(repo repository.ClockedRepo, remote string, author identity.Interface) error {
_, err := Fetch(repo, remote)
if err != nil {
return err
}

for merge := range MergeAll(repo, remote, author) {
if merge.Err != nil {
return merge.Err
}
if merge.Status == entity.MergeStatusInvalid {
return errors.Errorf("merge failure: %s", merge.Reason)
}
}

return nil
}

// MergeAll will merge all the available remote bug
// Note: an author is necessary for the case where a merge commit is created, as this commit will
// have an author and may be signed if a signing key is available.
func MergeAll(repo repository.ClockedRepo, remote string, author identity.Interface) <-chan entity.MergeResult {
// no caching for the merge, we load everything from git even if that means multiple
// copy of the same entity in memory. The cache layer will intercept the results to
// invalidate entities if necessary.
identityResolver := identity.NewSimpleResolver(repo)

out := make(chan entity.MergeResult)

go func() {
defer close(out)

results := dag.MergeAll(def, repo, identityResolver, remote, author)

// wrap the dag.Entity into a complete Bug
for result := range results {
result := result
if result.Entity != nil {
result.Entity = &Bug{
Entity: result.Entity.(*dag.Entity),
}
}
out <- result
}
}()

return out
}

// RemoveBug will remove a local bug from its entity.Id
func RemoveBug(repo repository.ClockedRepo, id entity.Id) error {
return dag.Remove(def, repo, id)
}
44 changes: 44 additions & 0 deletions migration3/after/bug/comment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package bug

import (
"github.com/dustin/go-humanize"

"github.com/MichaelMure/git-bug-migration/migration3/after/entity"
"github.com/MichaelMure/git-bug-migration/migration3/after/identity"
"github.com/MichaelMure/git-bug-migration/migration3/after/repository"
"github.com/MichaelMure/git-bug-migration/migration3/after/util/timestamp"
)

// Comment represent a comment in a Bug
type Comment struct {
id entity.Id
Author identity.Interface
Message string
Files []repository.Hash

// Creation time of the comment.
// Should be used only for human display, never for ordering as we can't rely on it in a distributed system.
UnixTime timestamp.Timestamp
}

// Id return the Comment identifier
func (c Comment) Id() entity.Id {
if c.id == "" {
// simply panic as it would be a coding error
// (using an id of an identity not stored yet)
panic("no id yet")
}
return c.id
}

// FormatTimeRel format the UnixTime of the comment for human consumption
func (c Comment) FormatTimeRel() string {
return humanize.Time(c.UnixTime.Time())
}

func (c Comment) FormatTime() string {
return c.UnixTime.Time().Format("Mon Jan 2 15:04:05 2006 +0200")
}

// Sign post method for gqlgen
func (c Comment) IsAuthored() {}
Loading