aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2020-07-28 14:41:35 +0200
committerGitHub <noreply@github.com>2020-07-28 14:41:35 +0200
commit51c887ff9dd4f2a5f65d2d3de57f8a7729b1010d (patch)
tree434b75cbe63543ba5f609dadfb0e891765d13ea8
parent0590de9f045c2090d72655a7c2e1d41be9b9104c (diff)
parenta62ce78c4fc4e541cecaab0ab22def8752d3d9e2 (diff)
downloadgit-bug-51c887ff9dd4f2a5f65d2d3de57f8a7729b1010d.tar.gz
Merge pull request #433 from MichaelMure/remove-bug-2
Add the 'rm' command
-rw-r--r--bug/bug.go63
-rw-r--r--bug/bug_test.go75
-rw-r--r--cache/repo_cache_bug.go16
-rw-r--r--cache/repo_cache_test.go53
-rw-r--r--commands/rm.go43
-rw-r--r--commands/root.go1
6 files changed, 233 insertions, 18 deletions
diff --git a/bug/bug.go b/bug/bug.go
index 04bd5996..2ee89031 100644
--- a/bug/bug.go
+++ b/bug/bug.go
@@ -109,8 +109,8 @@ func ReadLocalBug(repo repository.ClockedRepo, id entity.Id) (*Bug, error) {
}
// ReadRemoteBug will read a remote bug from its hash
-func ReadRemoteBug(repo repository.ClockedRepo, remote string, id string) (*Bug, error) {
- ref := fmt.Sprintf(bugsRemoteRefPattern, remote) + id
+func ReadRemoteBug(repo repository.ClockedRepo, remote string, id entity.Id) (*Bug, error) {
+ ref := fmt.Sprintf(bugsRemoteRefPattern, remote) + id.String()
return readBug(repo, ref)
}
@@ -242,9 +242,54 @@ func readBug(repo repository.ClockedRepo, ref string) (*Bug, error) {
return &bug, nil
}
-func RemoveLocalBug(repo repository.ClockedRepo, id entity.Id) error {
- ref := bugsRefPattern + id.String()
- return repo.RemoveRef(ref)
+// RemoveBug will remove a local bug from its entity.Id
+func RemoveBug(repo repository.ClockedRepo, id entity.Id) error {
+ var fullMatches []string
+
+ refs, err := repo.ListRefs(bugsRefPattern + id.String())
+ if err != nil {
+ return err
+ }
+ if len(refs) > 1 {
+ return NewErrMultipleMatchBug(refsToIds(refs))
+ }
+ if len(refs) == 1 {
+ // we have the bug locally
+ fullMatches = append(fullMatches, refs[0])
+ }
+
+ remotes, err := repo.GetRemotes()
+ if err != nil {
+ return err
+ }
+
+ for remote := range remotes {
+ remotePrefix := fmt.Sprintf(bugsRemoteRefPattern+id.String(), remote)
+ remoteRefs, err := repo.ListRefs(remotePrefix)
+ if err != nil {
+ return err
+ }
+ if len(remoteRefs) > 1 {
+ return NewErrMultipleMatchBug(refsToIds(refs))
+ }
+ if len(remoteRefs) == 1 {
+ // found the bug in a remote
+ fullMatches = append(fullMatches, remoteRefs[0])
+ }
+ }
+
+ if len(fullMatches) == 0 {
+ return ErrBugNotExist
+ }
+
+ for _, ref := range fullMatches {
+ err = repo.RemoveRef(ref)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
}
type StreamedBug struct {
@@ -305,13 +350,17 @@ func refsToIds(refs []string) []entity.Id {
ids := make([]entity.Id, len(refs))
for i, ref := range refs {
- split := strings.Split(ref, "/")
- ids[i] = entity.Id(split[len(split)-1])
+ ids[i] = refToId(ref)
}
return ids
}
+func refToId(ref string) entity.Id {
+ split := strings.Split(ref, "/")
+ return entity.Id(split[len(split)-1])
+}
+
// Validate check if the Bug data is valid
func (bug *Bug) Validate() error {
// non-empty
diff --git a/bug/bug_test.go b/bug/bug_test.go
index 43e760af..400e50f8 100644
--- a/bug/bug_test.go
+++ b/bug/bug_test.go
@@ -1,10 +1,12 @@
package bug
import (
+ "fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
@@ -64,7 +66,7 @@ func TestBugValidity(t *testing.T) {
}
}
-func TestBugCommitLoadRemove(t *testing.T) {
+func TestBugCommitLoad(t *testing.T) {
bug1 := NewBug()
rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
@@ -100,16 +102,6 @@ func TestBugCommitLoadRemove(t *testing.T) {
bug3, err := ReadLocalBug(repo, bug1.Id())
assert.NoError(t, err)
equivalentBug(t, bug1, bug3)
-
- err = RemoveLocalBug(repo, bug1.Id())
- assert.NoError(t, err)
-
- streamedBugs := ReadAllLocalBugs(repo)
- count := 0
- for range streamedBugs {
- count++
- }
- assert.Equal(t, 0, count)
}
func equivalentBug(t *testing.T, expected, actual *Bug) {
@@ -123,3 +115,64 @@ func equivalentBug(t *testing.T, expected, actual *Bug) {
assert.Equal(t, expected, actual)
}
+
+func TestBugRemove(t *testing.T) {
+ repo := repository.CreateTestRepo(false)
+ remoteA := repository.CreateTestRepo(true)
+ remoteB := repository.CreateTestRepo(true)
+ defer repository.CleanupTestRepos(repo, remoteA, remoteB)
+
+ err := repo.AddRemote("remoteA", "file://"+remoteA.GetPath())
+ require.NoError(t, err)
+
+ err = repo.AddRemote("remoteB", "file://"+remoteB.GetPath())
+ require.NoError(t, err)
+
+ // generate a bunch of bugs
+ rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
+ err = rene.Commit(repo)
+ require.NoError(t, err)
+
+ for i := 0; i < 100; i++ {
+ b := NewBug()
+ createOp := NewCreateOp(rene, time.Now().Unix(), "title", fmt.Sprintf("message%v", i), nil)
+ b.Append(createOp)
+ err = b.Commit(repo)
+ require.NoError(t, err)
+ }
+
+ // and one more for testing
+ b := NewBug()
+ createOp := NewCreateOp(rene, time.Now().Unix(), "title", "message", nil)
+ b.Append(createOp)
+ err = b.Commit(repo)
+ require.NoError(t, err)
+
+ _, err = Push(repo, "remoteA")
+ require.NoError(t, err)
+
+ _, err = Push(repo, "remoteB")
+ require.NoError(t, err)
+
+ _, err = Fetch(repo, "remoteA")
+ require.NoError(t, err)
+
+ _, err = Fetch(repo, "remoteB")
+ require.NoError(t, err)
+
+ err = RemoveBug(repo, b.Id())
+ require.NoError(t, err)
+
+ _, err = ReadLocalBug(repo, b.Id())
+ require.Error(t, ErrBugNotExist, err)
+
+ _, err = ReadRemoteBug(repo, "remoteA", b.Id())
+ require.Error(t, ErrBugNotExist, err)
+
+ _, err = ReadRemoteBug(repo, "remoteB", b.Id())
+ require.Error(t, ErrBugNotExist, err)
+
+ ids, err := ListLocalIds(repo)
+ require.NoError(t, err)
+ require.Len(t, ids, 100)
+}
diff --git a/cache/repo_cache_bug.go b/cache/repo_cache_bug.go
index 30692363..bcbfcea3 100644
--- a/cache/repo_cache_bug.go
+++ b/cache/repo_cache_bug.go
@@ -359,3 +359,19 @@ func (c *RepoCache) NewBugRaw(author *IdentityCache, unixTime int64, title strin
return cached, op, nil
}
+
+// RemoveBug removes a bug from the cache and repo given a bug id prefix
+func (c *RepoCache) RemoveBug(prefix string) error {
+ b, err := c.ResolveBugPrefix(prefix)
+
+ if err != nil {
+ return err
+ }
+
+ err = bug.RemoveBug(c.repo, b.Id())
+
+ delete(c.bugs, b.Id())
+ delete(c.bugExcerpts, b.Id())
+
+ return c.writeBugCache()
+}
diff --git a/cache/repo_cache_test.go b/cache/repo_cache_test.go
index 0a333c8f..0deb155e 100644
--- a/cache/repo_cache_test.go
+++ b/cache/repo_cache_test.go
@@ -1,10 +1,14 @@
package cache
import (
+ "fmt"
"testing"
+ "time"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/query"
"github.com/MichaelMure/git-bug/repository"
)
@@ -157,3 +161,52 @@ func TestPushPull(t *testing.T) {
require.Len(t, cacheA.AllBugsIds(), 2)
}
+
+func TestRemove(t *testing.T) {
+ repo := repository.CreateTestRepo(false)
+ remoteA := repository.CreateTestRepo(true)
+ remoteB := repository.CreateTestRepo(true)
+ defer repository.CleanupTestRepos(repo, remoteA, remoteB)
+
+ err := repo.AddRemote("remoteA", "file://"+remoteA.GetPath())
+ require.NoError(t, err)
+
+ err = repo.AddRemote("remoteB", "file://"+remoteB.GetPath())
+ require.NoError(t, err)
+
+ repoCache, err := NewRepoCache(repo)
+ require.NoError(t, err)
+
+ // generate a bunch of bugs
+ rene, err := repoCache.NewIdentity("René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+
+ for i := 0; i < 100; i++ {
+ _, _, err := repoCache.NewBugRaw(rene, time.Now().Unix(), "title", fmt.Sprintf("message%v", i), nil, nil)
+ require.NoError(t, err)
+ }
+
+ // and one more for testing
+ b1, _, err := repoCache.NewBugRaw(rene, time.Now().Unix(), "title", "message", nil, nil)
+ require.NoError(t, err)
+
+ _, err = repoCache.Push("remoteA")
+ require.NoError(t, err)
+
+ _, err = repoCache.Push("remoteB")
+ require.NoError(t, err)
+
+ _, err = repoCache.Fetch("remoteA")
+ require.NoError(t, err)
+
+ _, err = repoCache.Fetch("remoteB")
+ require.NoError(t, err)
+
+ err = repoCache.RemoveBug(b1.Id().String())
+ require.NoError(t, err)
+ assert.Equal(t, 100, len(repoCache.bugs))
+ assert.Equal(t, 100, len(repoCache.bugExcerpts))
+
+ _, err = repoCache.ResolveBug(b1.Id())
+ assert.Error(t, bug.ErrBugNotExist, err)
+}
diff --git a/commands/rm.go b/commands/rm.go
new file mode 100644
index 00000000..09f6a6cc
--- /dev/null
+++ b/commands/rm.go
@@ -0,0 +1,43 @@
+package commands
+
+import (
+ "errors"
+
+ "github.com/spf13/cobra"
+)
+
+func newRmCommand() *cobra.Command {
+ env := newEnv()
+
+ cmd := &cobra.Command{
+ Use: "rm <id>",
+ Short: "Remove an existing bug.",
+ Long: "Remove an existing bug in the local repository. Note removing bugs that were imported from bridges will not remove the bug on the remote, and will only remove the local copy of the bug.",
+ PreRunE: loadBackendEnsureUser(env),
+ PostRunE: closeBackend(env),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return runRm(env, args)
+ },
+ }
+
+ flags := cmd.Flags()
+ flags.SortFlags = false
+
+ return cmd
+}
+
+func runRm(env *Env, args []string) (err error) {
+ if len(args) == 0 {
+ return errors.New("you must provide a bug prefix to remove")
+ }
+
+ err = env.backend.RemoveBug(args[0])
+
+ if err != nil {
+ return
+ }
+
+ env.out.Printf("bug %s removed\n", args[0])
+
+ return
+}
diff --git a/commands/root.go b/commands/root.go
index a67fec1a..e7848363 100644
--- a/commands/root.go
+++ b/commands/root.go
@@ -71,6 +71,7 @@ _git_bug() {
cmd.AddCommand(newLsLabelCommand())
cmd.AddCommand(newPullCommand())
cmd.AddCommand(newPushCommand())
+ cmd.AddCommand(newRmCommand())
cmd.AddCommand(newSelectCommand())
cmd.AddCommand(newShowCommand())
cmd.AddCommand(newStatusCommand())