aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKalin Staykov <k.t.staykov@gmail.com>2022-11-26 15:49:59 +0200
committerMichael Muré <batolettre@gmail.com>2023-01-11 14:58:58 +0100
commitfc266b733cf0ea794209031e3500ab3f86db6cee (patch)
treebae1996ec19fb74446429200e52cdb942f60ac54
parent9c50a359704f4edd2f33df6d256e032feae3a576 (diff)
downloadgit-bug-fc266b733cf0ea794209031e3500ab3f86db6cee.tar.gz
add wipe sub-command that remove local bugs and identities
-rw-r--r--cache/bug_subcache.go1
-rw-r--r--cache/identity_subcache.go3
-rw-r--r--cache/repo_cache.go1
-rw-r--r--cache/repo_cache_common.go26
-rw-r--r--cache/repo_cache_test.go33
-rw-r--r--cache/subcache.go43
-rw-r--r--commands/root.go1
-rw-r--r--commands/wipe.go58
-rw-r--r--doc/man/git-bug-wipe.127
-rw-r--r--doc/man/git-bug.12
-rw-r--r--doc/md/git-bug.md1
-rw-r--r--doc/md/git-bug_wipe.md18
-rw-r--r--entities/bug/bug_actions.go6
-rw-r--r--entities/identity/identity.go50
-rw-r--r--entities/identity/identity_actions.go71
-rw-r--r--entities/identity/identity_test.go2
-rw-r--r--entities/identity/identity_user.go4
-rw-r--r--entity/dag/entity_actions.go16
-rw-r--r--entity/dag/entity_actions_test.go31
-rw-r--r--repository/gogit.go11
-rw-r--r--repository/index_bleve.go7
-rw-r--r--repository/localstorage_billy.go16
-rw-r--r--repository/mock_repo.go12
-rw-r--r--repository/repo.go10
-rw-r--r--repository/repo_testing.go34
25 files changed, 416 insertions, 68 deletions
diff --git a/cache/bug_subcache.go b/cache/bug_subcache.go
index 920fe1dc..21c9a6d2 100644
--- a/cache/bug_subcache.go
+++ b/cache/bug_subcache.go
@@ -38,6 +38,7 @@ func NewRepoCacheBug(repo repository.ClockedRepo,
ReadWithResolver: bug.ReadWithResolver,
ReadAllWithResolver: bug.ReadAllWithResolver,
Remove: bug.Remove,
+ RemoveAll: bug.RemoveAll,
MergeAll: bug.MergeAll,
}
diff --git a/cache/identity_subcache.go b/cache/identity_subcache.go
index f862ca8b..05a91358 100644
--- a/cache/identity_subcache.go
+++ b/cache/identity_subcache.go
@@ -39,7 +39,8 @@ func NewRepoCacheIdentity(repo repository.ClockedRepo,
ReadAllWithResolver: func(repo repository.ClockedRepo, resolvers entity.Resolvers) <-chan entity.StreamedEntity[*identity.Identity] {
return identity.ReadAllLocal(repo)
},
- Remove: identity.RemoveIdentity,
+ Remove: identity.Remove,
+ RemoveAll: identity.RemoveAll,
MergeAll: func(repo repository.ClockedRepo, resolvers entity.Resolvers, remote string, mergeAuthor identity.Interface) <-chan entity.MergeResult {
return identity.MergeAll(repo, remote)
},
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index 99e9abbd..2121d1e4 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -32,6 +32,7 @@ type cacheMgmt interface {
Load() error
Build() error
SetCacheSize(size int)
+ RemoveAll() error
MergeAll(remote string) <-chan entity.MergeResult
GetNamespace() string
Close() error
diff --git a/cache/repo_cache_common.go b/cache/repo_cache_common.go
index f768b8e2..759536bd 100644
--- a/cache/repo_cache_common.go
+++ b/cache/repo_cache_common.go
@@ -3,12 +3,12 @@ package cache
import (
"sync"
- "github.com/go-git/go-billy/v5"
"github.com/pkg/errors"
"github.com/MichaelMure/git-bug/entities/identity"
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
+ "github.com/MichaelMure/git-bug/util/multierr"
)
func (c *RepoCache) Name() string {
@@ -56,7 +56,7 @@ func (c *RepoCache) GetRemotes() (map[string]string, error) {
}
// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
-func (c *RepoCache) LocalStorage() billy.Filesystem {
+func (c *RepoCache) LocalStorage() repository.LocalStorage {
return c.repo.LocalStorage()
}
@@ -82,6 +82,15 @@ func (c *RepoCache) Fetch(remote string) (string, error) {
return c.repo.FetchRefs(remote, prefixes...)
}
+// RemoveAll deletes all entities from the cache and the disk.
+func (c *RepoCache) RemoveAll() error {
+ var errWait multierr.ErrWaitGroup
+ for _, mgmt := range c.subcaches {
+ errWait.Go(mgmt.RemoveAll)
+ }
+ return errWait.Wait()
+}
+
// MergeAll will merge all the available remote bug and identities
func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
out := make(chan entity.MergeResult)
@@ -163,6 +172,19 @@ func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
return nil
}
+func (c *RepoCache) ClearUserIdentity() error {
+ c.muUserIdentity.Lock()
+ defer c.muUserIdentity.Unlock()
+
+ err := identity.ClearUserIdentity(c.repo)
+ if err != nil {
+ return err
+ }
+
+ c.userIdentityId = ""
+ return nil
+}
+
func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
c.muUserIdentity.RLock()
if c.userIdentityId != "" {
diff --git a/cache/repo_cache_test.go b/cache/repo_cache_test.go
index 07a3fee8..3c11220d 100644
--- a/cache/repo_cache_test.go
+++ b/cache/repo_cache_test.go
@@ -135,6 +135,39 @@ func TestCache(t *testing.T) {
_, err = cache.Bugs().ResolvePrefix(bug1.Id().String()[:10])
require.NoError(t, err)
+ require.Len(t, cache.bugs.cached, 1)
+ require.Len(t, cache.bugs.excerpts, 2)
+ require.Len(t, cache.identities.cached, 1)
+ require.Len(t, cache.identities.excerpts, 2)
+ require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(2), indexCount(t, bug.Namespace))
+
+ // Remove + RemoveAll
+ err = cache.Identities().Remove(iden1.Id().String()[:10])
+ require.NoError(t, err)
+ err = cache.Bugs().Remove(bug1.Id().String()[:10])
+ require.NoError(t, err)
+ require.Len(t, cache.bugs.cached, 0)
+ require.Len(t, cache.bugs.excerpts, 1)
+ require.Len(t, cache.identities.cached, 0)
+ require.Len(t, cache.identities.excerpts, 1)
+ require.Equal(t, uint64(1), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(1), indexCount(t, bug.Namespace))
+
+ _, err = cache.Identities().New("René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ _, _, err = cache.Bugs().NewRaw(iden2, time.Now().Unix(), "title", "message", nil, nil)
+ require.NoError(t, err)
+
+ err = cache.RemoveAll()
+ require.NoError(t, err)
+ require.Len(t, cache.bugs.cached, 0)
+ require.Len(t, cache.bugs.excerpts, 0)
+ require.Len(t, cache.identities.cached, 0)
+ require.Len(t, cache.identities.excerpts, 0)
+ require.Equal(t, uint64(0), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(0), indexCount(t, bug.Namespace))
+
// Close
require.NoError(t, cache.Close())
require.Empty(t, cache.bugs.cached)
diff --git a/cache/subcache.go b/cache/subcache.go
index 7b599b10..aa9ae62d 100644
--- a/cache/subcache.go
+++ b/cache/subcache.go
@@ -34,6 +34,7 @@ type Actions[EntityT entity.Interface] struct {
ReadWithResolver func(repo repository.ClockedRepo, resolvers entity.Resolvers, id entity.Id) (EntityT, error)
ReadAllWithResolver func(repo repository.ClockedRepo, resolvers entity.Resolvers) <-chan entity.StreamedEntity[EntityT]
Remove func(repo repository.ClockedRepo, id entity.Id) error
+ RemoveAll func(repo repository.ClockedRepo) error
MergeAll func(repo repository.ClockedRepo, resolvers entity.Resolvers, remote string, mergeAuthor identity.Interface) <-chan entity.MergeResult
}
@@ -399,7 +400,49 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) Remove(prefix string) error {
delete(sc.excerpts, e.Id())
sc.lru.Remove(e.Id())
+ index, err := sc.repo.GetIndex(sc.namespace)
+ if err != nil {
+ sc.mu.Unlock()
+ return err
+ }
+
+ err = index.Remove(e.Id().String())
+ sc.mu.Unlock()
+ if err != nil {
+ return err
+ }
+
+ return sc.write()
+}
+
+func (sc *SubCache[EntityT, ExcerptT, CacheT]) RemoveAll() error {
+ sc.mu.Lock()
+
+ err := sc.actions.RemoveAll(sc.repo)
+ if err != nil {
+ sc.mu.Unlock()
+ return err
+ }
+
+ for id, _ := range sc.cached {
+ delete(sc.cached, id)
+ sc.lru.Remove(id)
+ }
+ for id, _ := range sc.excerpts {
+ delete(sc.excerpts, id)
+ }
+
+ index, err := sc.repo.GetIndex(sc.namespace)
+ if err != nil {
+ sc.mu.Unlock()
+ return err
+ }
+
+ err = index.Clear()
sc.mu.Unlock()
+ if err != nil {
+ return err
+ }
return sc.write()
}
diff --git a/commands/root.go b/commands/root.go
index cb4fd686..0c854739 100644
--- a/commands/root.go
+++ b/commands/root.go
@@ -81,6 +81,7 @@ the same git remote you are already using to collaborate with other people.
cmd.AddCommand(newCommandsCommand())
cmd.AddCommand(newVersionCommand())
+ cmd.AddCommand(newWipeCommand())
return cmd
}
diff --git a/commands/wipe.go b/commands/wipe.go
new file mode 100644
index 00000000..0ec53f49
--- /dev/null
+++ b/commands/wipe.go
@@ -0,0 +1,58 @@
+package commands
+
+import (
+ "github.com/spf13/cobra"
+
+ "github.com/MichaelMure/git-bug/commands/execenv"
+)
+
+func newWipeCommand() *cobra.Command {
+ env := execenv.NewEnv()
+
+ cmd := &cobra.Command{
+ Use: "wipe",
+ Short: "Wipe git-bug from the git repository",
+ PreRunE: execenv.LoadBackend(env),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return runWipe(env)
+ },
+ }
+
+ return cmd
+}
+
+func runWipe(env *execenv.Env) error {
+ env.Out.Println("cleaning entities...")
+ err := env.Backend.RemoveAll()
+ if err != nil {
+ _ = env.Backend.Close()
+ return err
+ }
+
+ env.Out.Println("cleaning git config ...")
+ err = env.Backend.ClearUserIdentity()
+ if err != nil {
+ _ = env.Backend.Close()
+ return err
+ }
+ err = env.Backend.LocalConfig().RemoveAll("git-bug")
+ if err != nil {
+ _ = env.Backend.Close()
+ return err
+ }
+
+ storage := env.Backend.LocalStorage()
+
+ err = env.Backend.Close()
+ if err != nil {
+ return err
+ }
+
+ env.Out.Println("cleaning caches ...")
+ err = storage.RemoveAll(".")
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/doc/man/git-bug-wipe.1 b/doc/man/git-bug-wipe.1
new file mode 100644
index 00000000..fc3178f6
--- /dev/null
+++ b/doc/man/git-bug-wipe.1
@@ -0,0 +1,27 @@
+.nh
+.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" ""
+
+.SH NAME
+.PP
+git-bug-wipe - Wipe git-bug from the git repository
+
+
+.SH SYNOPSIS
+.PP
+\fBgit-bug wipe [flags]\fP
+
+
+.SH DESCRIPTION
+.PP
+Wipe git-bug from the git repository
+
+
+.SH OPTIONS
+.PP
+\fB-h\fP, \fB--help\fP[=false]
+ help for wipe
+
+
+.SH SEE ALSO
+.PP
+\fBgit-bug(1)\fP
diff --git a/doc/man/git-bug.1 b/doc/man/git-bug.1
index 8b66d312..9bf59164 100644
--- a/doc/man/git-bug.1
+++ b/doc/man/git-bug.1
@@ -29,4 +29,4 @@ the same git remote you are already using to collaborate with other people.
.SH SEE ALSO
.PP
-\fBgit-bug-bridge(1)\fP, \fBgit-bug-bug(1)\fP, \fBgit-bug-commands(1)\fP, \fBgit-bug-label(1)\fP, \fBgit-bug-pull(1)\fP, \fBgit-bug-push(1)\fP, \fBgit-bug-termui(1)\fP, \fBgit-bug-user(1)\fP, \fBgit-bug-version(1)\fP, \fBgit-bug-webui(1)\fP
+\fBgit-bug-bridge(1)\fP, \fBgit-bug-bug(1)\fP, \fBgit-bug-commands(1)\fP, \fBgit-bug-label(1)\fP, \fBgit-bug-pull(1)\fP, \fBgit-bug-push(1)\fP, \fBgit-bug-termui(1)\fP, \fBgit-bug-user(1)\fP, \fBgit-bug-version(1)\fP, \fBgit-bug-webui(1)\fP, \fBgit-bug-wipe(1)\fP
diff --git a/doc/md/git-bug.md b/doc/md/git-bug.md
index b311aff5..a71d6dfb 100644
--- a/doc/md/git-bug.md
+++ b/doc/md/git-bug.md
@@ -34,4 +34,5 @@ git-bug [flags]
* [git-bug user](git-bug_user.md) - List identities
* [git-bug version](git-bug_version.md) - Show git-bug version information
* [git-bug webui](git-bug_webui.md) - Launch the web UI
+* [git-bug wipe](git-bug_wipe.md) - Wipe git-bug from the git repository
diff --git a/doc/md/git-bug_wipe.md b/doc/md/git-bug_wipe.md
new file mode 100644
index 00000000..be1a7246
--- /dev/null
+++ b/doc/md/git-bug_wipe.md
@@ -0,0 +1,18 @@
+## git-bug wipe
+
+Wipe git-bug from the git repository
+
+```
+git-bug wipe [flags]
+```
+
+### Options
+
+```
+ -h, --help help for wipe
+```
+
+### SEE ALSO
+
+* [git-bug](git-bug.md) - A bug tracker embedded in Git
+
diff --git a/entities/bug/bug_actions.go b/entities/bug/bug_actions.go
index 198e4ed0..651d24dc 100644
--- a/entities/bug/bug_actions.go
+++ b/entities/bug/bug_actions.go
@@ -37,3 +37,9 @@ func MergeAll(repo repository.ClockedRepo, resolvers entity.Resolvers, remote st
func Remove(repo repository.ClockedRepo, id entity.Id) error {
return dag.Remove(def, repo, id)
}
+
+// RemoveAll will remove all local bugs.
+// RemoveAll is idempotent.
+func RemoveAll(repo repository.ClockedRepo) error {
+ return dag.RemoveAll(def, repo)
+}
diff --git a/entities/identity/identity.go b/entities/identity/identity.go
index 22ce652c..7c91b7b3 100644
--- a/entities/identity/identity.go
+++ b/entities/identity/identity.go
@@ -164,56 +164,6 @@ func ListLocalIds(repo repository.Repo) ([]entity.Id, error) {
return entity.RefsToIds(refs), nil
}
-// RemoveIdentity will remove a local identity from its entity.Id
-func RemoveIdentity(repo repository.ClockedRepo, id entity.Id) error {
- var fullMatches []string
-
- refs, err := repo.ListRefs(identityRefPattern + id.String())
- if err != nil {
- return err
- }
- if len(refs) > 1 {
- return entity.NewErrMultipleMatch(Typename, entity.RefsToIds(refs))
- }
- if len(refs) == 1 {
- // we have the identity locally
- fullMatches = append(fullMatches, refs[0])
- }
-
- remotes, err := repo.GetRemotes()
- if err != nil {
- return err
- }
-
- for remote := range remotes {
- remotePrefix := fmt.Sprintf(identityRemoteRefPattern+id.String(), remote)
- remoteRefs, err := repo.ListRefs(remotePrefix)
- if err != nil {
- return err
- }
- if len(remoteRefs) > 1 {
- return entity.NewErrMultipleMatch(Typename, entity.RefsToIds(refs))
- }
- if len(remoteRefs) == 1 {
- // found the identity in a remote
- fullMatches = append(fullMatches, remoteRefs[0])
- }
- }
-
- if len(fullMatches) == 0 {
- return entity.NewErrNotFound(Typename)
- }
-
- for _, ref := range fullMatches {
- err = repo.RemoveRef(ref)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
// ReadAllLocal read and parse all local Identity
func ReadAllLocal(repo repository.ClockedRepo) <-chan entity.StreamedEntity[*Identity] {
return readAll(repo, identityRefPattern)
diff --git a/entities/identity/identity_actions.go b/entities/identity/identity_actions.go
index 13776078..07560dc0 100644
--- a/entities/identity/identity_actions.go
+++ b/entities/identity/identity_actions.go
@@ -123,3 +123,74 @@ func MergeAll(repo repository.ClockedRepo, remote string) <-chan entity.MergeRes
return out
}
+
+// Remove will remove a local identity from its entity.Id.
+// It is left as a responsibility to the caller to make sure that this identities is not
+// linked from another entity, otherwise it would break it.
+// Remove is idempotent.
+func Remove(repo repository.ClockedRepo, id entity.Id) error {
+ var fullMatches []string
+
+ refs, err := repo.ListRefs(identityRefPattern + id.String())
+ if err != nil {
+ return err
+ }
+ if len(refs) > 1 {
+ return entity.NewErrMultipleMatch(Typename, entity.RefsToIds(refs))
+ }
+ if len(refs) == 1 {
+ // we have the identity locally
+ fullMatches = append(fullMatches, refs[0])
+ }
+
+ remotes, err := repo.GetRemotes()
+ if err != nil {
+ return err
+ }
+
+ for remote := range remotes {
+ remotePrefix := fmt.Sprintf(identityRemoteRefPattern+id.String(), remote)
+ remoteRefs, err := repo.ListRefs(remotePrefix)
+ if err != nil {
+ return err
+ }
+ if len(remoteRefs) > 1 {
+ return entity.NewErrMultipleMatch(Typename, entity.RefsToIds(refs))
+ }
+ if len(remoteRefs) == 1 {
+ // found the identity in a remote
+ fullMatches = append(fullMatches, remoteRefs[0])
+ }
+ }
+
+ if len(fullMatches) == 0 {
+ return entity.NewErrNotFound(Typename)
+ }
+
+ for _, ref := range fullMatches {
+ err = repo.RemoveRef(ref)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// RemoveAll will remove all local identities.
+// It is left as a responsibility to the caller to make sure that those identities are not
+// linked from another entity, otherwise it would break them.
+// RemoveAll is idempotent.
+func RemoveAll(repo repository.ClockedRepo) error {
+ localIds, err := ListLocalIds(repo)
+ if err != nil {
+ return err
+ }
+ for _, id := range localIds {
+ err = Remove(repo, id)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/entities/identity/identity_test.go b/entities/identity/identity_test.go
index 0ecc8058..85d5385b 100644
--- a/entities/identity/identity_test.go
+++ b/entities/identity/identity_test.go
@@ -275,7 +275,7 @@ func TestIdentityRemove(t *testing.T) {
_, err = Fetch(repo, "remoteB")
require.NoError(t, err)
- err = RemoveIdentity(repo, rene.Id())
+ err = Remove(repo, rene.Id())
require.NoError(t, err)
_, err = ReadLocal(repo, rene.Id())
diff --git a/entities/identity/identity_user.go b/entities/identity/identity_user.go
index 7eb374d4..f9e39bb2 100644
--- a/entities/identity/identity_user.go
+++ b/entities/identity/identity_user.go
@@ -15,6 +15,10 @@ func SetUserIdentity(repo repository.RepoConfig, identity *Identity) error {
return repo.LocalConfig().StoreString(identityConfigKey, identity.Id().String())
}
+func ClearUserIdentity(repo repository.RepoConfig) error {
+ return repo.LocalConfig().RemoveAll(identityConfigKey)
+}
+
// GetUserIdentity read the current user identity, set with a git config entry
func GetUserIdentity(repo repository.Repo) (*Identity, error) {
id, err := GetUserIdentityId(repo)
diff --git a/entity/dag/entity_actions.go b/entity/dag/entity_actions.go
index 97a68c36..5f0abec3 100644
--- a/entity/dag/entity_actions.go
+++ b/entity/dag/entity_actions.go
@@ -258,3 +258,19 @@ func Remove(def Definition, repo repository.ClockedRepo, id entity.Id) error {
return nil
}
+
+// RemoveAll delete all Entity matching the Definition.
+// RemoveAll is idempotent.
+func RemoveAll(def Definition, repo repository.ClockedRepo) error {
+ localIds, err := ListLocalIds(def, repo)
+ if err != nil {
+ return err
+ }
+ for _, id := range localIds {
+ err = Remove(def, repo, id)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/entity/dag/entity_actions_test.go b/entity/dag/entity_actions_test.go
index fd219644..6181614b 100644
--- a/entity/dag/entity_actions_test.go
+++ b/entity/dag/entity_actions_test.go
@@ -406,3 +406,34 @@ func TestRemove(t *testing.T) {
err = Remove(def, repoA, e.Id())
require.NoError(t, err)
}
+
+func TestRemoveAll(t *testing.T) {
+ repoA, _, _, id1, _, resolvers, def := makeTestContextRemote(t)
+
+ var ids []entity.Id
+
+ for i := 0; i < 10; i++ {
+ e := New(def)
+ e.Append(newOp1(id1, "foo"))
+ require.NoError(t, e.Commit(repoA))
+ ids = append(ids, e.Id())
+ }
+
+ _, err := Push(def, repoA, "remote")
+ require.NoError(t, err)
+
+ err = RemoveAll(def, repoA)
+ require.NoError(t, err)
+
+ for _, id := range ids {
+ _, err = Read(def, wrapper, repoA, resolvers, id)
+ require.Error(t, err)
+
+ _, err = readRemote(def, wrapper, repoA, resolvers, "remote", id)
+ require.Error(t, err)
+ }
+
+ // Remove is idempotent
+ err = RemoveAll(def, repoA)
+ require.NoError(t, err)
+}
diff --git a/repository/gogit.go b/repository/gogit.go
index 01c47d41..93806026 100644
--- a/repository/gogit.go
+++ b/repository/gogit.go
@@ -13,7 +13,6 @@ import (
"time"
"github.com/ProtonMail/go-crypto/openpgp"
- "github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs"
gogit "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
@@ -48,7 +47,7 @@ type GoGitRepo struct {
indexes map[string]Index
keyring Keyring
- localStorage billy.Filesystem
+ localStorage LocalStorage
}
// OpenGoGitRepo opens an already existing repo at the given path and
@@ -77,7 +76,7 @@ func OpenGoGitRepo(path, namespace string, clockLoaders []ClockLoader) (*GoGitRe
clocks: make(map[string]lamport.Clock),
indexes: make(map[string]Index),
keyring: k,
- localStorage: osfs.New(filepath.Join(path, namespace)),
+ localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, namespace))},
}
loaderToRun := make([]ClockLoader, 0, len(clockLoaders))
@@ -131,7 +130,7 @@ func InitGoGitRepo(path, namespace string) (*GoGitRepo, error) {
clocks: make(map[string]lamport.Clock),
indexes: make(map[string]Index),
keyring: k,
- localStorage: osfs.New(filepath.Join(path, ".git", namespace)),
+ localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, ".git", namespace))},
}, nil
}
@@ -156,7 +155,7 @@ func InitBareGoGitRepo(path, namespace string) (*GoGitRepo, error) {
clocks: make(map[string]lamport.Clock),
indexes: make(map[string]Index),
keyring: k,
- localStorage: osfs.New(filepath.Join(path, namespace)),
+ localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, namespace))},
}, nil
}
@@ -320,7 +319,7 @@ func (repo *GoGitRepo) GetRemotes() (map[string]string, error) {
// LocalStorage returns a billy.Filesystem giving access to
// $RepoPath/.git/$Namespace.
-func (repo *GoGitRepo) LocalStorage() billy.Filesystem {
+func (repo *GoGitRepo) LocalStorage() LocalStorage {
return repo.localStorage
}
diff --git a/repository/index_bleve.go b/repository/index_bleve.go
index aae41d5f..40170919 100644
--- a/repository/index_bleve.go
+++ b/repository/index_bleve.go
@@ -129,6 +129,13 @@ func (b *bleveIndex) DocCount() (uint64, error) {
return b.index.DocCount()
}
+func (b *bleveIndex) Remove(id string) error {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ return b.index.Delete(id)
+}
+
func (b *bleveIndex) Clear() error {
b.mu.Lock()
defer b.mu.Unlock()
diff --git a/repository/localstorage_billy.go b/repository/localstorage_billy.go
new file mode 100644
index 00000000..ab3909c9
--- /dev/null
+++ b/repository/localstorage_billy.go
@@ -0,0 +1,16 @@
+package repository
+
+import (
+ "github.com/go-git/go-billy/v5"
+ "github.com/go-git/go-billy/v5/util"
+)
+
+var _ LocalStorage = &billyLocalStorage{}
+
+type billyLocalStorage struct {
+ billy.Filesystem
+}
+
+func (b billyLocalStorage) RemoveAll(path string) error {
+ return util.RemoveAll(b.Filesystem, path)
+}
diff --git a/repository/mock_repo.go b/repository/mock_repo.go
index c2cef8ef..6ea5c71e 100644
--- a/repository/mock_repo.go
+++ b/repository/mock_repo.go
@@ -9,7 +9,6 @@ import (
"github.com/99designs/keyring"
"github.com/ProtonMail/go-crypto/openpgp"
- "github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
"github.com/MichaelMure/git-bug/util/lamport"
@@ -123,14 +122,14 @@ func (r *mockRepoCommon) GetRemotes() (map[string]string, error) {
var _ RepoStorage = &mockRepoStorage{}
type mockRepoStorage struct {
- localFs billy.Filesystem
+ localFs LocalStorage
}
func NewMockRepoStorage() *mockRepoStorage {
- return &mockRepoStorage{localFs: memfs.New()}
+ return &mockRepoStorage{localFs: billyLocalStorage{Filesystem: memfs.New()}}
}
-func (m *mockRepoStorage) LocalStorage() billy.Filesystem {
+func (m *mockRepoStorage) LocalStorage() LocalStorage {
return m.localFs
}
@@ -204,6 +203,11 @@ func (m *mockIndex) DocCount() (uint64, error) {
return uint64(len(*m)), nil
}
+func (m *mockIndex) Remove(id string) error {
+ delete(*m, id)
+ return nil
+}
+
func (m *mockIndex) Clear() error {
for k, _ := range *m {
delete(*m, k)
diff --git a/repository/repo.go b/repository/repo.go
index 66baec65..c39051d5 100644
--- a/repository/repo.go
+++ b/repository/repo.go
@@ -76,10 +76,15 @@ type RepoCommon interface {
GetRemotes() (map[string]string, error)
}
+type LocalStorage interface {
+ billy.Filesystem
+ RemoveAll(path string) error
+}
+
// RepoStorage give access to the filesystem
type RepoStorage interface {
// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
- LocalStorage() billy.Filesystem
+ LocalStorage() LocalStorage
}
// RepoIndex gives access to full-text search indexes
@@ -103,6 +108,9 @@ type Index interface {
// DocCount returns the number of document in the index.
DocCount() (uint64, error)
+ // Remove delete one document in the index.
+ Remove(id string) error
+
// Clear empty the index.
Clear() error
diff --git a/repository/repo_testing.go b/repository/repo_testing.go
index 821eb762..09332e62 100644
--- a/repository/repo_testing.go
+++ b/repository/repo_testing.go
@@ -2,6 +2,7 @@ package repository
import (
"math/rand"
+ "os"
"testing"
"github.com/ProtonMail/go-crypto/openpgp"
@@ -10,8 +11,6 @@ import (
"github.com/MichaelMure/git-bug/util/lamport"
)
-// TODO: add tests for RepoStorage
-
type RepoCreator func(t testing.TB, bare bool) TestedRepo
// Test suite for a Repo implementation
@@ -32,6 +31,10 @@ func RepoTest(t *testing.T, creator RepoCreator) {
RepoConfigTest(t, repo)
})
+ t.Run("Storage", func(t *testing.T) {
+ RepoStorageTest(t, repo)
+ })
+
t.Run("Index", func(t *testing.T) {
RepoIndexTest(t, repo)
})
@@ -48,6 +51,33 @@ func RepoConfigTest(t *testing.T, repo RepoConfig) {
testConfig(t, repo.LocalConfig())
}
+func RepoStorageTest(t *testing.T, repo RepoStorage) {
+ storage := repo.LocalStorage()
+
+ err := storage.MkdirAll("foo/bar", 0755)
+ require.NoError(t, err)
+
+ f, err := storage.Create("foo/bar/foofoo")
+ require.NoError(t, err)
+
+ _, err = f.Write([]byte("hello"))
+ require.NoError(t, err)
+
+ // remove all
+ err = storage.RemoveAll(".")
+ require.NoError(t, err)
+
+ fi, err := storage.ReadDir(".")
+ // a real FS would remove the root directory with RemoveAll and subsequent call would fail
+ // a memory FS would still have a virtual root and subsequent call would succeed
+ // not ideal, but will do for now
+ if err == nil {
+ require.Empty(t, fi)
+ } else {
+ require.True(t, os.IsNotExist(err))
+ }
+}
+
func randomHash() Hash {
var letterRunes = "abcdef0123456789"
b := make([]byte, idLengthSHA256)