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--bug/operation_pack.go5
-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--entity/err.go26
-rw-r--r--go.mod11
-rw-r--r--go.sum42
-rw-r--r--identity/identity_test.go4
-rw-r--r--identity/version.go8
-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
-rw-r--r--webui/package-lock.json6
-rw-r--r--webui/package.json2
-rw-r--r--webui/src/App.tsx2
-rw-r--r--webui/src/pages/bug/CommentInput.tsx96
-rw-r--r--webui/src/pages/list/ListQuery.tsx57
-rw-r--r--webui/src/pages/new/NewBug.graphql7
-rw-r--r--webui/src/pages/new/NewBugPage.tsx121
43 files changed, 955 insertions, 345 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/bug/operation_pack.go b/bug/operation_pack.go
index 0bd3fb7d..1a8ef0db 100644
--- a/bug/operation_pack.go
+++ b/bug/operation_pack.go
@@ -6,6 +6,7 @@ import (
"github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
)
@@ -47,10 +48,10 @@ func (opp *OperationPack) UnmarshalJSON(data []byte) error {
}
if aux.Version < formatVersion {
- return fmt.Errorf("outdated repository format, please use https://github.com/MichaelMure/git-bug-migration to upgrade")
+ return entity.NewErrOldFormatVersion(aux.Version)
}
if aux.Version > formatVersion {
- return fmt.Errorf("your version of git-bug is too old for this repository (version %v), please upgrade to the latest version", aux.Version)
+ return entity.NewErrNewFormatVersion(aux.Version)
}
for _, raw := range aux.Operations {
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/entity/err.go b/entity/err.go
index 7d6c662e..90304d03 100644
--- a/entity/err.go
+++ b/entity/err.go
@@ -30,3 +30,29 @@ func IsErrMultipleMatch(err error) bool {
_, ok := err.(*ErrMultipleMatch)
return ok
}
+
+// ErrOldFormatVersion indicate that the read data has a too old format.
+type ErrOldFormatVersion struct {
+ formatVersion uint
+}
+
+func NewErrOldFormatVersion(formatVersion uint) *ErrOldFormatVersion {
+ return &ErrOldFormatVersion{formatVersion: formatVersion}
+}
+
+func (e ErrOldFormatVersion) Error() string {
+ return fmt.Sprintf("outdated repository format %v, please use https://github.com/MichaelMure/git-bug-migration to upgrade", e.formatVersion)
+}
+
+// ErrNewFormatVersion indicate that the read data is too new for this software.
+type ErrNewFormatVersion struct {
+ formatVersion uint
+}
+
+func NewErrNewFormatVersion(formatVersion uint) *ErrNewFormatVersion {
+ return &ErrNewFormatVersion{formatVersion: formatVersion}
+}
+
+func (e ErrNewFormatVersion) Error() string {
+ return fmt.Sprintf("your version of git-bug is too old for this repository (version %v), please upgrade to the latest version", e.formatVersion)
+}
diff --git a/go.mod b/go.mod
index a6496b45..d67bea3f 100644
--- a/go.mod
+++ b/go.mod
@@ -5,11 +5,11 @@ go 1.13
require (
github.com/99designs/gqlgen v0.10.3-0.20200209012558-b7a58a1c0e4b
github.com/99designs/keyring v1.1.6
- github.com/MichaelMure/go-term-text v0.2.9
+ github.com/MichaelMure/go-term-text v0.2.10
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195
github.com/awesome-gocui/gocui v0.6.1-0.20191115151952-a34ffb055986
github.com/blang/semver v3.5.1+incompatible
- github.com/blevesearch/bleve v1.0.13
+ github.com/blevesearch/bleve v1.0.14
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9
github.com/corpix/uarand v0.1.1 // indirect
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
@@ -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
@@ -36,17 +37,17 @@ require (
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e
github.com/spf13/cobra v1.1.1
- github.com/stretchr/testify v1.6.1
+ github.com/stretchr/testify v1.7.0
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
github.com/vektah/gqlparser v1.3.1
- github.com/xanzy/go-gitlab v0.39.0
+ github.com/xanzy/go-gitlab v0.40.1
github.com/xanzy/ssh-agent v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/net v0.0.0-20201024042810-be3efd7ff127 // indirect
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
golang.org/x/sys v0.0.0-20201020230747-6e5568b54d1a // indirect
- golang.org/x/text v0.3.4
+ golang.org/x/text v0.3.5
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
google.golang.org/appengine v1.6.7 // indirect
)
diff --git a/go.sum b/go.sum
index d22c5f3a..082484c1 100644
--- a/go.sum
+++ b/go.sum
@@ -43,6 +43,7 @@ github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
github.com/MichaelMure/go-term-text v0.2.9 h1:jUxInT3rDhl4WoJgLnmMS3hR79zigyJS1TqKFDTI6xE=
github.com/MichaelMure/go-term-text v0.2.9/go.mod h1:2QSU/Nn2u41Tqoar+90RlYuhjngJPYgod7evnsYwkWc=
+github.com/MichaelMure/go-term-text v0.2.10/go.mod h1:DrWFodEEZsSgK1PQY9dqTn+pw3zGeYDmVF5PA8ECZhs=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@@ -50,15 +51,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 +62,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=
@@ -84,8 +78,12 @@ github.com/blevesearch/bleve v1.0.10/go.mod h1:KHAOH5HuVGn9fo+dN5TkqcA1HcuOQ89go
github.com/blevesearch/bleve v1.0.12/go.mod h1:G0ErXWdIrUSYZLPoMpS9Z3saTnTsk4ebhPsVv/+0nxk=
github.com/blevesearch/bleve v1.0.13 h1:NtqdA+2UL715y2/9Epg9Ie9uspNcilGMYNM+tT+HfAo=
github.com/blevesearch/bleve v1.0.13/go.mod h1:3y+16vR4Cwtis/bOGCt7r+CHKB2/ewizEqKBUycXomA=
+github.com/blevesearch/bleve v1.0.14 h1:Q8r+fHTt35jtGXJUM0ULwM3Tzg+MRfyai4ZkWDy2xO4=
+github.com/blevesearch/bleve v1.0.14/go.mod h1:e/LJTr+E7EaoVdkQZTfoz7dt4KoDNvDbLb8MSKuNTLQ=
github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040 h1:SjYVcfJVZoCfBlg+fkaq2eoZHTf5HaJfaTeTkOtyfHQ=
github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040/go.mod h1:WH+MU2F4T0VmSdaPX+Wu5GYoZBrYWdOZWSjzvYcDmqQ=
+github.com/blevesearch/blevex v1.0.0/go.mod h1:2rNVqoG2BZI8t1/P1awgTKnGlx5MP9ZbtEciQaNhswc=
+github.com/blevesearch/cld2 v0.0.0-20200327141045-8b5f551d37f5/go.mod h1:PN0QNTLs9+j1bKy3d/GB/59wsNBFC4sWLWG3k69lWbc=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/mmap-go v1.0.2 h1:JtMHb+FgQCTTYIhtMvimw15dJwu1Y5lrZDMOFXVWPk0=
@@ -99,24 +97,34 @@ github.com/blevesearch/zap/v11 v11.0.10/go.mod h1:BdqdgKy6u0Jgw/CqrMfP2Gue/Eldcf
github.com/blevesearch/zap/v11 v11.0.12/go.mod h1:JLfFhc8DWP01zMG/6VwEY2eAnlJsTN1vDE4S0rC5Y78=
github.com/blevesearch/zap/v11 v11.0.13 h1:NDvmjAyeEQsBbPElubVPqrBtSDOftXYwxkHeZfflU4A=
github.com/blevesearch/zap/v11 v11.0.13/go.mod h1:qKkNigeXbxZwym02wsxoQpbme1DgAwTvRlT/beIGfTM=
+github.com/blevesearch/zap/v11 v11.0.14 h1:IrDAvtlzDylh6H2QCmS0OGcN9Hpf6mISJlfKjcwJs7k=
+github.com/blevesearch/zap/v11 v11.0.14/go.mod h1:MUEZh6VHGXv1PKx3WnCbdP404LGG2IZVa/L66pyFwnY=
github.com/blevesearch/zap/v12 v12.0.10 h1:T1/GXNBxC9eetfuMwCM5RLWXeharSMyAdNEdXVtBuHA=
github.com/blevesearch/zap/v12 v12.0.10/go.mod h1:QtKkjpmV/sVFEnKSaIWPXZJAaekL97TrTV3ImhNx+nw=
github.com/blevesearch/zap/v12 v12.0.12/go.mod h1:1HrB4hhPfI8u8x4SPYbluhb8xhflpPvvj8EcWImNnJY=
github.com/blevesearch/zap/v12 v12.0.13 h1:05Ebdmv2tRTUytypG4DlOIHLLw995DtVV0Zl3YwwDew=
github.com/blevesearch/zap/v12 v12.0.13/go.mod h1:0RTeU1uiLqsPoybUn6G/Zgy6ntyFySL3uWg89NgX3WU=
+github.com/blevesearch/zap/v12 v12.0.14 h1:2o9iRtl1xaRjsJ1xcqTyLX414qPAwykHNV7wNVmbp3w=
+github.com/blevesearch/zap/v12 v12.0.14/go.mod h1:rOnuZOiMKPQj18AEKEHJxuI14236tTQ1ZJz4PAnWlUg=
github.com/blevesearch/zap/v13 v13.0.2 h1:quhI5OVFX33dhPpUW+nLyXGpu7QT8qTgzu6qA/fRRXM=
github.com/blevesearch/zap/v13 v13.0.2/go.mod h1:/9QLKla8/8mloJvQQutPhB+tw6y35urvKeAFeun2JGA=
github.com/blevesearch/zap/v13 v13.0.4/go.mod h1:YdB7UuG7TBWu/1dz9e2SaLp1RKfFfdJx+ulIK5HR1bA=
github.com/blevesearch/zap/v13 v13.0.5 h1:+Gcwl95uei3MgBlJAddBFRv9gl+FMNcXpMa7BX3byJw=
github.com/blevesearch/zap/v13 v13.0.5/go.mod h1:HTfWECmzBN7BbdBxdEigpUsD6MOPFOO84tZ0z/g3CnE=
+github.com/blevesearch/zap/v13 v13.0.6 h1:r+VNSVImi9cBhTNNR+Kfl5uiGy8kIbb0JMz/h8r6+O4=
+github.com/blevesearch/zap/v13 v13.0.6/go.mod h1:L89gsjdRKGyGrRN6nCpIScCvvkyxvmeDCwZRcjjPCrw=
github.com/blevesearch/zap/v14 v14.0.1 h1:s8KeqX53Vc4eRaziHsnY2bYUE+8IktWqRL9W5H5VDMY=
github.com/blevesearch/zap/v14 v14.0.1/go.mod h1:Y+tUL9TypMca5+96m7iJb2lpcntETXSeDoI5BBX2tvY=
github.com/blevesearch/zap/v14 v14.0.3/go.mod h1:oObAhcDHw7p1ahiTCqhRkdxdl7UA8qpvX10pSgrTMHc=
github.com/blevesearch/zap/v14 v14.0.4 h1:BnWWkdgmPhK50J9dkBlQrWB4UDa22OMPIUzn1oXcXfY=
github.com/blevesearch/zap/v14 v14.0.4/go.mod h1:sTwuFoe1n/+VtaHNAjY3W5GzHZ5UxFkw1MZ82P/WKpA=
+github.com/blevesearch/zap/v14 v14.0.5 h1:NdcT+81Nvmp2zL+NhwSvGSLh7xNgGL8QRVZ67njR0NU=
+github.com/blevesearch/zap/v14 v14.0.5/go.mod h1:bWe8S7tRrSBTIaZ6cLRbgNH4TUDaC9LZSpRGs85AsGY=
github.com/blevesearch/zap/v15 v15.0.1/go.mod h1:ho0frqAex2ktT9cYFAxQpoQXsxb/KEfdjpx4s49rf/M=
github.com/blevesearch/zap/v15 v15.0.2 h1:7wV4ksnKzBibLaWBolzbxngxdVAUmF7HJ+gMOqkzsdQ=
github.com/blevesearch/zap/v15 v15.0.2/go.mod h1:nfycXPgfbio8l+ZSkXUkhSNjTpp57jZ0/MKa6TigWvM=
+github.com/blevesearch/zap/v15 v15.0.3 h1:Ylj8Oe+mo0P25tr9iLPp33lN6d4qcztGjaIsP51UxaY=
+github.com/blevesearch/zap/v15 v15.0.3/go.mod h1:iuwQrImsh1WjWJ0Ue2kBqY83a0rFtJTqfa9fp1rbVVU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9 h1:a1zrFsLFac2xoM6zG1u72DWJwZG3ayttYLfmLbxVETk=
@@ -178,12 +186,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 +200,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 +293,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=
@@ -314,10 +320,10 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc=
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
+github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpDugJfX+HddPHHg=
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 +339,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 +348,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=
@@ -360,6 +364,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -382,7 +387,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=
@@ -418,6 +422,7 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
@@ -470,9 +475,11 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
+github.com/tebeka/snowball v0.4.2/go.mod h1:4IfL14h1lvwZcp1sfXuuc7/7yCsvVffTWxWxCLfFpYg=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
@@ -487,6 +494,8 @@ github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xanzy/go-gitlab v0.39.0 h1:7aiZ03fJfCdqoHFhsZq/SoVYp2lR91hfYWmiXLOU5Qo=
github.com/xanzy/go-gitlab v0.39.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
+github.com/xanzy/go-gitlab v0.40.1 h1:jHueLh5Inzv20TL5Yki+CaLmyvtw3Yq7blbWx7GmglQ=
+github.com/xanzy/go-gitlab v0.40.1/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
@@ -648,6 +657,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -727,7 +737,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=
@@ -785,7 +794,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/identity/version.go b/identity/version.go
index 73e4d7c7..bbf93575 100644
--- a/identity/version.go
+++ b/identity/version.go
@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/lamport"
"github.com/MichaelMure/git-bug/util/text"
@@ -102,8 +103,11 @@ func (v *Version) UnmarshalJSON(data []byte) error {
return err
}
- if aux.FormatVersion != formatVersion {
- return fmt.Errorf("unknown format version %v", aux.FormatVersion)
+ if aux.FormatVersion < formatVersion {
+ return entity.NewErrOldFormatVersion(aux.FormatVersion)
+ }
+ if aux.FormatVersion > formatVersion {
+ return entity.NewErrNewFormatVersion(aux.FormatVersion)
}
v.time = aux.Time
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)
diff --git a/webui/package-lock.json b/webui/package-lock.json
index 47d5e17d..8809a0ba 100644
--- a/webui/package-lock.json
+++ b/webui/package-lock.json
@@ -17570,9 +17570,9 @@
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
},
"prettier": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz",
- "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
+ "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"dev": true
},
"prettier-linter-helpers": {
diff --git a/webui/package.json b/webui/package.json
index 4f2a8da4..39696a25 100644
--- a/webui/package.json
+++ b/webui/package.json
@@ -38,7 +38,7 @@
"eslint-config-prettier": "^6.12.0",
"eslint-plugin-graphql": "^4.0.0",
"eslint-plugin-prettier": "^3.1.4",
- "prettier": "^2.1.2"
+ "prettier": "^2.2.1"
},
"scripts": {
"start": "npm run generate && react-scripts start",
diff --git a/webui/src/App.tsx b/webui/src/App.tsx
index 16663870..3a5ef025 100644
--- a/webui/src/App.tsx
+++ b/webui/src/App.tsx
@@ -4,12 +4,14 @@ import { Route, Switch } from 'react-router';
import Layout from './layout';
import BugPage from './pages/bug';
import ListPage from './pages/list';
+import NewBugPage from './pages/new/NewBugPage';
export default function App() {
return (
<Layout>
<Switch>
<Route path="/" exact component={ListPage} />
+ <Route path="/new" exact component={NewBugPage} />
<Route path="/bug/:id" exact component={BugPage} />
</Switch>
</Layout>
diff --git a/webui/src/pages/bug/CommentInput.tsx b/webui/src/pages/bug/CommentInput.tsx
new file mode 100644
index 00000000..540a53f7
--- /dev/null
+++ b/webui/src/pages/bug/CommentInput.tsx
@@ -0,0 +1,96 @@
+import React, { useState, useEffect } from 'react';
+
+import Tab from '@material-ui/core/Tab';
+import Tabs from '@material-ui/core/Tabs';
+import TextField from '@material-ui/core/TextField';
+import { makeStyles } from '@material-ui/core/styles';
+
+import Content from 'src/components/Content';
+
+const useStyles = makeStyles((theme) => ({
+ container: {
+ margin: theme.spacing(2, 0),
+ padding: theme.spacing(0, 2, 2, 2),
+ },
+ textarea: {},
+ tabContent: {
+ margin: theme.spacing(2, 0),
+ },
+ preview: {
+ borderBottom: `solid 3px ${theme.palette.grey['200']}`,
+ minHeight: '5rem',
+ },
+ actions: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ },
+}));
+
+type TabPanelProps = {
+ children: React.ReactNode;
+ value: number;
+ index: number;
+} & React.HTMLProps<HTMLDivElement>;
+function TabPanel({ children, value, index, ...props }: TabPanelProps) {
+ return (
+ <div
+ role="tabpanel"
+ hidden={value !== index}
+ id={`editor-tabpanel-${index}`}
+ aria-labelledby={`editor-tab-${index}`}
+ {...props}
+ >
+ {value === index && children}
+ </div>
+ );
+}
+
+const a11yProps = (index: number) => ({
+ id: `editor-tab-${index}`,
+ 'aria-controls': `editor-tabpanel-${index}`,
+});
+
+type Props = {
+ loading: boolean;
+ onChange: (comment: string) => void;
+};
+
+function CommentInput({ loading, onChange }: Props) {
+ const [input, setInput] = useState<string>('');
+ const [tab, setTab] = useState(0);
+ const classes = useStyles();
+
+ useEffect(() => {
+ onChange(input);
+ }, [input, onChange]);
+
+ return (
+ <div>
+ <Tabs value={tab} onChange={(_, t) => setTab(t)}>
+ <Tab label="Write" {...a11yProps(0)} />
+ <Tab label="Preview" {...a11yProps(1)} />
+ </Tabs>
+ <div className={classes.tabContent}>
+ <TabPanel value={tab} index={0}>
+ <TextField
+ fullWidth
+ label="Comment"
+ placeholder="Leave a comment"
+ className={classes.textarea}
+ multiline
+ value={input}
+ variant="filled"
+ rows="4" // TODO: rowsMin support
+ onChange={(e: any) => setInput(e.target.value)}
+ disabled={loading}
+ />
+ </TabPanel>
+ <TabPanel value={tab} index={1} className={classes.preview}>
+ <Content markdown={input} />
+ </TabPanel>
+ </div>
+ </div>
+ );
+}
+
+export default CommentInput;
diff --git a/webui/src/pages/list/ListQuery.tsx b/webui/src/pages/list/ListQuery.tsx
index 7eb6f4c5..424ffac0 100644
--- a/webui/src/pages/list/ListQuery.tsx
+++ b/webui/src/pages/list/ListQuery.tsx
@@ -2,6 +2,7 @@ import { ApolloError } from '@apollo/client';
import React, { useState, useEffect, useRef } from 'react';
import { useLocation, useHistory, Link } from 'react-router-dom';
+import { Button } from '@material-ui/core';
import IconButton from '@material-ui/core/IconButton';
import InputBase from '@material-ui/core/InputBase';
import Paper from '@material-ui/core/Paper';
@@ -40,6 +41,17 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({
alignItems: 'center',
justifyContent: 'space-between',
},
+ filterissueLabel: {
+ fontSize: '14px',
+ fontWeight: 'bold',
+ paddingRight: '12px',
+ },
+ filterissueContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'flex-start',
+ justifyContents: 'left',
+ },
search: {
borderRadius: theme.shape.borderRadius,
borderColor: fade(theme.palette.primary.main, 0.2),
@@ -95,6 +107,13 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({
padding: theme.spacing(2, 3),
},
},
+ greenButton: {
+ backgroundColor: '#2ea44fd9',
+ color: '#fff',
+ '&:hover': {
+ backgroundColor: '#2ea44f',
+ },
+ },
}));
function editParams(
@@ -271,21 +290,29 @@ function ListQuery() {
return (
<Paper className={classes.main}>
<header className={classes.header}>
- <h1>Issues</h1>
- <form onSubmit={formSubmit}>
- <InputBase
- placeholder="Filter"
- value={input}
- onInput={(e: any) => setInput(e.target.value)}
- classes={{
- root: classes.search,
- focused: classes.searchFocused,
- }}
- />
- <button type="submit" hidden>
- Search
- </button>
- </form>
+ <div className="filterissueContainer">
+ <form onSubmit={formSubmit}>
+ <label className={classes.filterissueLabel} htmlFor="issuefilter">
+ Filter
+ </label>
+ <InputBase
+ id="issuefilter"
+ placeholder="Filter"
+ value={input}
+ onInput={(e: any) => setInput(e.target.value)}
+ classes={{
+ root: classes.search,
+ focused: classes.searchFocused,
+ }}
+ />
+ <button type="submit" hidden>
+ Search
+ </button>
+ </form>
+ </div>
+ <Button className={classes.greenButton} variant="contained" href="/new">
+ New issue
+ </Button>
</header>
<FilterToolbar query={query} queryLocation={queryLocation} />
{content}
diff --git a/webui/src/pages/new/NewBug.graphql b/webui/src/pages/new/NewBug.graphql
new file mode 100644
index 00000000..92df016e
--- /dev/null
+++ b/webui/src/pages/new/NewBug.graphql
@@ -0,0 +1,7 @@
+mutation newBug($input: NewBugInput!) {
+ newBug(input: $input) {
+ bug {
+ humanId
+ }
+ }
+} \ No newline at end of file
diff --git a/webui/src/pages/new/NewBugPage.tsx b/webui/src/pages/new/NewBugPage.tsx
new file mode 100644
index 00000000..c8e68e7b
--- /dev/null
+++ b/webui/src/pages/new/NewBugPage.tsx
@@ -0,0 +1,121 @@
+import React, { FormEvent, useState } from 'react';
+
+import { Button } from '@material-ui/core';
+import Paper from '@material-ui/core/Paper';
+import TextField from '@material-ui/core/TextField/TextField';
+import { fade, makeStyles, Theme } from '@material-ui/core/styles';
+
+import CommentInput from '../bug/CommentInput';
+
+import { useNewBugMutation } from './NewBug.generated';
+
+/**
+ * Css in JS styles
+ */
+const useStyles = makeStyles((theme: Theme) => ({
+ main: {
+ maxWidth: 800,
+ margin: 'auto',
+ marginTop: theme.spacing(4),
+ marginBottom: theme.spacing(4),
+ padding: theme.spacing(2),
+ overflow: 'hidden',
+ },
+ titleInput: {
+ borderRadius: theme.shape.borderRadius,
+ borderColor: fade(theme.palette.primary.main, 0.2),
+ borderStyle: 'solid',
+ borderWidth: '1px',
+ backgroundColor: fade(theme.palette.primary.main, 0.05),
+ padding: theme.spacing(0, 0),
+ transition: theme.transitions.create([
+ 'width',
+ 'borderColor',
+ 'backgroundColor',
+ ]),
+ },
+ form: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ actions: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ },
+ gitbugButton: {
+ backgroundColor: '#2ea44fd9',
+ color: '#fff',
+ '&:hover': {
+ backgroundColor: '#2ea44f',
+ },
+ },
+}));
+
+/**
+ * Form to create a new issue
+ */
+function NewBugPage() {
+ const [newBug, { loading, error }] = useNewBugMutation();
+ const [issueTitle, setIssueTitle] = useState('');
+ const [issueComment, setIssueComment] = useState('');
+ const classes = useStyles();
+ let issueTitleInput: any;
+
+ function submitNewIssue(e: FormEvent) {
+ e.preventDefault();
+ if (!isFormValid()) return;
+ console.log('submitNewISsue');
+ console.log('title: ', issueTitle);
+ console.log('comment: ', issueComment);
+ newBug({
+ variables: {
+ input: {
+ title: issueTitle,
+ message: issueComment,
+ },
+ },
+ });
+ issueTitleInput.value = '';
+ }
+
+ function isFormValid() {
+ return issueTitle.length > 0 && issueComment.length > 0 ? true : false;
+ }
+
+ if (loading) return <div>Loading</div>;
+ if (error) return <div>Error</div>;
+
+ return (
+ <Paper className={classes.main}>
+ <form className={classes.form} onSubmit={submitNewIssue}>
+ <TextField
+ inputRef={(node) => {
+ issueTitleInput = node;
+ }}
+ label="Title"
+ className={classes.titleInput}
+ variant="outlined"
+ fullWidth
+ margin="dense"
+ onChange={(event: any) => setIssueTitle(event.target.value)}
+ />
+ <CommentInput
+ loading={false}
+ onChange={(comment: string) => setIssueComment(comment)}
+ />
+ <div className={classes.actions}>
+ <Button
+ className={classes.gitbugButton}
+ variant="contained"
+ type="submit"
+ disabled={isFormValid() ? false : true}
+ >
+ Submit new issue
+ </Button>
+ </div>
+ </form>
+ </Paper>
+ );
+}
+
+export default NewBugPage;