diff --git a/CHANGELOG.md b/CHANGELOG.md index bc40dcc0b..89b7b0c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- git libraries bare or non bare format is automatically detected ([#897](https://github.com/src-d/gitbase/pull/897)) + ## [0.22.0-beta1] - 2019-06-20 ### Added diff --git a/cmd/gitbase/command/server.go b/cmd/gitbase/command/server.go index 1d7ea546a..c3bde49c4 100644 --- a/cmd/gitbase/command/server.go +++ b/cmd/gitbase/command/server.go @@ -59,6 +59,7 @@ type Server struct { Format string `long:"format" default:"git" choice:"git" choice:"siva" description:"Library format"` Bucket int `long:"bucket" default:"2" description:"Bucketing level to use with siva libraries"` Bare bool `long:"bare" description:"Sets the library to use bare git repositories, used only with git format libraries"` + NonBare bool `long:"non-bare" description:"Sets the library to use non bare git repositories, used only with git format libraries"` NonRooted bool `long:"non-rooted" description:"Disables treating siva files as rooted repositories"` Host string `long:"host" default:"localhost" description:"Host where the server is going to listen"` Port int `short:"p" long:"port" default:"3306" description:"Port where the server is going to listen"` @@ -122,6 +123,10 @@ func (c *Server) Execute(args []string) error { logrus.SetLevel(logrus.DebugLevel) } + if c.Bare && c.NonBare { + return fmt.Errorf("cannot use both --bare and --non-bare") + } + // info is the default log level if c.LogLevel != "info" { level, err := logrus.ParseLevel(c.LogLevel) @@ -270,11 +275,19 @@ func (c *Server) addDirectories() error { logrus.Error("at least one folder should be provided.") } + defaultBare := bareAuto + switch { + case c.Bare: + defaultBare = bareOn + case c.NonBare: + defaultBare = bareOff + } + for _, d := range c.Directories { dir := directory{ Path: d, Format: c.Format, - Bare: c.Bare, + Bare: defaultBare, Bucket: c.Bucket, Rooted: !c.NonRooted, } @@ -327,10 +340,15 @@ func (c *Server) addDirectory(d directory) error { return nil } + bare, err := discoverBare(d) + if err != nil { + return err + } + plainOpts := &plain.LocationOptions{ Cache: c.sharedCache, Performance: true, - Bare: d.Bare, + Bare: bare, } if c.plainLibrary == nil { @@ -354,12 +372,20 @@ func (c *Server) addDirectory(d directory) error { return nil } +type bareOpt int + +const ( + bareAuto bareOpt = iota + bareOn + bareOff +) + type directory struct { Path string Format string Bucket int Rooted bool - Bare bool + Bare bareOpt } var ( @@ -405,12 +431,18 @@ func parseDirectory(dir directory) (directory, error) { dir.Format = val case "bare": - if val != "true" && val != "false" { + switch val { + case "true": + dir.Bare = bareOn + case "false": + dir.Bare = bareOff + case "auto": + dir.Bare = bareAuto + default: logrus.Errorf("invalid value in bare, it can only "+ - "be true or false %v", val) + "be true, false, or auto %v", val) return dir, ErrInvalid } - dir.Bare = (val == "true") case "rooted": if val != "true" && val != "false" { @@ -436,3 +468,24 @@ func parseDirectory(dir directory) (directory, error) { return dir, nil } + +func discoverBare(d directory) (bool, error) { + fs := osfs.New(d.Path) + + var bare bool + if d.Bare == bareAuto { + b, err := plain.IsFirstRepositoryBare(fs, "/") + if plain.ErrRepositoriesNotFound.Is(err) { + logrus.WithField("path", d.Path). + Errorf("could not find repositories, assuming non bare format") + } else if err != nil { + return false, err + } + + bare = b + } else { + bare = d.Bare == bareOn + } + + return bare, nil +} diff --git a/cmd/gitbase/command/server_test.go b/cmd/gitbase/command/server_test.go index 5adf3a375..239512628 100644 --- a/cmd/gitbase/command/server_test.go +++ b/cmd/gitbase/command/server_test.go @@ -1,8 +1,13 @@ package command import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" "testing" + fixtures "github.com/src-d/go-git-fixtures" "github.com/stretchr/testify/require" ) @@ -70,14 +75,14 @@ func TestDirectories(t *testing.T) { path: "file:///siva/path?bare=true", expected: directory{ Path: "/siva/path", - Bare: true, + Bare: bareOn, }, }, { path: "file:///siva/path?bare=false", expected: directory{ Path: "/siva/path", - Bare: false, + Bare: bareOff, }, }, { @@ -118,7 +123,7 @@ func TestDirectories(t *testing.T) { expected: directory{ Path: "/siva/path", Format: "git", - Bare: false, + Bare: bareOff, }, }, { @@ -147,3 +152,124 @@ func TestDirectories(t *testing.T) { }) } } + +func TestDiscoverBare(t *testing.T) { + defer func() { + require.NoError(t, fixtures.Clean()) + }() + + tmpDir, err := ioutil.TempDir("", "gitbase") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + + emptyDir := filepath.Join(tmpDir, "empty") + err = os.Mkdir(emptyDir, 0777) + require.NoError(t, err) + + bareDir := filepath.Join(tmpDir, "bare") + err = os.Mkdir(bareDir, 0777) + require.NoError(t, err) + dir := fixtures.ByTag("worktree").One().DotGit().Root() + err = os.Rename(dir, filepath.Join(bareDir, "repo")) + require.NoError(t, err) + + nonBareDir := filepath.Join(tmpDir, "non_bare") + err = os.Mkdir(nonBareDir, 0777) + require.NoError(t, err) + dir = fixtures.ByTag("worktree").One().Worktree().Root() + err = os.Rename(dir, filepath.Join(nonBareDir, "repo")) + require.NoError(t, err) + + tests := []struct { + path string + bare bareOpt + expected bool + err bool + }{ + { + path: "/does/not/exist", + err: true, + }, + { + path: emptyDir, + bare: bareAuto, + expected: false, + }, + { + path: emptyDir, + bare: bareOn, + expected: true, + }, + { + path: emptyDir, + bare: bareOff, + expected: false, + }, + { + path: bareDir, + bare: bareAuto, + expected: true, + }, + { + path: bareDir, + bare: bareOn, + expected: true, + }, + { + path: bareDir, + bare: bareOff, + expected: false, + }, + { + path: nonBareDir, + bare: bareAuto, + expected: false, + }, + { + path: nonBareDir, + bare: bareOn, + expected: true, + }, + { + path: nonBareDir, + bare: bareOff, + expected: false, + }, + } + + for _, test := range tests { + dir := directory{ + Path: test.path, + Bare: test.bare, + } + + t.Run(bareTestName(dir, test.err), func(t *testing.T) { + bare, err := discoverBare(dir) + if test.err { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, test.expected, bare) + }) + } +} + +func bareTestName(d directory, err bool) string { + bare := "" + switch d.Bare { + case bareOn: + bare = "bare" + case bareOff: + bare = "non-bare" + case bareAuto: + bare = "auto" + } + + if err { + bare = "error" + } + + return fmt.Sprintf("%s_%s", d.Path, bare) +} diff --git a/docs/using-gitbase/configuration.md b/docs/using-gitbase/configuration.md index 3d076219d..dea584af6 100644 --- a/docs/using-gitbase/configuration.md +++ b/docs/using-gitbase/configuration.md @@ -92,6 +92,8 @@ Help Options: --bucket= Bucketing level to use with siva libraries (default: 2) --bare Sets the library to use bare git repositories, used only with git format libraries + --non-bare Sets the library to use non bare git repositories, + used only with git format libraries --non-rooted Disables treating siva files as rooted repositories --host= Host where the server is going to listen (default: localhost) diff --git a/docs/using-gitbase/getting-started.md b/docs/using-gitbase/getting-started.md index 6e92ec549..acc1402a1 100644 --- a/docs/using-gitbase/getting-started.md +++ b/docs/using-gitbase/getting-started.md @@ -119,19 +119,20 @@ mysql -q -u root -h 127.0.0.1 --default-auth=mysql_native_password ## Library format specification -By default the directories added to github should contain normal git repositories. If the format of the repositories is different you have two ways to specify it. +By default the directories added to gitbase should contain git repositories and it detects if they are standard or bare format. Each directory added can only contain one type of repository. If you want to specify the format you have two ways to do it: If all the directories are in the same format you can set it globally with these parameters: * `--format`: it can be either `git` for filesystem repositories or `siva` for siva archives * `--bare`: specifies that git archives are bare, can only be used with `git` format +* `--non-bare`: specifies that git archives are standard, can only be used with `git` format * `--bucket`: sets the number of characters to use for bucketing, used with `siva` libraries * `--non-rooted`: disables rooted repositories management in `siva` libraries If you are mixing formats you can specify each directory as a `file://` URL with these parameters: * `format`: can be `git` or `siva` -* `bare`: `true` or `false` +* `bare`: `true`, `false` or `auto` * `bucket`: the characters to use for directory bucketing * `rooted`: `true` or `false` diff --git a/go.mod b/go.mod index b7dcf635a..f0455c704 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/opentracing/opentracing-go v1.1.0 github.com/sirupsen/logrus v1.3.0 github.com/src-d/enry/v2 v2.0.0 - github.com/src-d/go-borges v0.0.0-20190619084057-d02cf3fd6581 + github.com/src-d/go-borges v0.0.0-20190620132223-0499c9b6034b github.com/src-d/go-git v4.7.0+incompatible github.com/src-d/go-git-fixtures v3.5.1-0.20190605154830-57f3972b0248+incompatible github.com/src-d/go-mysql-server v0.4.1-0.20190620142646-9722f31e0f85 diff --git a/go.sum b/go.sum index 93449f952..029e7ff93 100644 --- a/go.sum +++ b/go.sum @@ -217,8 +217,8 @@ github.com/src-d/enry/v2 v2.0.0 h1:2ADqfDHhroFwL1RGhMS9e15NkEwln8P4AABwVvIdAlo= github.com/src-d/enry/v2 v2.0.0/go.mod h1:qQeCMRwzMF3ckeGr+h0tJLdxXnq+NVZsIDMELj0t028= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= -github.com/src-d/go-borges v0.0.0-20190619084057-d02cf3fd6581 h1:ZOJFuELokTVFnwpgD0tj9PMTXiBwutmGoH2WCaG5dts= -github.com/src-d/go-borges v0.0.0-20190619084057-d02cf3fd6581/go.mod h1:Myl/zHrk3iT/I5T08RTBpuGzchucytSsi6p7KzM2lOA= +github.com/src-d/go-borges v0.0.0-20190620132223-0499c9b6034b h1:c2CSWoLOo1o2ija5OWDETzMUi9kncByT5GL7EkSpzxk= +github.com/src-d/go-borges v0.0.0-20190620132223-0499c9b6034b/go.mod h1:Myl/zHrk3iT/I5T08RTBpuGzchucytSsi6p7KzM2lOA= github.com/src-d/go-git v4.7.0+incompatible h1:IYSSnbAHeKmsfbQFi9ozbid+KNh0bKjlorMfQehQbcE= github.com/src-d/go-git v4.7.0+incompatible/go.mod h1:1bQciz+hn0jzPQNsYj0hDFZHLJBdV7gXE2mWhC7EkFk= github.com/src-d/go-git-fixtures v3.5.1-0.20190605154830-57f3972b0248+incompatible h1:A5bKevhs9C//Nh8QV0J+1KphEaIa25cDe1DTs/yPxDI=