diff options
author | Kalin Staykov <k.t.staykov@gmail.com> | 2022-11-26 15:49:59 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2023-01-11 14:58:58 +0100 |
commit | fc266b733cf0ea794209031e3500ab3f86db6cee (patch) | |
tree | bae1996ec19fb74446429200e52cdb942f60ac54 | |
parent | 9c50a359704f4edd2f33df6d256e032feae3a576 (diff) | |
download | git-bug-fc266b733cf0ea794209031e3500ab3f86db6cee.tar.gz |
add wipe sub-command that remove local bugs and identities
-rw-r--r-- | cache/bug_subcache.go | 1 | ||||
-rw-r--r-- | cache/identity_subcache.go | 3 | ||||
-rw-r--r-- | cache/repo_cache.go | 1 | ||||
-rw-r--r-- | cache/repo_cache_common.go | 26 | ||||
-rw-r--r-- | cache/repo_cache_test.go | 33 | ||||
-rw-r--r-- | cache/subcache.go | 43 | ||||
-rw-r--r-- | commands/root.go | 1 | ||||
-rw-r--r-- | commands/wipe.go | 58 | ||||
-rw-r--r-- | doc/man/git-bug-wipe.1 | 27 | ||||
-rw-r--r-- | doc/man/git-bug.1 | 2 | ||||
-rw-r--r-- | doc/md/git-bug.md | 1 | ||||
-rw-r--r-- | doc/md/git-bug_wipe.md | 18 | ||||
-rw-r--r-- | entities/bug/bug_actions.go | 6 | ||||
-rw-r--r-- | entities/identity/identity.go | 50 | ||||
-rw-r--r-- | entities/identity/identity_actions.go | 71 | ||||
-rw-r--r-- | entities/identity/identity_test.go | 2 | ||||
-rw-r--r-- | entities/identity/identity_user.go | 4 | ||||
-rw-r--r-- | entity/dag/entity_actions.go | 16 | ||||
-rw-r--r-- | entity/dag/entity_actions_test.go | 31 | ||||
-rw-r--r-- | repository/gogit.go | 11 | ||||
-rw-r--r-- | repository/index_bleve.go | 7 | ||||
-rw-r--r-- | repository/localstorage_billy.go | 16 | ||||
-rw-r--r-- | repository/mock_repo.go | 12 | ||||
-rw-r--r-- | repository/repo.go | 10 | ||||
-rw-r--r-- | repository/repo_testing.go | 34 |
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) |