more refactoring to have reusable bug action across different UI

This commit is contained in:
Michael Muré 2018-07-25 18:01:32 +02:00
parent 49c90eab26
commit 6a12373965
No known key found for this signature in database
GPG Key ID: A4457C029293126F
16 changed files with 220 additions and 119 deletions

View File

@ -41,7 +41,7 @@ func NewBug() *Bug {
// Find an existing Bug matching a prefix
func FindLocalBug(repo repository.Repo, prefix string) (*Bug, error) {
ids, err := repo.ListRefs(bugsRefPattern)
ids, err := repo.ListIds(bugsRefPattern)
if err != nil {
return nil, err

View File

@ -3,13 +3,14 @@ package bug
import (
"fmt"
"github.com/MichaelMure/git-bug/repository"
"io"
"strings"
)
const MsgNew = "new"
const MsgInvalid = "invalid data"
const MsgUpdated = "updated"
const MsgNothing = "nothing to do"
const MsgMergeNew = "new"
const MsgMergeInvalid = "invalid data"
const MsgMergeUpdated = "updated"
const MsgMergeNothing = "nothing to do"
func Fetch(repo repository.Repo, remote string) error {
remoteRefSpec := fmt.Sprintf(bugsRemoteRefPattern, remote)
@ -22,6 +23,27 @@ func Push(repo repository.Repo, remote string) error {
return repo.PushRefs(remote, bugsRefPattern+"*")
}
func Pull(repo repository.Repo, out io.Writer, remote string) error {
fmt.Fprintf(out, "Fetching remote ...\n")
if err := Fetch(repo, remote); err != nil {
return err
}
fmt.Fprintf(out, "\nMerging data ...\n")
for merge := range MergeAll(repo, remote) {
if merge.Err != nil {
return merge.Err
}
if merge.Status != MsgMergeNothing {
fmt.Fprintf(out, "%s: %s\n", merge.HumanId, merge.Status)
}
}
return nil
}
type MergeResult struct {
Err error
@ -73,7 +95,7 @@ func MergeAll(repo repository.Repo, remote string) <-chan MergeResult {
// Check for error in remote data
if !remoteBug.IsValid() {
out <- newMergeStatus(id, MsgInvalid)
out <- newMergeStatus(id, MsgMergeInvalid)
continue
}
@ -89,7 +111,7 @@ func MergeAll(repo repository.Repo, remote string) <-chan MergeResult {
return
}
out <- newMergeStatus(id, MsgNew)
out <- newMergeStatus(id, MsgMergeNew)
continue
}
@ -108,12 +130,14 @@ func MergeAll(repo repository.Repo, remote string) <-chan MergeResult {
}
if updated {
out <- newMergeStatus(id, MsgUpdated)
out <- newMergeStatus(id, MsgMergeUpdated)
} else {
out <- newMergeStatus(id, MsgNothing)
out <- newMergeStatus(id, MsgMergeNothing)
}
}
}()
return out
}

View File

@ -31,3 +31,8 @@ func (op AddCommentOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
return snapshot
}
func Comment(b *bug.Bug, author bug.Person, message string) {
addCommentOp := NewAddCommentOp(author, message)
b.Append(addCommentOp)
}

View File

@ -33,3 +33,11 @@ func (op CreateOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
}
return snapshot
}
func Create(author bug.Person, title, message string) (*bug.Bug, error) {
newBug := bug.NewBug()
createOp := NewCreateOp(author, title, message)
newBug.Append(createOp)
return newBug, nil
}

View File

@ -1,7 +1,9 @@
package operations
import (
"fmt"
"github.com/MichaelMure/git-bug/bug"
"io"
"sort"
)
@ -54,3 +56,65 @@ AddLoop:
return snapshot
}
func ChangeLabels(out io.Writer, b *bug.Bug, author bug.Person, add, remove []string) error {
var added, removed []bug.Label
snap := b.Compile()
for _, str := range add {
label := bug.Label(str)
// check for duplicate
if labelExist(added, label) {
fmt.Fprintf(out, "label \"%s\" is a duplicate\n", str)
continue
}
// check that the label doesn't already exist
if labelExist(snap.Labels, label) {
fmt.Fprintf(out, "label \"%s\" is already set on this bug\n", str)
continue
}
added = append(added, label)
}
for _, str := range remove {
label := bug.Label(str)
// check for duplicate
if labelExist(removed, label) {
fmt.Fprintf(out, "label \"%s\" is a duplicate\n", str)
continue
}
// check that the label actually exist
if !labelExist(snap.Labels, label) {
fmt.Fprintf(out, "label \"%s\" doesn't exist on this bug\n", str)
continue
}
removed = append(removed, label)
}
if len(added) == 0 && len(removed) == 0 {
return fmt.Errorf("no label added or removed")
}
labelOp := NewLabelChangeOperation(author, added, removed)
b.Append(labelOp)
return nil
}
func labelExist(labels []bug.Label, label bug.Label) bool {
for _, l := range labels {
if l == label {
return true
}
}
return false
}

View File

@ -25,3 +25,13 @@ func (op SetStatusOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
return snapshot
}
func Open(b *bug.Bug, author bug.Person) {
op := NewSetStatusOp(author, bug.OpenStatus)
b.Append(op)
}
func Close(b *bug.Bug, author bug.Person) {
op := NewSetStatusOp(author, bug.ClosedStatus)
b.Append(op)
}

View File

@ -25,3 +25,8 @@ func (op SetTitleOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
return snapshot
}
func SetTitle(b *bug.Bug, author bug.Person, title string) {
setTitleOp := NewSetTitleOp(author, title)
b.Append(setTitleOp)
}

View File

@ -28,13 +28,9 @@ func runCloseBug(cmd *cobra.Command, args []string) error {
return err
}
op := operations.NewSetStatusOp(author, bug.ClosedStatus)
operations.Close(b, author)
b.Append(op)
err = b.Commit(repo)
return err
return b.Commit(repo)
}
var closeCmd = &cobra.Command{

View File

@ -49,13 +49,9 @@ func runComment(cmd *cobra.Command, args []string) error {
return err
}
addCommentOp := operations.NewAddCommentOp(author, commentMessage)
operations.Comment(b, author, commentMessage)
b.Append(addCommentOp)
err = b.Commit(repo)
return err
return b.Commit(repo)
}
var commentCmd = &cobra.Command{

View File

@ -2,10 +2,10 @@ package commands
import (
"errors"
"fmt"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/bug/operations"
"github.com/spf13/cobra"
"os"
)
var labelRemove bool
@ -21,6 +21,14 @@ func runLabel(cmd *cobra.Command, args []string) error {
prefix := args[0]
var add, remove []string
if labelRemove {
remove = args[1:]
} else {
add = args[1:]
}
b, err := bug.FindLocalBug(repo, prefix)
if err != nil {
return err
@ -31,65 +39,13 @@ func runLabel(cmd *cobra.Command, args []string) error {
return err
}
var added, removed []bug.Label
err = operations.ChangeLabels(os.Stdout, b, author, add, remove)
snap := b.Compile()
for _, arg := range args[1:] {
label := bug.Label(arg)
if labelRemove {
// check for duplicate
if labelExist(removed, label) {
fmt.Printf("label \"%s\" is a duplicate\n", arg)
continue
}
// check that the label actually exist
if !labelExist(snap.Labels, label) {
fmt.Printf("label \"%s\" doesn't exist on this bug\n", arg)
continue
}
removed = append(removed, label)
} else {
// check for duplicate
if labelExist(added, label) {
fmt.Printf("label \"%s\" is a duplicate\n", arg)
continue
}
// check that the label doesn't already exist
if labelExist(snap.Labels, label) {
fmt.Printf("label \"%s\" is already set on this bug\n", arg)
continue
}
added = append(added, label)
}
if err != nil {
return err
}
if len(added) == 0 && len(removed) == 0 {
return errors.New("no label added or removed")
}
labelOp := operations.NewLabelChangeOperation(author, added, removed)
b.Append(labelOp)
err = b.Commit(repo)
return err
}
func labelExist(labels []bug.Label, label bug.Label) bool {
for _, l := range labels {
if l == label {
return true
}
}
return false
return b.Commit(repo)
}
var labelCmd = &cobra.Command{

View File

@ -44,18 +44,20 @@ func runNewBug(cmd *cobra.Command, args []string) error {
return err
}
newBug := bug.NewBug()
createOp := operations.NewCreateOp(author, title, newMessage)
newBug.Append(createOp)
newBug, err := operations.Create(author, title, newMessage)
if err != nil {
return err
}
err = newBug.Commit(repo)
if err != nil {
return err
}
fmt.Printf("%s created\n", newBug.HumanId())
return err
return nil
}
var newCmd = &cobra.Command{

View File

@ -28,13 +28,9 @@ func runOpenBug(cmd *cobra.Command, args []string) error {
return err
}
op := operations.NewSetStatusOp(author, bug.OpenStatus)
operations.Open(b, author)
b.Append(op)
err = b.Commit(repo)
return err
return b.Commit(repo)
}
var openCmd = &cobra.Command{

View File

@ -2,9 +2,9 @@ package commands
import (
"errors"
"fmt"
"github.com/MichaelMure/git-bug/bug"
"github.com/spf13/cobra"
"os"
)
func runPull(cmd *cobra.Command, args []string) error {
@ -17,25 +17,7 @@ func runPull(cmd *cobra.Command, args []string) error {
remote = args[0]
}
fmt.Printf("Fetching remote ...\n\n")
if err := bug.Fetch(repo, remote); err != nil {
return err
}
fmt.Printf("\nMerging data ...\n\n")
for merge := range bug.MergeAll(repo, remote) {
if merge.Err != nil {
return merge.Err
}
if merge.Status != bug.MsgNothing {
fmt.Printf("%s: %s\n", merge.HumanId, merge.Status)
}
}
return nil
return bug.Pull(repo, os.Stdout, remote)
}
// showCmd defines the "push" subcommand.

View File

@ -3,7 +3,6 @@ package repository
import (
"bytes"
"crypto/sha1"
"fmt"
"github.com/MichaelMure/git-bug/util"
"io"
@ -19,7 +18,7 @@ type GitRepo struct {
// Run the given git command with the given I/O reader/writers, returning an error if it fails.
func (repo *GitRepo) runGitCommandWithIO(stdin io.Reader, stdout, stderr io.Writer, args ...string) error {
fmt.Println("Running git", strings.Join(args, " "))
//fmt.Println("Running git", strings.Join(args, " "))
cmd := exec.Command("git", args...)
cmd.Dir = repo.Path
@ -74,17 +73,29 @@ func NewGitRepo(path string) (*GitRepo, error) {
return nil, err
}
func InitGitRepo(path string) (*GitRepo, error) {
repo := &GitRepo{Path: path}
_, err := repo.runGitCommand("init", path)
if err != nil {
return nil, err
}
return repo, nil
}
func InitBareGitRepo(path string) (*GitRepo, error) {
repo := &GitRepo{Path: path}
_, err := repo.runGitCommand("init", "--bare", path)
if err != nil {
return nil, err
}
return repo, nil
}
// GetPath returns the path to the repo.
func (repo *GitRepo) GetPath() string {
return repo.Path
}
// GetRepoStateHash returns a hash which embodies the entire current state of a repository.
func (repo *GitRepo) GetRepoStateHash() (string, error) {
stateSummary, err := repo.runGitCommand("show-ref")
return fmt.Sprintf("%x", sha1.Sum([]byte(stateSummary))), err
}
// GetUserName returns the name the the user has used to configure git
func (repo *GitRepo) GetUserName() (string, error) {
return repo.runGitCommand("config", "user.name")
@ -189,6 +200,24 @@ func (repo *GitRepo) UpdateRef(ref string, hash util.Hash) error {
// ListRefs will return a list of Git ref matching the given refspec
func (repo *GitRepo) ListRefs(refspec string) ([]string, error) {
stdout, err := repo.runGitCommand("for-each-ref", "--format=%(refname)", refspec)
if err != nil {
return nil, err
}
splitted := strings.Split(stdout, "\n")
if len(splitted) == 1 && splitted[0] == "" {
return []string{}, nil
}
return splitted, nil
}
// ListIds will return a list of Git ref matching the given refspec,
// stripped to only the last part of the ref
func (repo *GitRepo) ListIds(refspec string) ([]string, error) {
// the format option will strip the ref name to keep only the last part (ie, the bug id)
stdout, err := repo.runGitCommand("for-each-ref", "--format=%(refname:lstrip=-1)", refspec)
@ -274,3 +303,10 @@ func (repo *GitRepo) GetTreeHash(commit util.Hash) (util.Hash, error) {
return util.Hash(stdout), nil
}
// Add a new remote to the repository
func (repo *GitRepo) AddRemote(name string, url string) error {
_, err := repo.runGitCommand("remote", "add", name, url)
return err
}

View File

@ -3,6 +3,8 @@ package repository
import (
"crypto/sha1"
"fmt"
"strings"
"github.com/MichaelMure/git-bug/util"
)
@ -134,6 +136,21 @@ func (r *mockRepoForTest) ListRefs(refspec string) ([]string, error) {
return keys, nil
}
// ListIds will return a list of Git ref matching the given refspec,
// stripped to only the last part of the ref
func (r *mockRepoForTest) ListIds(refspec string) ([]string, error) {
keys := make([]string, len(r.refs))
i := 0
for k := range r.refs {
splitted := strings.Split(k, "/")
keys[i] = splitted[len(splitted)-1]
i++
}
return keys, nil
}
func (r *mockRepoForTest) ListCommits(ref string) ([]util.Hash, error) {
var hashes []util.Hash

View File

@ -48,6 +48,10 @@ type Repo interface {
// ListRefs will return a list of Git ref matching the given refspec
ListRefs(refspec string) ([]string, error)
// ListIds will return a list of Git ref matching the given refspec,
// stripped to only the last part of the ref
ListIds(refspec string) ([]string, error)
// RefExist will check if a reference exist in Git
RefExist(ref string) (bool, error)