aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/go.yml41
-rw-r--r--.github/workflows/nodejs.yml38
-rw-r--r--api/graphql/resolvers/repo.go5
-rw-r--r--bridge/github/config_test.go3
-rw-r--r--bug/bug_test.go4
-rw-r--r--cache/repo_cache.go45
-rw-r--r--cache/repo_cache_bug.go70
-rw-r--r--cache/repo_cache_common.go24
-rw-r--r--cache/repo_cache_identity.go11
-rw-r--r--cache/repo_cache_test.go8
-rw-r--r--commands/env.go2
-rw-r--r--commands/ls.go5
-rw-r--r--commands/select/select.go22
-rw-r--r--doc/gen_docs.go5
-rw-r--r--go.mod1
-rw-r--r--go.sum19
-rw-r--r--identity/identity_test.go4
-rw-r--r--input/input.go36
-rw-r--r--misc/gen_completion.go14
-rw-r--r--misc/random_bugs/cmd/main.go2
-rw-r--r--repository/git.go132
-rw-r--r--repository/git_testing.go6
-rw-r--r--repository/gogit.go227
-rw-r--r--repository/gogit_config.go6
-rw-r--r--repository/gogit_test.go6
-rw-r--r--repository/gogit_testing.go6
-rw-r--r--repository/keyring.go4
-rw-r--r--repository/mock_repo.go84
-rw-r--r--repository/repo.go36
-rw-r--r--repository/repo_testing.go28
-rw-r--r--termui/bug_table.go6
-rw-r--r--util/lamport/persisted_clock.go29
-rw-r--r--util/lamport/persisted_clock_test.go8
33 files changed, 620 insertions, 317 deletions
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 00000000..3ce22f3a
--- /dev/null
+++ b/.github/workflows/go.yml
@@ -0,0 +1,41 @@
+name: Go build and test
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+ workflow_dispatch:
+
+jobs:
+ build:
+
+ strategy:
+ matrix:
+ go-version: [1.13.x, 1.14.x, 1.15.x]
+ platform: [ubuntu-latest, macos-latest, windows-latest]
+
+ runs-on: ${{ matrix.platform }}
+
+ steps:
+
+ - name: Set up Go ${{ matrix.node-version }}
+ uses: actions/setup-go@v2
+ with:
+ go-version: ${{ matrix.go-version }}
+
+ - name: Check out code
+ uses: actions/checkout@v2
+
+ - name: Build
+ run: make
+
+ - name: Test
+ run: make test
+ env:
+ GITHUB_TEST_USER: ${{ secrets._GITHUB_TEST_USER }}
+ GITHUB_TOKEN_ADMIN: ${{ secrets._GITHUB_TOKEN_ADMIN }}
+ GITHUB_TOKEN_PRIVATE: ${{ secrets._GITHUB_TOKEN_PRIVATE }}
+ GITHUB_TOKEN_PUBLIC: ${{ secrets._GITHUB_TOKEN_PUBLIC }}
+ GITLAB_API_TOKEN: ${{ secrets.GITLAB_API_TOKEN }}
+ GITLAB_PROJECT_ID: ${{ secrets.GITLAB_PROJECT_ID }}
diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
new file mode 100644
index 00000000..d7a69c8e
--- /dev/null
+++ b/.github/workflows/nodejs.yml
@@ -0,0 +1,38 @@
+name: Node.js build and test
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+ workflow_dispatch:
+
+defaults:
+ run:
+ working-directory: webui
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [10.x, 12.x, 14.x]
+
+ steps:
+ - name: Setup Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ - name: Check out code
+ uses: actions/checkout@v2
+
+ - name: Install
+ run: make install
+
+ - name: Build
+ run: make build
+
+ - name: Test
+ run: make test
diff --git a/api/graphql/resolvers/repo.go b/api/graphql/resolvers/repo.go
index 5d96428e..c2163cbe 100644
--- a/api/graphql/resolvers/repo.go
+++ b/api/graphql/resolvers/repo.go
@@ -41,7 +41,10 @@ func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *st
}
// Simply pass a []string with the ids to the pagination algorithm
- source := obj.Repo.QueryBugs(q)
+ source, err := obj.Repo.QueryBugs(q)
+ if err != nil {
+ return nil, err
+ }
// The edger create a custom edge holding just the id
edger := func(id entity.Id, offset int) connections.Edge {
diff --git a/bridge/github/config_test.go b/bridge/github/config_test.go
index eecb1aa8..01907435 100644
--- a/bridge/github/config_test.go
+++ b/bridge/github/config_test.go
@@ -103,6 +103,9 @@ func TestValidateUsername(t *testing.T) {
if env := os.Getenv("TRAVIS"); env == "true" {
t.Skip("Travis environment: avoiding non authenticated requests")
}
+ if _, has := os.LookupEnv("CI"); has {
+ t.Skip("Github action environment: avoiding non authenticated requests")
+ }
tests := []struct {
name string
diff --git a/bug/bug_test.go b/bug/bug_test.go
index 05f6e617..6363f4e9 100644
--- a/bug/bug_test.go
+++ b/bug/bug_test.go
@@ -130,10 +130,10 @@ func TestBugRemove(t *testing.T) {
remoteB := repository.CreateGoGitTestRepo(true)
defer repository.CleanupTestRepos(repo, remoteA, remoteB)
- err := repo.AddRemote("remoteA", "file://"+remoteA.GetPath())
+ err := repo.AddRemote("remoteA", remoteA.GetLocalRemote())
require.NoError(t, err)
- err = repo.AddRemote("remoteB", "file://"+remoteB.GetPath())
+ err = repo.AddRemote("remoteB", remoteB.GetLocalRemote())
require.NoError(t, err)
// generate a bunch of bugs
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index 8bce3d8c..b5b9ee54 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -5,8 +5,6 @@ import (
"io"
"io/ioutil"
"os"
- "path"
- "path/filepath"
"strconv"
"sync"
@@ -15,7 +13,6 @@ import (
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/process"
- "github.com/blevesearch/bleve"
)
// 1: original format
@@ -57,8 +54,6 @@ type RepoCache struct {
muBug sync.RWMutex
// excerpt of bugs data for all bugs
bugExcerpts map[entity.Id]*BugExcerpt
- // searchable cache of all bugs
- searchCache bleve.Index
// bug loaded in memory
bugs map[entity.Id]*BugCache
// loadedBugs is an LRU cache that records which bugs the cache has loaded in
@@ -133,25 +128,18 @@ func (c *RepoCache) write() error {
}
func (c *RepoCache) lock() error {
- lockPath := repoLockFilePath(c.repo)
-
err := repoIsAvailable(c.repo)
if err != nil {
return err
}
- err = os.MkdirAll(filepath.Dir(lockPath), 0777)
- if err != nil {
- return err
- }
-
- f, err := os.Create(lockPath)
+ f, err := c.repo.LocalStorage().Create(lockfile)
if err != nil {
return err
}
pid := fmt.Sprintf("%d", os.Getpid())
- _, err = f.WriteString(pid)
+ _, err = f.Write([]byte(pid))
if err != nil {
return err
}
@@ -170,16 +158,17 @@ func (c *RepoCache) Close() error {
c.bugs = make(map[entity.Id]*BugCache)
c.bugExcerpts = nil
- if c.searchCache != nil {
- c.searchCache.Close()
- c.searchCache = nil
+ err := c.repo.Close()
+ if err != nil {
+ return err
}
- lockPath := repoLockFilePath(c.repo)
- return os.Remove(lockPath)
+ return c.repo.LocalStorage().Remove(lockfile)
}
func (c *RepoCache) buildCache() error {
+ // TODO: make that parallel
+
c.muBug.Lock()
defer c.muBug.Unlock()
c.muIdentity.Lock()
@@ -207,9 +196,10 @@ func (c *RepoCache) buildCache() error {
allBugs := bug.ReadAllLocal(c.repo)
- err := c.createBleveIndex()
+ // wipe the index just to be sure
+ err := c.repo.ClearBleveIndex("bug")
if err != nil {
- return fmt.Errorf("Unable to create search cache. Error: %v", err)
+ return err
}
for b := range allBugs {
@@ -230,17 +220,11 @@ func (c *RepoCache) buildCache() error {
return nil
}
-func repoLockFilePath(repo repository.Repo) string {
- return path.Join(repo.GetPath(), "git-bug", lockfile)
-}
-
// repoIsAvailable check is the given repository is locked by a Cache.
// Note: this is a smart function that will cleanup the lock file if the
// corresponding process is not there anymore.
// If no error is returned, the repo is free to edit.
-func repoIsAvailable(repo repository.Repo) error {
- lockPath := repoLockFilePath(repo)
-
+func repoIsAvailable(repo repository.RepoStorage) error {
// Todo: this leave way for a racey access to the repo between the test
// if the file exist and the actual write. It's probably not a problem in
// practice because using a repository will be done from user interaction
@@ -252,8 +236,7 @@ func repoIsAvailable(repo repository.Repo) error {
// computer. Should add a configuration that prevent the cleaning of the
// lock file
- f, err := os.Open(lockPath)
-
+ f, err := repo.LocalStorage().Open(lockfile)
if err != nil && !os.IsNotExist(err) {
return err
}
@@ -285,7 +268,7 @@ func repoIsAvailable(repo repository.Repo) error {
return err
}
- err = os.Remove(lockPath)
+ err = repo.LocalStorage().Remove(lockfile)
if err != nil {
return err
}
diff --git a/cache/repo_cache_bug.go b/cache/repo_cache_bug.go
index d57e1bce..1701f66d 100644
--- a/cache/repo_cache_bug.go
+++ b/cache/repo_cache_bug.go
@@ -5,8 +5,6 @@ import (
"encoding/gob"
"errors"
"fmt"
- "os"
- "path"
"sort"
"strings"
"time"
@@ -25,14 +23,6 @@ const (
var errBugNotInCache = errors.New("bug missing from cache")
-func bugCacheFilePath(repo repository.Repo) string {
- return path.Join(repo.GetPath(), "git-bug", bugCacheFile)
-}
-
-func searchCacheDirPath(repo repository.Repo) string {
- return path.Join(repo.GetPath(), "git-bug", searchCacheDir)
-}
-
// bugUpdated is a callback to trigger when the excerpt of a bug changed,
// that is each time a bug is updated
func (c *RepoCache) bugUpdated(id entity.Id) error {
@@ -65,7 +55,7 @@ func (c *RepoCache) loadBugCache() error {
c.muBug.Lock()
defer c.muBug.Unlock()
- f, err := os.Open(bugCacheFilePath(c.repo))
+ f, err := c.repo.LocalStorage().Open(bugCacheFile)
if err != nil {
return err
}
@@ -88,14 +78,13 @@ func (c *RepoCache) loadBugCache() error {
c.bugExcerpts = aux.Excerpts
- blevePath := searchCacheDirPath(c.repo)
- searchCache, err := bleve.Open(blevePath)
+ index, err := c.repo.GetBleveIndex("bug")
if err != nil {
- return fmt.Errorf("Unable to open search cache. Error: %v", err)
+ return err
}
- c.searchCache = searchCache
- count, err := c.searchCache.DocCount()
+ // simple heuristic to detect a mismatch between the index and the bugs
+ count, err := index.DocCount()
if err != nil {
return err
}
@@ -106,26 +95,6 @@ func (c *RepoCache) loadBugCache() error {
return nil
}
-func (c *RepoCache) createBleveIndex() error {
- blevePath := searchCacheDirPath(c.repo)
-
- _ = os.RemoveAll(blevePath)
-
- mapping := bleve.NewIndexMapping()
- mapping.DefaultAnalyzer = "en"
-
- dir := searchCacheDirPath(c.repo)
-
- bleveIndex, err := bleve.New(dir, mapping)
- if err != nil {
- return err
- }
-
- c.searchCache = bleveIndex
-
- return nil
-}
-
// write will serialize on disk the bug cache file
func (c *RepoCache) writeBugCache() error {
c.muBug.RLock()
@@ -148,7 +117,7 @@ func (c *RepoCache) writeBugCache() error {
return err
}
- f, err := os.Create(bugCacheFilePath(c.repo))
+ f, err := c.repo.LocalStorage().Create(bugCacheFile)
if err != nil {
return err
}
@@ -293,12 +262,12 @@ func (c *RepoCache) resolveBugMatcher(f func(*BugExcerpt) bool) (entity.Id, erro
}
// QueryBugs return the id of all Bug matching the given Query
-func (c *RepoCache) QueryBugs(q *query.Query) []entity.Id {
+func (c *RepoCache) QueryBugs(q *query.Query) ([]entity.Id, error) {
c.muBug.RLock()
defer c.muBug.RUnlock()
if q == nil {
- return c.AllBugsIds()
+ return c.AllBugsIds(), nil
}
matcher := compileMatcher(q.Filters)
@@ -319,9 +288,15 @@ func (c *RepoCache) QueryBugs(q *query.Query) []entity.Id {
bleveQuery := bleve.NewQueryStringQuery(strings.Join(terms, " "))
bleveSearch := bleve.NewSearchRequest(bleveQuery)
- searchResults, err := c.searchCache.Search(bleveSearch)
+
+ index, err := c.repo.GetBleveIndex("bug")
if err != nil {
- panic("bleve search failed")
+ return nil, err
+ }
+
+ searchResults, err := index.Search(bleveSearch)
+ if err != nil {
+ return nil, err
}
for _, hit := range searchResults.Hits {
@@ -347,7 +322,7 @@ func (c *RepoCache) QueryBugs(q *query.Query) []entity.Id {
case query.OrderByEdit:
sorter = BugsByEditTime(filtered)
default:
- panic("missing sort type")
+ return nil, errors.New("missing sort type")
}
switch q.OrderDirection {
@@ -356,7 +331,7 @@ func (c *RepoCache) QueryBugs(q *query.Query) []entity.Id {
case query.OrderDescending:
sorter = sort.Reverse(sorter)
default:
- panic("missing sort direction")
+ return nil, errors.New("missing sort direction")
}
sort.Sort(sorter)
@@ -367,7 +342,7 @@ func (c *RepoCache) QueryBugs(q *query.Query) []entity.Id {
result[i] = val.Id
}
- return result
+ return result, nil
}
// AllBugsIds return all known bug ids
@@ -510,7 +485,12 @@ func (c *RepoCache) addBugToSearchIndex(snap *bug.Snapshot) error {
searchableBug.Text = append(searchableBug.Text, snap.Title)
- err := c.searchCache.Index(snap.Id().String(), searchableBug)
+ index, err := c.repo.GetBleveIndex("bug")
+ if err != nil {
+ return err
+ }
+
+ err = index.Index(snap.Id().String(), searchableBug)
if err != nil {
return err
}
diff --git a/cache/repo_cache_common.go b/cache/repo_cache_common.go
index 95e2f7bb..5dc19d22 100644
--- a/cache/repo_cache_common.go
+++ b/cache/repo_cache_common.go
@@ -3,6 +3,7 @@ package cache
import (
"fmt"
+ "github.com/go-git/go-billy/v5"
"github.com/pkg/errors"
"github.com/MichaelMure/git-bug/bug"
@@ -30,13 +31,19 @@ func (c *RepoCache) AnyConfig() repository.ConfigRead {
return c.repo.AnyConfig()
}
+// Keyring give access to a user-wide storage for secrets
func (c *RepoCache) Keyring() repository.Keyring {
return c.repo.Keyring()
}
-// GetPath returns the path to the repo.
-func (c *RepoCache) GetPath() string {
- return c.repo.GetPath()
+// GetUserName returns the name the the user has used to configure git
+func (c *RepoCache) GetUserName() (string, error) {
+ return c.repo.GetUserName()
+}
+
+// GetUserEmail returns the email address that the user has used to configure git.
+func (c *RepoCache) GetUserEmail() (string, error) {
+ return c.repo.GetUserEmail()
}
// GetCoreEditor returns the name of the editor that the user has used to configure git.
@@ -49,14 +56,9 @@ func (c *RepoCache) GetRemotes() (map[string]string, error) {
return c.repo.GetRemotes()
}
-// GetUserName returns the name the the user has used to configure git
-func (c *RepoCache) GetUserName() (string, error) {
- return c.repo.GetUserName()
-}
-
-// GetUserEmail returns the email address that the user has used to configure git.
-func (c *RepoCache) GetUserEmail() (string, error) {
- return c.repo.GetUserEmail()
+// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
+func (c *RepoCache) LocalStorage() billy.Filesystem {
+ return c.repo.LocalStorage()
}
// ReadData will attempt to read arbitrary data from the given hash
diff --git a/cache/repo_cache_identity.go b/cache/repo_cache_identity.go
index 8957d4aa..8df5b810 100644
--- a/cache/repo_cache_identity.go
+++ b/cache/repo_cache_identity.go
@@ -4,20 +4,13 @@ import (
"bytes"
"encoding/gob"
"fmt"
- "os"
- "path"
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
- "github.com/MichaelMure/git-bug/repository"
)
const identityCacheFile = "identity-cache"
-func identityCacheFilePath(repo repository.Repo) string {
- return path.Join(repo.GetPath(), "git-bug", identityCacheFile)
-}
-
// identityUpdated is a callback to trigger when the excerpt of an identity
// changed, that is each time an identity is updated
func (c *RepoCache) identityUpdated(id entity.Id) error {
@@ -41,7 +34,7 @@ func (c *RepoCache) loadIdentityCache() error {
c.muIdentity.Lock()
defer c.muIdentity.Unlock()
- f, err := os.Open(identityCacheFilePath(c.repo))
+ f, err := c.repo.LocalStorage().Open(identityCacheFile)
if err != nil {
return err
}
@@ -88,7 +81,7 @@ func (c *RepoCache) writeIdentityCache() error {
return err
}
- f, err := os.Create(identityCacheFilePath(c.repo))
+ f, err := c.repo.LocalStorage().Create(identityCacheFile)
if err != nil {
return err
}
diff --git a/cache/repo_cache_test.go b/cache/repo_cache_test.go
index 0037c7bb..bd06e84d 100644
--- a/cache/repo_cache_test.go
+++ b/cache/repo_cache_test.go
@@ -73,7 +73,9 @@ func TestCache(t *testing.T) {
// Querying
q, err := query.Parse("status:open author:descartes sort:edit-asc")
require.NoError(t, err)
- require.Len(t, cache.QueryBugs(q), 2)
+ res, err := cache.QueryBugs(q)
+ require.NoError(t, err)
+ require.Len(t, res, 2)
// Close
require.NoError(t, cache.Close())
@@ -167,10 +169,10 @@ func TestRemove(t *testing.T) {
remoteB := repository.CreateGoGitTestRepo(true)
defer repository.CleanupTestRepos(repo, remoteA, remoteB)
- err := repo.AddRemote("remoteA", "file://"+remoteA.GetPath())
+ err := repo.AddRemote("remoteA", remoteA.GetLocalRemote())
require.NoError(t, err)
- err = repo.AddRemote("remoteB", "file://"+remoteB.GetPath())
+ err = repo.AddRemote("remoteB", remoteB.GetLocalRemote())
require.NoError(t, err)
repoCache, err := NewRepoCache(repo)
diff --git a/commands/env.go b/commands/env.go
index 5658342d..8d12c5bf 100644
--- a/commands/env.go
+++ b/commands/env.go
@@ -54,7 +54,7 @@ func loadRepo(env *Env) func(*cobra.Command, []string) error {
return fmt.Errorf("unable to get the current working directory: %q", err)
}
- env.repo, err = repository.NewGoGitRepo(cwd, []repository.ClockLoader{bug.ClockLoader})
+ env.repo, err = repository.OpenGoGitRepo(cwd, []repository.ClockLoader{bug.ClockLoader})
if err == repository.ErrNotARepo {
return fmt.Errorf("%s must be run from within a git repo", rootCommandName)
}
diff --git a/commands/ls.go b/commands/ls.go
index f6d654b1..327fd37f 100644
--- a/commands/ls.go
+++ b/commands/ls.go
@@ -110,7 +110,10 @@ func runLs(env *Env, opts lsOptions, args []string) error {
return err
}
- allIds := env.backend.QueryBugs(q)
+ allIds, err := env.backend.QueryBugs(q)
+ if err != nil {
+ return err
+ }
bugExcerpt := make([]*cache.BugExcerpt, len(allIds))
for i, id := range allIds {
diff --git a/commands/select/select.go b/commands/select/select.go
index cf861fcc..3df74387 100644
--- a/commands/select/select.go
+++ b/commands/select/select.go
@@ -5,14 +5,12 @@ import (
"io"
"io/ioutil"
"os"
- "path"
"github.com/pkg/errors"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/entity"
- "github.com/MichaelMure/git-bug/repository"
)
const selectFile = "select"
@@ -71,14 +69,12 @@ func ResolveBug(repo *cache.RepoCache, args []string) (*cache.BugCache, []string
// Select will select a bug for future use
func Select(repo *cache.RepoCache, id entity.Id) error {
- selectPath := selectFilePath(repo)
-
- f, err := os.OpenFile(selectPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
+ f, err := repo.LocalStorage().OpenFile(selectFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
- _, err = f.WriteString(id.String())
+ _, err = f.Write([]byte(id.String()))
if err != nil {
return err
}
@@ -88,15 +84,11 @@ func Select(repo *cache.RepoCache, id entity.Id) error {
// Clear will clear the selected bug, if any
func Clear(repo *cache.RepoCache) error {
- selectPath := selectFilePath(repo)
-
- return os.Remove(selectPath)
+ return repo.LocalStorage().Remove(selectFile)
}
func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
- selectPath := selectFilePath(repo)
-
- f, err := os.Open(selectPath)
+ f, err := repo.LocalStorage().Open(selectFile)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
@@ -115,7 +107,7 @@ func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
id := entity.Id(buf)
if err := id.Validate(); err != nil {
- err = os.Remove(selectPath)
+ err = repo.LocalStorage().Remove(selectFile)
if err != nil {
return nil, errors.Wrap(err, "error while removing invalid select file")
}
@@ -135,7 +127,3 @@ func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
return b, nil
}
-
-func selectFilePath(repo repository.RepoCommon) string {
- return path.Join(repo.GetPath(), "git-bug", selectFile)
-}
diff --git a/doc/gen_docs.go b/doc/gen_docs.go
index 8bb596fe..482abd8e 100644
--- a/doc/gen_docs.go
+++ b/doc/gen_docs.go
@@ -3,7 +3,6 @@ package main
import (
"fmt"
"os"
- "path"
"path/filepath"
"sync"
"time"
@@ -42,7 +41,7 @@ func main() {
func genManPage(root *cobra.Command) error {
cwd, _ := os.Getwd()
- dir := path.Join(cwd, "doc", "man")
+ dir := filepath.Join(cwd, "doc", "man")
// fixed date to avoid having to commit each month
date := time.Date(2019, 4, 1, 12, 0, 0, 0, time.UTC)
@@ -69,7 +68,7 @@ func genManPage(root *cobra.Command) error {
func genMarkdown(root *cobra.Command) error {
cwd, _ := os.Getwd()
- dir := path.Join(cwd, "doc", "md")
+ dir := filepath.Join(cwd, "doc", "md")
files, err := filepath.Glob(dir + "/*.md")
if err != nil {
diff --git a/go.mod b/go.mod
index 96c578f2..50222a38 100644
--- a/go.mod
+++ b/go.mod
@@ -20,6 +20,7 @@ require (
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
github.com/fatih/color v1.10.0
+ github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.2.0
github.com/golang/protobuf v1.4.3 // indirect
github.com/gorilla/mux v1.8.0
diff --git a/go.sum b/go.sum
index e3dde69b..559e40a1 100644
--- a/go.sum
+++ b/go.sum
@@ -50,15 +50,10 @@ github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhIN
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195 h1:c4mLfegoDw6OhSJXTd2jUEQgZUQuJWtocudb97Qn9EM=
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
@@ -66,14 +61,12 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/awesome-gocui/gocui v0.6.1-0.20191115151952-a34ffb055986 h1:QvIfX96O11qjX1Zr3hKkG0dI12JBRBGABWffyZ1GI60=
github.com/awesome-gocui/gocui v0.6.1-0.20191115151952-a34ffb055986/go.mod h1:1QikxFaPhe2frKeKvEwZEIGia3haiOxOUXKinrv17mA=
github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc h1:wGNpKcHU8Aadr9yOzsT3GEsFLS7HQu8HxQIomnekqf0=
github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc/go.mod h1:tOy3o5Nf1bA17mnK4W41gD7PS3u4Cv0P0pqFcoWMy8s=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
@@ -178,12 +171,10 @@ github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+ne
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
@@ -194,9 +185,10 @@ github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
+github.com/go-git/go-billy v4.2.0+incompatible h1:Z6QtVXd5tjxUtcODLugkJg4WaZnGg13CD8qB9pr+7q0=
+github.com/go-git/go-billy v4.2.0+incompatible/go.mod h1:hedUGslB3n31bx5SW9KMjV/t0CUKnrapjVG9fT7xKX4=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
@@ -286,7 +278,6 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@@ -317,7 +308,6 @@ github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGzny
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
@@ -333,7 +323,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM=
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -343,7 +332,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -382,7 +370,6 @@ github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -728,7 +715,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -786,7 +772,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
diff --git a/identity/identity_test.go b/identity/identity_test.go
index baf933c8..82e58b01 100644
--- a/identity/identity_test.go
+++ b/identity/identity_test.go
@@ -273,10 +273,10 @@ func TestIdentityRemove(t *testing.T) {
remoteB := repository.CreateGoGitTestRepo(true)
defer repository.CleanupTestRepos(repo, remoteA, remoteB)
- err := repo.AddRemote("remoteA", "file://"+remoteA.GetPath())
+ err := repo.AddRemote("remoteA", remoteA.GetLocalRemote())
require.NoError(t, err)
- err = repo.AddRemote("remoteB", "file://"+remoteB.GetPath())
+ err = repo.AddRemote("remoteB", remoteB.GetLocalRemote())
require.NoError(t, err)
// generate an identity for testing
diff --git a/input/input.go b/input/input.go
index ca787ceb..ef76f53e 100644
--- a/input/input.go
+++ b/input/input.go
@@ -11,9 +11,11 @@ import (
"io/ioutil"
"os"
"os/exec"
+ "path/filepath"
"strings"
"github.com/MichaelMure/git-bug/repository"
+ "github.com/go-git/go-billy/v5/util"
"github.com/pkg/errors"
)
@@ -35,7 +37,7 @@ const bugTitleCommentTemplate = `%s%s
// BugCreateEditorInput will open the default editor in the terminal with a
// template for the user to fill. The file is then processed to extract title
// and message.
-func BugCreateEditorInput(repo repository.RepoCommon, preTitle string, preMessage string) (string, string, error) {
+func BugCreateEditorInput(repo repository.RepoCommonStorage, preTitle string, preMessage string) (string, string, error) {
if preMessage != "" {
preMessage = "\n\n" + preMessage
}
@@ -101,10 +103,10 @@ const bugCommentTemplate = `%s
// BugCommentEditorInput will open the default editor in the terminal with a
// template for the user to fill. The file is then processed to extract a comment.
-func BugCommentEditorInput(repo repository.RepoCommon, preMessage string) (string, error) {
+func BugCommentEditorInput(repo repository.RepoCommonStorage, preMessage string) (string, error) {
template := fmt.Sprintf(bugCommentTemplate, preMessage)
- raw, err := launchEditorWithTemplate(repo, messageFilename, template)
+ raw, err := launchEditorWithTemplate(repo, messageFilename, template)
if err != nil {
return "", err
}
@@ -152,10 +154,10 @@ const bugTitleTemplate = `%s
// BugTitleEditorInput will open the default editor in the terminal with a
// template for the user to fill. The file is then processed to extract a title.
-func BugTitleEditorInput(repo repository.RepoCommon, preTitle string) (string, error) {
+func BugTitleEditorInput(repo repository.RepoCommonStorage, preTitle string) (string, error) {
template := fmt.Sprintf(bugTitleTemplate, preTitle)
- raw, err := launchEditorWithTemplate(repo, messageFilename, template)
+ raw, err := launchEditorWithTemplate(repo, messageFilename, template)
if err != nil {
return "", err
}
@@ -212,10 +214,10 @@ const queryTemplate = `%s
// QueryEditorInput will open the default editor in the terminal with a
// template for the user to fill. The file is then processed to extract a query.
-func QueryEditorInput(repo repository.RepoCommon, preQuery string) (string, error) {
+func QueryEditorInput(repo repository.RepoCommonStorage, preQuery string) (string, error) {
template := fmt.Sprintf(queryTemplate, preQuery)
- raw, err := launchEditorWithTemplate(repo, messageFilename, template)
+ raw, err := launchEditorWithTemplate(repo, messageFilename, template)
if err != nil {
return "", err
}
@@ -238,11 +240,8 @@ func QueryEditorInput(repo repository.RepoCommon, preQuery string) (string, erro
// launchEditorWithTemplate will launch an editor as launchEditor do, but with a
// provided template.
-func launchEditorWithTemplate(repo repository.RepoCommon, fileName string, template string) (string, error) {
- path := fmt.Sprintf("%s/%s", repo.GetPath(), fileName)
-
- err := ioutil.WriteFile(path, []byte(template), 0644)
-
+func launchEditorWithTemplate(repo repository.RepoCommonStorage, fileName string, template string) (string, error) {
+ err := util.WriteFile(repo.LocalStorage(), fileName, []byte(template), 0644)
if err != nil {
return "", err
}
@@ -254,20 +253,25 @@ func launchEditorWithTemplate(repo repository.RepoCommon, fileName string, templ
// method blocks until the editor command has returned.
//
// The specified filename should be a temporary file and provided as a relative path
-// from the repo (e.g. "FILENAME" will be converted to "[<reporoot>/].git/FILENAME"). This file
+// from the repo (e.g. "FILENAME" will be converted to "[<reporoot>/].git/git-bug/FILENAME"). This file
// will be deleted after the editor is closed and its contents have been read.
//
// This method returns the text that was read from the temporary file, or
// an error if any step in the process failed.
-func launchEditor(repo repository.RepoCommon, fileName string) (string, error) {
- path := fmt.Sprintf("%s/%s", repo.GetPath(), fileName)
- defer os.Remove(path)
+func launchEditor(repo repository.RepoCommonStorage, fileName string) (string, error) {
+ defer repo.LocalStorage().Remove(fileName)
editor, err := repo.GetCoreEditor()
if err != nil {
return "", fmt.Errorf("Unable to detect default git editor: %v\n", err)
}
+ repo.LocalStorage().Root()
+
+ // bypass the interface but that's ok: we need that because we are communicating
+ // the absolute path to an external program
+ path := filepath.Join(repo.LocalStorage().Root(), fileName)
+
cmd, err := startInlineCommand(editor, path)
if err != nil {
// Running the editor directly did not work. This might mean that
diff --git a/misc/gen_completion.go b/misc/gen_completion.go
index af00fa64..c073e67e 100644
--- a/misc/gen_completion.go
+++ b/misc/gen_completion.go
@@ -3,7 +3,7 @@ package main
import (
"fmt"
"os"
- "path"
+ "path/filepath"
"sync"
"github.com/spf13/cobra"
@@ -41,24 +41,24 @@ func main() {
func genBash(root *cobra.Command) error {
cwd, _ := os.Getwd()
- dir := path.Join(cwd, "misc", "bash_completion", "git-bug")
+ dir := filepath.Join(cwd, "misc", "bash_completion", "git-bug")
return root.GenBashCompletionFile(dir)
}
func genFish(root *cobra.Command) error {
cwd, _ := os.Getwd()
- dir := path.Join(cwd, "misc", "fish_completion", "git-bug")
+ dir := filepath.Join(cwd, "misc", "fish_completion", "git-bug")
return root.GenFishCompletionFile(dir, true)
}
func genPowerShell(root *cobra.Command) error {
cwd, _ := os.Getwd()
- filepath := path.Join(cwd, "misc", "powershell_completion", "git-bug")
- return root.GenPowerShellCompletionFile(filepath)
+ path := filepath.Join(cwd, "misc", "powershell_completion", "git-bug")
+ return root.GenPowerShellCompletionFile(path)
}
func genZsh(root *cobra.Command) error {
cwd, _ := os.Getwd()
- filepath := path.Join(cwd, "misc", "zsh_completion", "git-bug")
- return root.GenZshCompletionFile(filepath)
+ path := filepath.Join(cwd, "misc", "zsh_completion", "git-bug")
+ return root.GenZshCompletionFile(path)
}
diff --git a/misc/random_bugs/cmd/main.go b/misc/random_bugs/cmd/main.go
index 3127b4aa..010ae6d1 100644
--- a/misc/random_bugs/cmd/main.go
+++ b/misc/random_bugs/cmd/main.go
@@ -20,7 +20,7 @@ func main() {
bug.ClockLoader,
}
- repo, err := repository.NewGoGitRepo(dir, loaders)
+ repo, err := repository.OpenGoGitRepo(dir, loaders)
if err != nil {
panic(err)
}
diff --git a/repository/git.go b/repository/git.go
index 504cdd89..bc9d8772 100644
--- a/repository/git.go
+++ b/repository/git.go
@@ -4,10 +4,15 @@ package repository
import (
"bytes"
"fmt"
- "path"
+ "os"
+ "path/filepath"
"strings"
"sync"
+ "github.com/blevesearch/bleve"
+ "github.com/go-git/go-billy/v5"
+ "github.com/go-git/go-billy/v5/osfs"
+
"github.com/MichaelMure/git-bug/util/lamport"
)
@@ -26,12 +31,15 @@ type GitRepo struct {
clocksMutex sync.Mutex
clocks map[string]lamport.Clock
+ indexesMutex sync.Mutex
+ indexes map[string]bleve.Index
+
keyring Keyring
}
-// NewGitRepo determines if the given working directory is inside of a git repository,
+// OpenGitRepo determines if the given working directory is inside of a git repository,
// and returns the corresponding GitRepo instance if it is.
-func NewGitRepo(path string, clockLoaders []ClockLoader) (*GitRepo, error) {
+func OpenGitRepo(path string, clockLoaders []ClockLoader) (*GitRepo, error) {
k, err := defaultKeyring()
if err != nil {
return nil, err
@@ -41,6 +49,7 @@ func NewGitRepo(path string, clockLoaders []ClockLoader) (*GitRepo, error) {
gitCli: gitCli{path: path},
path: path,
clocks: make(map[string]lamport.Clock),
+ indexes: make(map[string]bleve.Index),
keyring: k,
}
@@ -80,9 +89,10 @@ func NewGitRepo(path string, clockLoaders []ClockLoader) (*GitRepo, error) {
// InitGitRepo create a new empty git repo at the given path
func InitGitRepo(path string) (*GitRepo, error) {
repo := &GitRepo{
- gitCli: gitCli{path: path},
- path: path + "/.git",
- clocks: make(map[string]lamport.Clock),
+ gitCli: gitCli{path: path},
+ path: path + "/.git",
+ clocks: make(map[string]lamport.Clock),
+ indexes: make(map[string]bleve.Index),
}
_, err := repo.runGitCommand("init", path)
@@ -96,9 +106,10 @@ func InitGitRepo(path string) (*GitRepo, error) {
// InitBareGitRepo create a new --bare empty git repo at the given path
func InitBareGitRepo(path string) (*GitRepo, error) {
repo := &GitRepo{
- gitCli: gitCli{path: path},
- path: path,
- clocks: make(map[string]lamport.Clock),
+ gitCli: gitCli{path: path},
+ path: path,
+ clocks: make(map[string]lamport.Clock),
+ indexes: make(map[string]bleve.Index),
}
_, err := repo.runGitCommand("init", "--bare", path)
@@ -109,6 +120,17 @@ func InitBareGitRepo(path string) (*GitRepo, error) {
return repo, nil
}
+func (repo *GitRepo) Close() error {
+ var firstErr error
+ for _, index := range repo.indexes {
+ err := index.Close()
+ if err != nil && firstErr == nil {
+ firstErr = err
+ }
+ }
+ return firstErr
+}
+
// LocalConfig give access to the repository scoped configuration
func (repo *GitRepo) LocalConfig() Config {
return newGitConfig(repo.gitCli, false)
@@ -174,6 +196,63 @@ func (repo *GitRepo) GetRemotes() (map[string]string, error) {
return remotes, nil
}
+// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
+func (repo *GitRepo) LocalStorage() billy.Filesystem {
+ return osfs.New(repo.path)
+}
+
+// GetBleveIndex return a bleve.Index that can be used to index documents
+func (repo *GitRepo) GetBleveIndex(name string) (bleve.Index, error) {
+ repo.indexesMutex.Lock()
+ defer repo.indexesMutex.Unlock()
+
+ if index, ok := repo.indexes[name]; ok {
+ return index, nil
+ }
+
+ path := filepath.Join(repo.path, "indexes", name)
+
+ index, err := bleve.Open(path)
+ if err == nil {
+ repo.indexes[name] = index
+ return index, nil
+ }
+
+ err = os.MkdirAll(path, os.ModeDir)
+ if err != nil {
+ return nil, err
+ }
+
+ mapping := bleve.NewIndexMapping()
+ mapping.DefaultAnalyzer = "en"
+
+ index, err = bleve.New(path, mapping)
+ if err != nil {
+ return nil, err
+ }
+
+ repo.indexes[name] = index
+
+ return index, nil
+}
+
+// ClearBleveIndex will wipe the given index
+func (repo *GitRepo) ClearBleveIndex(name string) error {
+ repo.indexesMutex.Lock()
+ defer repo.indexesMutex.Unlock()
+
+ path := filepath.Join(repo.path, "indexes", name)
+
+ err := os.RemoveAll(path)
+ if err != nil {
+ return err
+ }
+
+ delete(repo.indexes, name)
+
+ return nil
+}
+
// FetchRefs fetch git refs from a remote
func (repo *GitRepo) FetchRefs(remote, refSpec string) (string, error) {
stdout, err := repo.runGitCommand("fetch", remote, refSpec)
@@ -358,6 +437,9 @@ func (repo *GitRepo) GetTreeHash(commit Hash) (Hash, error) {
// GetOrCreateClock return a Lamport clock stored in the Repo.
// If the clock doesn't exist, it's created.
func (repo *GitRepo) GetOrCreateClock(name string) (lamport.Clock, error) {
+ repo.clocksMutex.Lock()
+ defer repo.clocksMutex.Unlock()
+
c, err := repo.getClock(name)
if err == nil {
return c, nil
@@ -366,12 +448,7 @@ func (repo *GitRepo) GetOrCreateClock(name string) (lamport.Clock, error) {
return nil, err
}
- repo.clocksMutex.Lock()
- defer repo.clocksMutex.Unlock()
-
- p := path.Join(repo.path, clockPath, name+"-clock")
-
- c, err = lamport.NewPersistedClock(p)
+ c, err = lamport.NewPersistedClock(repo.LocalStorage(), name+"-clock")
if err != nil {
return nil, err
}
@@ -381,16 +458,11 @@ func (repo *GitRepo) GetOrCreateClock(name string) (lamport.Clock, error) {
}
func (repo *GitRepo) getClock(name string) (lamport.Clock, error) {
- repo.clocksMutex.Lock()
- defer repo.clocksMutex.Unlock()
-
if c, ok := repo.clocks[name]; ok {
return c, nil
}
- p := path.Join(repo.path, clockPath, name+"-clock")
-
- c, err := lamport.LoadPersistedClock(p)
+ c, err := lamport.LoadPersistedClock(repo.LocalStorage(), name+"-clock")
if err == nil {
repo.clocks[name] = c
return c, nil
@@ -408,3 +480,21 @@ func (repo *GitRepo) AddRemote(name string, url string) error {
return err
}
+
+// GetLocalRemote return the URL to use to add this repo as a local remote
+func (repo *GitRepo) GetLocalRemote() string {
+ return repo.path
+}
+
+// EraseFromDisk delete this repository entirely from the disk
+func (repo *GitRepo) EraseFromDisk() error {
+ err := repo.Close()
+ if err != nil {
+ return err
+ }
+
+ path := filepath.Clean(strings.TrimSuffix(repo.path, string(filepath.Separator)+".git"))
+
+ // fmt.Println("Cleaning repo:", path)
+ return os.RemoveAll(path)
+}
diff --git a/repository/git_testing.go b/repository/git_testing.go
index 874cc86c..2168d53e 100644
--- a/repository/git_testing.go
+++ b/repository/git_testing.go
@@ -48,14 +48,12 @@ func SetupReposAndRemote() (repoA, repoB, remote TestedRepo) {
repoB = CreateGoGitTestRepo(false)
remote = CreateGoGitTestRepo(true)
- remoteAddr := "file://" + remote.GetPath()
-
- err := repoA.AddRemote("origin", remoteAddr)
+ err := repoA.AddRemote("origin", remote.GetLocalRemote())
if err != nil {
log.Fatal(err)
}
- err = repoB.AddRemote("origin", remoteAddr)
+ err = repoB.AddRemote("origin", remote.GetLocalRemote())
if err != nil {
log.Fatal(err)
}
diff --git a/repository/gogit.go b/repository/gogit.go
index c59409d4..bdac259d 100644
--- a/repository/gogit.go
+++ b/repository/gogit.go
@@ -6,13 +6,15 @@ import (
"io/ioutil"
"os"
"os/exec"
- stdpath "path"
"path/filepath"
"sort"
"strings"
"sync"
"time"
+ "github.com/blevesearch/bleve"
+ "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"
"github.com/go-git/go-git/v5/plumbing"
@@ -23,6 +25,7 @@ import (
)
var _ ClockedRepo = &GoGitRepo{}
+var _ TestedRepo = &GoGitRepo{}
type GoGitRepo struct {
r *gogit.Repository
@@ -31,10 +34,15 @@ type GoGitRepo struct {
clocksMutex sync.Mutex
clocks map[string]lamport.Clock
- keyring Keyring
+ indexesMutex sync.Mutex
+ indexes map[string]bleve.Index
+
+ keyring Keyring
+ localStorage billy.Filesystem
}
-func NewGoGitRepo(path string, clockLoaders []ClockLoader) (*GoGitRepo, error) {
+// OpenGoGitRepo open an already existing repo at the given path
+func OpenGoGitRepo(path string, clockLoaders []ClockLoader) (*GoGitRepo, error) {
path, err := detectGitPath(path)
if err != nil {
return nil, err
@@ -51,10 +59,12 @@ func NewGoGitRepo(path string, clockLoaders []ClockLoader) (*GoGitRepo, error) {
}
repo := &GoGitRepo{
- r: r,
- path: path,
- clocks: make(map[string]lamport.Clock),
- keyring: k,
+ r: r,
+ path: path,
+ clocks: make(map[string]lamport.Clock),
+ indexes: make(map[string]bleve.Index),
+ keyring: k,
+ localStorage: osfs.New(filepath.Join(path, "git-bug")),
}
for _, loader := range clockLoaders {
@@ -76,6 +86,50 @@ func NewGoGitRepo(path string, clockLoaders []ClockLoader) (*GoGitRepo, error) {
return repo, nil
}
+// InitGoGitRepo create a new empty git repo at the given path
+func InitGoGitRepo(path string) (*GoGitRepo, error) {
+ r, err := gogit.PlainInit(path, false)
+ if err != nil {
+ return nil, err
+ }
+
+ k, err := defaultKeyring()
+ if err != nil {
+ return nil, err
+ }
+
+ return &GoGitRepo{
+ r: r,
+ path: filepath.Join(path, ".git"),
+ clocks: make(map[string]lamport.Clock),
+ indexes: make(map[string]bleve.Index),
+ keyring: k,
+ localStorage: osfs.New(filepath.Join(path, ".git", "git-bug")),
+ }, nil
+}
+
+// InitBareGoGitRepo create a new --bare empty git repo at the given path
+func InitBareGoGitRepo(path string) (*GoGitRepo, error) {
+ r, err := gogit.PlainInit(path, true)
+ if err != nil {
+ return nil, err
+ }
+
+ k, err := defaultKeyring()
+ if err != nil {
+ return nil, err
+ }
+
+ return &GoGitRepo{
+ r: r,
+ path: path,
+ clocks: make(map[string]lamport.Clock),
+ indexes: make(map[string]bleve.Index),
+ keyring: k,
+ localStorage: osfs.New(filepath.Join(path, "git-bug")),
+ }, nil
+}
+
func detectGitPath(path string) (string, error) {
// normalize the path
path, err := filepath.Abs(path)
@@ -84,12 +138,12 @@ func detectGitPath(path string) (string, error) {
}
for {
- fi, err := os.Stat(stdpath.Join(path, ".git"))
+ fi, err := os.Stat(filepath.Join(path, ".git"))
if err == nil {
if !fi.IsDir() {
return "", fmt.Errorf(".git exist but is not a directory")
}
- return stdpath.Join(path, ".git"), nil
+ return filepath.Join(path, ".git"), nil
}
if !os.IsNotExist(err) {
// unknown error
@@ -117,7 +171,7 @@ func isGitDir(path string) (bool, error) {
markers := []string{"HEAD", "objects", "refs"}
for _, marker := range markers {
- _, err := os.Stat(stdpath.Join(path, marker))
+ _, err := os.Stat(filepath.Join(path, marker))
if err == nil {
continue
}
@@ -132,44 +186,15 @@ func isGitDir(path string) (bool, error) {
return true, nil
}
-// InitGoGitRepo create a new empty git repo at the given path
-func InitGoGitRepo(path string) (*GoGitRepo, error) {
- r, err := gogit.PlainInit(path, false)
- if err != nil {
- return nil, err
- }
-
- k, err := defaultKeyring()
- if err != nil {
- return nil, err
- }
-
- return &GoGitRepo{
- r: r,
- path: path + "/.git",
- clocks: make(map[string]lamport.Clock),
- keyring: k,
- }, nil
-}
-
-// InitBareGoGitRepo create a new --bare empty git repo at the given path
-func InitBareGoGitRepo(path string) (*GoGitRepo, error) {
- r, err := gogit.PlainInit(path, true)
- if err != nil {
- return nil, err
- }
-
- k, err := defaultKeyring()
- if err != nil {
- return nil, err
+func (repo *GoGitRepo) Close() error {
+ var firstErr error
+ for _, index := range repo.indexes {
+ err := index.Close()
+ if err != nil && firstErr == nil {
+ firstErr = err
+ }
}
-
- return &GoGitRepo{
- r: r,
- path: path,
- clocks: make(map[string]lamport.Clock),
- keyring: k,
- }, nil
+ return firstErr
}
// LocalConfig give access to the repository scoped configuration
@@ -179,10 +204,7 @@ func (repo *GoGitRepo) LocalConfig() Config {
// GlobalConfig give access to the global scoped configuration
func (repo *GoGitRepo) GlobalConfig() Config {
- // TODO: replace that with go-git native implementation once it's supported
- // see: https://github.com/go-git/go-git
- // see: https://github.com/src-d/go-git/issues/760
- return newGoGitGlobalConfig(repo.r)
+ return newGoGitGlobalConfig()
}
// AnyConfig give access to a merged local/global configuration
@@ -195,11 +217,6 @@ func (repo *GoGitRepo) Keyring() Keyring {
return repo.keyring
}
-// GetPath returns the path to the repo.
-func (repo *GoGitRepo) GetPath() string {
- return repo.path
-}
-
// GetUserName returns the name the the user has used to configure git
func (repo *GoGitRepo) GetUserName() (string, error) {
return repo.AnyConfig().ReadString("user.name")
@@ -270,6 +287,69 @@ func (repo *GoGitRepo) GetRemotes() (map[string]string, error) {
return result, nil
}
+// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
+func (repo *GoGitRepo) LocalStorage() billy.Filesystem {
+ return repo.localStorage
+}
+
+// GetBleveIndex return a bleve.Index that can be used to index documents
+func (repo *GoGitRepo) GetBleveIndex(name string) (bleve.Index, error) {
+ repo.indexesMutex.Lock()
+ defer repo.indexesMutex.Unlock()
+
+ if index, ok := repo.indexes[name]; ok {
+ return index, nil
+ }
+
+ path := filepath.Join(repo.path, "git-bug", "indexes", name)
+
+ index, err := bleve.Open(path)
+ if err == nil {
+ repo.indexes[name] = index
+ return index, nil
+ }
+
+ err = os.MkdirAll(path, os.ModePerm)
+ if err != nil {
+ return nil, err
+ }
+
+ mapping := bleve.NewIndexMapping()
+ mapping.DefaultAnalyzer = "en"
+
+ index, err = bleve.New(path, mapping)
+ if err != nil {
+ return nil, err
+ }
+
+ repo.indexes[name] = index
+
+ return index, nil
+}
+
+// ClearBleveIndex will wipe the given index
+func (repo *GoGitRepo) ClearBleveIndex(name string) error {
+ repo.indexesMutex.Lock()
+ defer repo.indexesMutex.Unlock()
+
+ path := filepath.Join(repo.path, "indexes", name)
+
+ err := os.RemoveAll(path)
+ if err != nil {
+ return err
+ }
+
+ if index, ok := repo.indexes[name]; ok {
+ err = index.Close()
+ if err != nil {
+ return err
+ }
+ delete(repo.indexes, name)
+ }
+
+ return nil
+}
+
// FetchRefs fetch git refs from a remote
func (repo *GoGitRepo) FetchRefs(remote string, refSpec string) (string, error) {
buf := bytes.NewBuffer(nil)
@@ -600,6 +680,9 @@ func (repo *GoGitRepo) ListCommits(ref string) ([]Hash, error) {
// GetOrCreateClock return a Lamport clock stored in the Repo.
// If the clock doesn't exist, it's created.
func (repo *GoGitRepo) GetOrCreateClock(name string) (lamport.Clock, error) {
+ repo.clocksMutex.Lock()
+ defer repo.clocksMutex.Unlock()
+
c, err := repo.getClock(name)
if err == nil {
return c, nil
@@ -608,12 +691,7 @@ func (repo *GoGitRepo) GetOrCreateClock(name string) (lamport.Clock, error) {
return nil, err
}
- repo.clocksMutex.Lock()
- defer repo.clocksMutex.Unlock()
-
- p := stdpath.Join(repo.path, clockPath, name+"-clock")
-
- c, err = lamport.NewPersistedClock(p)
+ c, err = lamport.NewPersistedClock(repo.localStorage, name+"-clock")
if err != nil {
return nil, err
}
@@ -623,16 +701,11 @@ func (repo *GoGitRepo) GetOrCreateClock(name string) (lamport.Clock, error) {
}
func (repo *GoGitRepo) getClock(name string) (lamport.Clock, error) {
- repo.clocksMutex.Lock()
- defer repo.clocksMutex.Unlock()
-
if c, ok := repo.clocks[name]; ok {
return c, nil
}
- p := stdpath.Join(repo.path, clockPath, name+"-clock")
-
- c, err := lamport.LoadPersistedClock(p)
+ c, err := lamport.LoadPersistedClock(repo.localStorage, name+"-clock")
if err == nil {
repo.clocks[name] = c
return c, nil
@@ -653,3 +726,21 @@ func (repo *GoGitRepo) AddRemote(name string, url string) error {
return err
}
+
+// GetLocalRemote return the URL to use to add this repo as a local remote
+func (repo *GoGitRepo) GetLocalRemote() string {
+ return repo.path
+}
+
+// EraseFromDisk delete this repository entirely from the disk
+func (repo *GoGitRepo) EraseFromDisk() error {
+ err := repo.Close()
+ if err != nil {
+ return err
+ }
+
+ path := filepath.Clean(strings.TrimSuffix(repo.path, string(filepath.Separator)+".git"))
+
+ // fmt.Println("Cleaning repo:", path)
+ return os.RemoveAll(path)
+}
diff --git a/repository/gogit_config.go b/repository/gogit_config.go
index 2f9a4cc3..ba61adca 100644
--- a/repository/gogit_config.go
+++ b/repository/gogit_config.go
@@ -24,7 +24,11 @@ func newGoGitLocalConfig(repo *gogit.Repository) *goGitConfig {
}
}
-func newGoGitGlobalConfig(repo *gogit.Repository) *goGitConfig {
+func newGoGitGlobalConfig() *goGitConfig {
+ // TODO: replace that with go-git native implementation once it's supported
+ // see: https://github.com/go-git/go-git
+ // see: https://github.com/src-d/go-git/issues/760
+
return &goGitConfig{
ConfigRead: &goGitConfigReader{getConfig: func() (*config.Config, error) {
return config.LoadConfig(config.GlobalScope)
diff --git a/repository/gogit_test.go b/repository/gogit_test.go
index fba990d3..a2bb49b9 100644
--- a/repository/gogit_test.go
+++ b/repository/gogit_test.go
@@ -19,7 +19,7 @@ func TestNewGoGitRepo(t *testing.T) {
_, err = InitGoGitRepo(plainRoot)
require.NoError(t, err)
- plainGitDir := path.Join(plainRoot, ".git")
+ plainGitDir := filepath.Join(plainRoot, ".git")
// Bare
bareRoot, err := ioutil.TempDir("", "")
@@ -52,13 +52,13 @@ func TestNewGoGitRepo(t *testing.T) {
}
for i, tc := range tests {
- r, err := NewGoGitRepo(tc.inPath, nil)
+ r, err := OpenGoGitRepo(tc.inPath, nil)
if tc.err {
require.Error(t, err, i)
} else {
require.NoError(t, err, i)
- assert.Equal(t, filepath.ToSlash(tc.outPath), filepath.ToSlash(r.GetPath()), i)
+ assert.Equal(t, filepath.ToSlash(tc.outPath), filepath.ToSlash(r.path), i)
}
}
}
diff --git a/repository/gogit_testing.go b/repository/gogit_testing.go
index f20ff6be..a8bff41e 100644
--- a/repository/gogit_testing.go
+++ b/repository/gogit_testing.go
@@ -42,14 +42,12 @@ func SetupGoGitReposAndRemote() (repoA, repoB, remote TestedRepo) {
repoB = CreateGoGitTestRepo(false)
remote = CreateGoGitTestRepo(true)
- remoteAddr := "file://" + remote.GetPath()
-
- err := repoA.AddRemote("origin", remoteAddr)
+ err := repoA.AddRemote("origin", remote.GetLocalRemote())
if err != nil {
log.Fatal(err)
}
- err = repoB.AddRemote("origin", remoteAddr)
+ err = repoB.AddRemote("origin", remote.GetLocalRemote())
if err != nil {
log.Fatal(err)
}
diff --git a/repository/keyring.go b/repository/keyring.go
index f690b0b3..4cb3c9ff 100644
--- a/repository/keyring.go
+++ b/repository/keyring.go
@@ -2,7 +2,7 @@ package repository
import (
"os"
- "path"
+ "path/filepath"
"github.com/99designs/keyring"
)
@@ -38,7 +38,7 @@ func defaultKeyring() (Keyring, error) {
ServiceName: "git-bug",
// Fallback encrypted file
- FileDir: path.Join(ucd, "git-bug", "keyring"),
+ FileDir: filepath.Join(ucd, "git-bug", "keyring"),
// As we write the file in the user's config directory, this file should already be protected by the OS against
// other user's access. We actually don't terribly need to protect it further and a password prompt across all
// UI's would be a pain. Therefore we use here a constant password so the file will be unreadable by generic file
diff --git a/repository/mock_repo.go b/repository/mock_repo.go
index 628939aa..8a1724ef 100644
--- a/repository/mock_repo.go
+++ b/repository/mock_repo.go
@@ -4,8 +4,12 @@ import (
"crypto/sha1"
"fmt"
"strings"
+ "sync"
"github.com/99designs/keyring"
+ "github.com/blevesearch/bleve"
+ "github.com/go-git/go-billy/v5"
+ "github.com/go-git/go-billy/v5/memfs"
"github.com/MichaelMure/git-bug/util/lamport"
)
@@ -18,15 +22,21 @@ type mockRepoForTest struct {
*mockRepoConfig
*mockRepoKeyring
*mockRepoCommon
+ *mockRepoStorage
+ *mockRepoBleve
*mockRepoData
*mockRepoClock
}
+func (m *mockRepoForTest) Close() error { return nil }
+
func NewMockRepoForTest() *mockRepoForTest {
return &mockRepoForTest{
mockRepoConfig: NewMockRepoConfig(),
mockRepoKeyring: NewMockRepoKeyring(),
mockRepoCommon: NewMockRepoCommon(),
+ mockRepoStorage: NewMockRepoStorage(),
+ mockRepoBleve: newMockRepoBleve(),
mockRepoData: NewMockRepoData(),
mockRepoClock: NewMockRepoClock(),
}
@@ -86,11 +96,6 @@ func NewMockRepoCommon() *mockRepoCommon {
return &mockRepoCommon{}
}
-// GetPath returns the path to the repo.
-func (r *mockRepoCommon) GetPath() string {
- return "~/mockRepo/"
-}
-
func (r *mockRepoCommon) GetUserName() (string, error) {
return "René Descartes", nil
}
@@ -112,6 +117,62 @@ func (r *mockRepoCommon) GetRemotes() (map[string]string, error) {
}, nil
}
+var _ RepoStorage = &mockRepoStorage{}
+
+type mockRepoStorage struct {
+ localFs billy.Filesystem
+}
+
+func NewMockRepoStorage() *mockRepoStorage {
+ return &mockRepoStorage{localFs: memfs.New()}
+}
+
+func (m *mockRepoStorage) LocalStorage() billy.Filesystem {
+ return m.localFs
+}
+
+var _ RepoBleve = &mockRepoBleve{}
+
+type mockRepoBleve struct {
+ indexesMutex sync.Mutex
+ indexes map[string]bleve.Index
+}
+
+func newMockRepoBleve() *mockRepoBleve {
+ return &mockRepoBleve{
+ indexes: make(map[string]bleve.Index),
+ }
+}
+
+func (m *mockRepoBleve) GetBleveIndex(name string) (bleve.Index, error) {
+ m.indexesMutex.Lock()
+ defer m.indexesMutex.Unlock()
+
+ if index, ok := m.indexes[name]; ok {
+ return index, nil
+ }
+
+ mapping := bleve.NewIndexMapping()
+ mapping.DefaultAnalyzer = "en"
+
+ index, err := bleve.NewMemOnly(mapping)
+ if err != nil {
+ return nil, err
+ }
+
+ m.indexes[name] = index
+
+ return index, nil
+}
+
+func (m *mockRepoBleve) ClearBleveIndex(name string) error {
+ m.indexesMutex.Lock()
+ defer m.indexesMutex.Unlock()
+
+ delete(m.indexes, name)
+ return nil
+}
+
var _ RepoData = &mockRepoData{}
type commit struct {
@@ -314,7 +375,17 @@ func (r *mockRepoData) AddRemote(name string, url string) error {
panic("implement me")
}
+func (m mockRepoForTest) GetLocalRemote() string {
+ panic("implement me")
+}
+
+func (m mockRepoForTest) EraseFromDisk() error {
+ // nothing to do
+ return nil
+}
+
type mockRepoClock struct {
+ mu sync.Mutex
clocks map[string]lamport.Clock
}
@@ -325,6 +396,9 @@ func NewMockRepoClock() *mockRepoClock {
}
func (r *mockRepoClock) GetOrCreateClock(name string) (lamport.Clock, error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
if c, ok := r.clocks[name]; ok {
return c, nil
}
diff --git a/repository/repo.go b/repository/repo.go
index 4b45a1c5..eb9296d4 100644
--- a/repository/repo.go
+++ b/repository/repo.go
@@ -4,6 +4,9 @@ package repository
import (
"errors"
+ "github.com/blevesearch/bleve"
+ "github.com/go-git/go-billy/v5"
+
"github.com/MichaelMure/git-bug/util/lamport"
)
@@ -20,6 +23,15 @@ type Repo interface {
RepoKeyring
RepoCommon
RepoData
+ RepoStorage
+ RepoBleve
+
+ Close() error
+}
+
+type RepoCommonStorage interface {
+ RepoCommon
+ RepoStorage
}
// ClockedRepo is a Repo that also has Lamport clocks
@@ -48,9 +60,6 @@ type RepoKeyring interface {
// RepoCommon represent the common function the we want all the repo to implement
type RepoCommon interface {
- // GetPath returns the path to the repo.
- GetPath() string
-
// GetUserName returns the name the the user has used to configure git
GetUserName() (string, error)
@@ -64,6 +73,21 @@ type RepoCommon interface {
GetRemotes() (map[string]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
+}
+
+// RepoBleve give access to Bleve to implement full-text search indexes.
+type RepoBleve interface {
+ // GetBleveIndex return a bleve.Index that can be used to index documents
+ GetBleveIndex(name string) (bleve.Index, error)
+
+ // ClearBleveIndex will wipe the given index
+ ClearBleveIndex(name string) error
+}
+
// RepoData give access to the git data storage
type RepoData interface {
// FetchRefs fetch git refs from a remote
@@ -145,4 +169,10 @@ type TestedRepo interface {
type repoTest interface {
// AddRemote add a new remote to the repository
AddRemote(name string, url string) error
+
+ // GetLocalRemote return the URL to use to add this repo as a local remote
+ GetLocalRemote() string
+
+ // EraseFromDisk delete this repository entirely from the disk
+ EraseFromDisk() error
}
diff --git a/repository/repo_testing.go b/repository/repo_testing.go
index 41b3609e..c0e1fa79 100644
--- a/repository/repo_testing.go
+++ b/repository/repo_testing.go
@@ -3,8 +3,6 @@ package repository
import (
"log"
"math/rand"
- "os"
- "strings"
"testing"
"github.com/stretchr/testify/require"
@@ -15,25 +13,13 @@ import (
func CleanupTestRepos(repos ...Repo) {
var firstErr error
for _, repo := range repos {
- path := repo.GetPath()
- if strings.HasSuffix(path, "/.git") {
- // for a normal repository (not --bare), we want to remove everything
- // including the parent directory where files are checked out
- path = strings.TrimSuffix(path, "/.git")
-
- // Testing non-bare repo should also check path is
- // only .git (i.e. ./.git), but doing so, we should
- // try to remove the current directory and hav some
- // trouble. In the present case, this case should not
- // occur.
- // TODO consider warning or error when path == ".git"
- }
- // fmt.Println("Cleaning repo:", path)
- err := os.RemoveAll(path)
- if err != nil {
- log.Println(err)
- if firstErr == nil {
- firstErr = err
+ if repo, ok := repo.(TestedRepo); ok {
+ err := repo.EraseFromDisk()
+ if err != nil {
+ log.Println(err)
+ if firstErr == nil {
+ firstErr = err
+ }
}
}
}
diff --git a/termui/bug_table.go b/termui/bug_table.go
index d3a187db..94185a29 100644
--- a/termui/bug_table.go
+++ b/termui/bug_table.go
@@ -237,7 +237,11 @@ func (bt *bugTable) disable(g *gocui.Gui) error {
}
func (bt *bugTable) paginate(max int) error {
- bt.allIds = bt.repo.QueryBugs(bt.query)
+ var err error
+ bt.allIds, err = bt.repo.QueryBugs(bt.query)
+ if err != nil {
+ return err
+ }
return bt.doPaginate(max)
}
diff --git a/util/lamport/persisted_clock.go b/util/lamport/persisted_clock.go
index e70b01ef..b9246f73 100644
--- a/util/lamport/persisted_clock.go
+++ b/util/lamport/persisted_clock.go
@@ -5,30 +5,28 @@ import (
"fmt"
"io/ioutil"
"os"
- "path/filepath"
+
+ "github.com/go-git/go-billy/v5"
+ "github.com/go-git/go-billy/v5/util"
)
var ErrClockNotExist = errors.New("clock doesn't exist")
type PersistedClock struct {
*MemClock
+ root billy.Filesystem
filePath string
}
// NewPersistedClock create a new persisted Lamport clock
-func NewPersistedClock(filePath string) (*PersistedClock, error) {
+func NewPersistedClock(root billy.Filesystem, filePath string) (*PersistedClock, error) {
clock := &PersistedClock{
MemClock: NewMemClock(),
+ root: root,
filePath: filePath,
}
- dir := filepath.Dir(filePath)
- err := os.MkdirAll(dir, 0777)
- if err != nil {
- return nil, err
- }
-
- err = clock.Write()
+ err := clock.Write()
if err != nil {
return nil, err
}
@@ -37,8 +35,9 @@ func NewPersistedClock(filePath string) (*PersistedClock, error) {
}
// LoadPersistedClock load a persisted Lamport clock from a file
-func LoadPersistedClock(filePath string) (*PersistedClock, error) {
+func LoadPersistedClock(root billy.Filesystem, filePath string) (*PersistedClock, error) {
clock := &PersistedClock{
+ root: root,
filePath: filePath,
}
@@ -71,13 +70,19 @@ func (pc *PersistedClock) Witness(time Time) error {
}
func (pc *PersistedClock) read() error {
- content, err := ioutil.ReadFile(pc.filePath)
+ f, err := pc.root.Open(pc.filePath)
if os.IsNotExist(err) {
return ErrClockNotExist
}
if err != nil {
return err
}
+ defer f.Close()
+
+ content, err := ioutil.ReadAll(f)
+ if err != nil {
+ return err
+ }
var value uint64
n, err := fmt.Sscanf(string(content), "%d", &value)
@@ -96,5 +101,5 @@ func (pc *PersistedClock) read() error {
func (pc *PersistedClock) Write() error {
data := []byte(fmt.Sprintf("%d", pc.counter))
- return ioutil.WriteFile(pc.filePath, data, 0644)
+ return util.WriteFile(pc.root, pc.filePath, data, 0644)
}
diff --git a/util/lamport/persisted_clock_test.go b/util/lamport/persisted_clock_test.go
index aacec3bf..9ef690da 100644
--- a/util/lamport/persisted_clock_test.go
+++ b/util/lamport/persisted_clock_test.go
@@ -1,18 +1,16 @@
package lamport
import (
- "io/ioutil"
- "path"
"testing"
+ "github.com/go-git/go-billy/v5/memfs"
"github.com/stretchr/testify/require"
)
func TestPersistedClock(t *testing.T) {
- dir, err := ioutil.TempDir("", "")
- require.NoError(t, err)
+ root := memfs.New()
- c, err := NewPersistedClock(path.Join(dir, "test-clock"))
+ c, err := NewPersistedClock(root, "test-clock")
require.NoError(t, err)
testClock(t, c)