aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2017-05-04 10:17:15 +0200
committerMáximo Cuadros <mcuadros@gmail.com>2017-05-04 10:17:15 +0200
commitb8b61e74469e0d2662e7d690eee14893f91fe259 (patch)
tree1933fe53200d98ad2c557d034609408641ce2900
parent40fa5882a2c73f8c075403b7ec85870f04deda07 (diff)
parente80cdbabb92a1ec35ffad536f52d3ff04b548fd1 (diff)
downloadgo-git-b8b61e74469e0d2662e7d690eee14893f91fe259.tar.gz
Merge branch 'master' of github.com:src-d/go-git into commit
-rw-r--r--_examples/common_test.go3
-rw-r--r--_examples/storage/README.md2
-rw-r--r--common_test.go4
-rw-r--r--plumbing/transport/client/client.go6
-rw-r--r--plumbing/transport/common.go156
-rw-r--r--plumbing/transport/common_test.go108
-rw-r--r--plumbing/transport/file/client.go2
-rw-r--r--plumbing/transport/file/client_test.go7
-rw-r--r--plumbing/transport/file/server.go4
-rw-r--r--plumbing/transport/file/server_test.go5
-rw-r--r--plumbing/transport/file/upload_pack_test.go12
-rw-r--r--plumbing/transport/git/common.go14
-rw-r--r--plumbing/transport/http/common.go12
-rw-r--r--plumbing/transport/http/upload_pack.go2
-rw-r--r--plumbing/transport/server/loader.go2
-rw-r--r--plumbing/transport/server/loader_test.go7
-rw-r--r--plumbing/transport/ssh/auth_method.go13
-rw-r--r--plumbing/transport/ssh/common.go21
-rw-r--r--references_test.go2
-rw-r--r--remote.go91
-rw-r--r--remote_test.go153
-rw-r--r--repository.go64
-rw-r--r--repository_test.go95
-rw-r--r--submodule_test.go5
-rw-r--r--worktree_status.go36
25 files changed, 663 insertions, 163 deletions
diff --git a/_examples/common_test.go b/_examples/common_test.go
index 5543eaf..ff01ba3 100644
--- a/_examples/common_test.go
+++ b/_examples/common_test.go
@@ -2,7 +2,6 @@ package examples
import (
"flag"
- "fmt"
"go/build"
"io/ioutil"
"os"
@@ -96,7 +95,7 @@ func createBareRepository(dir string) string {
func setEmptyRemote(dir string) string {
remote := createBareRepository(tempFolder())
- setRemote(dir, fmt.Sprintf("file://%s", remote))
+ setRemote(dir, remote)
return dir
}
diff --git a/_examples/storage/README.md b/_examples/storage/README.md
index 02b68da..b7207ee 100644
--- a/_examples/storage/README.md
+++ b/_examples/storage/README.md
@@ -1,4 +1,4 @@
-#go-git + aerospike: a git repository backed by a database
+# go-git + aerospike: a git repository backed by a database
<img src="https://upload.wikimedia.org/wikipedia/en/2/2b/Aerospike_logo.png" align="right"/> This is an example of a [go-git](https://github.com/src-d/go-git) repository backed by [Aerospike](http://www.aerospike.com/).
diff --git a/common_test.go b/common_test.go
index bd60385..e1a2a0f 100644
--- a/common_test.go
+++ b/common_test.go
@@ -1,7 +1,6 @@
package git
import (
- "fmt"
"testing"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -95,8 +94,7 @@ func (s *BaseSuite) GetBasicLocalRepositoryURL() string {
}
func (s *BaseSuite) GetLocalRepositoryURL(f *fixtures.Fixture) string {
- path := f.DotGit().Base()
- return fmt.Sprintf("file://%s", path)
+ return f.DotGit().Base()
}
type SuiteCommon struct{}
diff --git a/plumbing/transport/client/client.go b/plumbing/transport/client/client.go
index a398a74..76c1469 100644
--- a/plumbing/transport/client/client.go
+++ b/plumbing/transport/client/client.go
@@ -35,13 +35,13 @@ func InstallProtocol(scheme string, c transport.Transport) {
// http://, https://, ssh:// and file://.
// See `InstallProtocol` to add or modify protocols.
func NewClient(endpoint transport.Endpoint) (transport.Transport, error) {
- f, ok := Protocols[endpoint.Scheme]
+ f, ok := Protocols[endpoint.Protocol()]
if !ok {
- return nil, fmt.Errorf("unsupported scheme %q", endpoint.Scheme)
+ return nil, fmt.Errorf("unsupported scheme %q", endpoint.Protocol())
}
if f == nil {
- return nil, fmt.Errorf("malformed client for scheme %q, client is defined as nil", endpoint.Scheme)
+ return nil, fmt.Errorf("malformed client for scheme %q, client is defined as nil", endpoint.Protocol())
}
return f, nil
diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go
index d3cfe21..d6594ca 100644
--- a/plumbing/transport/common.go
+++ b/plumbing/transport/common.go
@@ -18,6 +18,7 @@ import (
"io"
"net/url"
"regexp"
+ "strconv"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
@@ -28,7 +29,7 @@ var (
ErrRepositoryNotFound = errors.New("repository not found")
ErrEmptyRemoteRepository = errors.New("remote repository is empty")
ErrAuthenticationRequired = errors.New("authentication required")
- ErrAuthorizationFailed = errors.New("authorization failed")
+ ErrAuthorizationFailed = errors.New("authorization failed")
ErrEmptyUploadPackRequest = errors.New("empty git-upload-pack given")
ErrInvalidAuthMethod = errors.New("invalid auth method")
ErrAlreadyConnected = errors.New("session already established")
@@ -88,42 +89,159 @@ type ReceivePackSession interface {
ReceivePack(*packp.ReferenceUpdateRequest) (*packp.ReportStatus, error)
}
-type Endpoint url.URL
-
-var (
- isSchemeRegExp = regexp.MustCompile("^[^:]+://")
- scpLikeUrlRegExp = regexp.MustCompile("^(?P<user>[^@]+@)?(?P<host>[^:]+):/?(?P<path>.+)$")
-)
+// Endpoint represents a Git URL in any supported protocol.
+type Endpoint interface {
+ // Protocol returns the protocol (e.g. git, https, file). It should never
+ // return the empty string.
+ Protocol() string
+ // User returns the user or an empty string if none is given.
+ User() string
+ // Password returns the password or an empty string if none is given.
+ Password() string
+ // Host returns the host or an empty string if none is given.
+ Host() string
+ // Port returns the port or 0 if there is no port or a default should be
+ // used.
+ Port() int
+ // Path returns the repository path.
+ Path() string
+ // String returns a string representation of the Git URL.
+ String() string
+}
func NewEndpoint(endpoint string) (Endpoint, error) {
- endpoint = transformSCPLikeIfNeeded(endpoint)
+ if e, ok := parseSCPLike(endpoint); ok {
+ return e, nil
+ }
+
+ if e, ok := parseFile(endpoint); ok {
+ return e, nil
+ }
u, err := url.Parse(endpoint)
if err != nil {
- return Endpoint{}, plumbing.NewPermanentError(err)
+ return nil, plumbing.NewPermanentError(err)
}
if !u.IsAbs() {
- return Endpoint{}, plumbing.NewPermanentError(fmt.Errorf(
+ return nil, plumbing.NewPermanentError(fmt.Errorf(
"invalid endpoint: %s", endpoint,
))
}
- return Endpoint(*u), nil
+ return urlEndpoint{u}, nil
+}
+
+type urlEndpoint struct {
+ *url.URL
+}
+
+func (e urlEndpoint) Protocol() string { return e.URL.Scheme }
+func (e urlEndpoint) Host() string { return e.URL.Hostname() }
+
+func (e urlEndpoint) User() string {
+ if e.URL.User == nil {
+ return ""
+ }
+
+ return e.URL.User.Username()
+}
+
+func (e urlEndpoint) Password() string {
+ if e.URL.User == nil {
+ return ""
+ }
+
+ p, _ := e.URL.User.Password()
+ return p
+}
+
+func (e urlEndpoint) Port() int {
+ p := e.URL.Port()
+ if p == "" {
+ return 0
+ }
+
+ i, err := strconv.Atoi(e.URL.Port())
+ if err != nil {
+ return 0
+ }
+
+ return i
+}
+
+func (e urlEndpoint) Path() string {
+ var res string = e.URL.Path
+ if e.URL.RawQuery != "" {
+ res += "?" + e.URL.RawQuery
+ }
+
+ if e.URL.Fragment != "" {
+ res += "#" + e.URL.Fragment
+ }
+
+ return res
+}
+
+type scpEndpoint struct {
+ user string
+ host string
+ path string
}
-func (e *Endpoint) String() string {
- u := url.URL(*e)
- return u.String()
+func (e *scpEndpoint) Protocol() string { return "ssh" }
+func (e *scpEndpoint) User() string { return e.user }
+func (e *scpEndpoint) Password() string { return "" }
+func (e *scpEndpoint) Host() string { return e.host }
+func (e *scpEndpoint) Port() int { return 22 }
+func (e *scpEndpoint) Path() string { return e.path }
+
+func (e *scpEndpoint) String() string {
+ var user string
+ if e.user != "" {
+ user = fmt.Sprintf("%s@", e.user)
+ }
+
+ return fmt.Sprintf("%s%s:%s", user, e.host, e.path)
+}
+
+type fileEndpoint struct {
+ path string
+}
+
+func (e *fileEndpoint) Protocol() string { return "file" }
+func (e *fileEndpoint) User() string { return "" }
+func (e *fileEndpoint) Password() string { return "" }
+func (e *fileEndpoint) Host() string { return "" }
+func (e *fileEndpoint) Port() int { return 0 }
+func (e *fileEndpoint) Path() string { return e.path }
+func (e *fileEndpoint) String() string { return e.path }
+
+var (
+ isSchemeRegExp = regexp.MustCompile(`^[^:]+://`)
+ scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?P<path>[^\\].*)$`)
+)
+
+func parseSCPLike(endpoint string) (Endpoint, bool) {
+ if isSchemeRegExp.MatchString(endpoint) || !scpLikeUrlRegExp.MatchString(endpoint) {
+ return nil, false
+ }
+
+ m := scpLikeUrlRegExp.FindStringSubmatch(endpoint)
+ return &scpEndpoint{
+ user: m[1],
+ host: m[2],
+ path: m[3],
+ }, true
}
-func transformSCPLikeIfNeeded(endpoint string) string {
- if !isSchemeRegExp.MatchString(endpoint) && scpLikeUrlRegExp.MatchString(endpoint) {
- m := scpLikeUrlRegExp.FindStringSubmatch(endpoint)
- return fmt.Sprintf("ssh://%s%s/%s", m[1], m[2], m[3])
+func parseFile(endpoint string) (Endpoint, bool) {
+ if isSchemeRegExp.MatchString(endpoint) {
+ return nil, false
}
- return endpoint
+ path := endpoint
+ return &fileEndpoint{path}, true
}
// UnsupportedCapabilities are the capabilities not supported by any client
diff --git a/plumbing/transport/common_test.go b/plumbing/transport/common_test.go
index e9a5efa..ec617bd 100644
--- a/plumbing/transport/common_test.go
+++ b/plumbing/transport/common_test.go
@@ -14,22 +14,118 @@ type SuiteCommon struct{}
var _ = Suite(&SuiteCommon{})
-func (s *SuiteCommon) TestNewEndpoint(c *C) {
+func (s *SuiteCommon) TestNewEndpointHTTP(c *C) {
+ e, err := NewEndpoint("http://git:pass@github.com/user/repository.git?foo#bar")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "http")
+ c.Assert(e.User(), Equals, "git")
+ c.Assert(e.Password(), Equals, "pass")
+ c.Assert(e.Host(), Equals, "github.com")
+ c.Assert(e.Port(), Equals, 0)
+ c.Assert(e.Path(), Equals, "/user/repository.git?foo#bar")
+ c.Assert(e.String(), Equals, "http://git:pass@github.com/user/repository.git?foo#bar")
+}
+
+func (s *SuiteCommon) TestNewEndpointSSH(c *C) {
e, err := NewEndpoint("ssh://git@github.com/user/repository.git")
c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "ssh")
+ c.Assert(e.User(), Equals, "git")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "github.com")
+ c.Assert(e.Port(), Equals, 0)
+ c.Assert(e.Path(), Equals, "/user/repository.git")
c.Assert(e.String(), Equals, "ssh://git@github.com/user/repository.git")
}
+func (s *SuiteCommon) TestNewEndpointSSHNoUser(c *C) {
+ e, err := NewEndpoint("ssh://github.com/user/repository.git")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "ssh")
+ c.Assert(e.User(), Equals, "")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "github.com")
+ c.Assert(e.Port(), Equals, 0)
+ c.Assert(e.Path(), Equals, "/user/repository.git")
+ c.Assert(e.String(), Equals, "ssh://github.com/user/repository.git")
+}
+
+func (s *SuiteCommon) TestNewEndpointSSHWithPort(c *C) {
+ e, err := NewEndpoint("ssh://git@github.com:777/user/repository.git")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "ssh")
+ c.Assert(e.User(), Equals, "git")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "github.com")
+ c.Assert(e.Port(), Equals, 777)
+ c.Assert(e.Path(), Equals, "/user/repository.git")
+ c.Assert(e.String(), Equals, "ssh://git@github.com:777/user/repository.git")
+}
+
func (s *SuiteCommon) TestNewEndpointSCPLike(c *C) {
e, err := NewEndpoint("git@github.com:user/repository.git")
c.Assert(err, IsNil)
- c.Assert(e.String(), Equals, "ssh://git@github.com/user/repository.git")
+ c.Assert(e.Protocol(), Equals, "ssh")
+ c.Assert(e.User(), Equals, "git")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "github.com")
+ c.Assert(e.Port(), Equals, 22)
+ c.Assert(e.Path(), Equals, "user/repository.git")
+ c.Assert(e.String(), Equals, "git@github.com:user/repository.git")
+}
+
+func (s *SuiteCommon) TestNewEndpointFileAbs(c *C) {
+ e, err := NewEndpoint("/foo.git")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "file")
+ c.Assert(e.User(), Equals, "")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "")
+ c.Assert(e.Port(), Equals, 0)
+ c.Assert(e.Path(), Equals, "/foo.git")
+ c.Assert(e.String(), Equals, "/foo.git")
+}
+
+func (s *SuiteCommon) TestNewEndpointFileRel(c *C) {
+ e, err := NewEndpoint("foo.git")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "file")
+ c.Assert(e.User(), Equals, "")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "")
+ c.Assert(e.Port(), Equals, 0)
+ c.Assert(e.Path(), Equals, "foo.git")
+ c.Assert(e.String(), Equals, "foo.git")
+}
+
+func (s *SuiteCommon) TestNewEndpointFileWindows(c *C) {
+ e, err := NewEndpoint("C:\\foo.git")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "file")
+ c.Assert(e.User(), Equals, "")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "")
+ c.Assert(e.Port(), Equals, 0)
+ c.Assert(e.Path(), Equals, "C:\\foo.git")
+ c.Assert(e.String(), Equals, "C:\\foo.git")
+}
+
+func (s *SuiteCommon) TestNewEndpointFileURL(c *C) {
+ e, err := NewEndpoint("file:///foo.git")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol(), Equals, "file")
+ c.Assert(e.User(), Equals, "")
+ c.Assert(e.Password(), Equals, "")
+ c.Assert(e.Host(), Equals, "")
+ c.Assert(e.Port(), Equals, 0)
+ c.Assert(e.Path(), Equals, "/foo.git")
+ c.Assert(e.String(), Equals, "file:///foo.git")
}
-func (s *SuiteCommon) TestNewEndpointWrongForgat(c *C) {
- e, err := NewEndpoint("foo")
- c.Assert(err, Not(IsNil))
- c.Assert(e.Host, Equals, "")
+func (s *SuiteCommon) TestNewEndpointInvalidURL(c *C) {
+ e, err := NewEndpoint("http://\\")
+ c.Assert(err, NotNil)
+ c.Assert(e, IsNil)
}
func (s *SuiteCommon) TestFilterUnsupportedCapabilities(c *C) {
diff --git a/plumbing/transport/file/client.go b/plumbing/transport/file/client.go
index 58e60db..d2a57d0 100644
--- a/plumbing/transport/file/client.go
+++ b/plumbing/transport/file/client.go
@@ -41,7 +41,7 @@ func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthM
return nil, err
}
- return &command{cmd: exec.Command(cmd, ep.Path)}, nil
+ return &command{cmd: exec.Command(cmd, ep.Path())}, nil
}
type command struct {
diff --git a/plumbing/transport/file/client_test.go b/plumbing/transport/file/client_test.go
index 220df3d..030175e 100644
--- a/plumbing/transport/file/client_test.go
+++ b/plumbing/transport/file/client_test.go
@@ -1,9 +1,9 @@
package file
import (
- "fmt"
"io"
"os"
+ "path/filepath"
"strings"
"testing"
@@ -20,13 +20,12 @@ filemode = true
bare = true`
func prepareRepo(c *C, path string) transport.Endpoint {
- url := fmt.Sprintf("file://%s", path)
- ep, err := transport.NewEndpoint(url)
+ ep, err := transport.NewEndpoint(path)
c.Assert(err, IsNil)
// git-receive-pack refuses to update refs/heads/master on non-bare repo
// so we ensure bare repo config.
- config := fmt.Sprintf("%s/config", path)
+ config := filepath.Join(path, "config")
if _, err := os.Stat(config); err == nil {
f, err := os.OpenFile(config, os.O_TRUNC|os.O_WRONLY, 0)
c.Assert(err, IsNil)
diff --git a/plumbing/transport/file/server.go b/plumbing/transport/file/server.go
index 74085c2..61dd42d 100644
--- a/plumbing/transport/file/server.go
+++ b/plumbing/transport/file/server.go
@@ -14,7 +14,7 @@ import (
// and error. This is meant to be used when implementing a git-upload-pack
// command.
func ServeUploadPack(path string) error {
- ep, err := transport.NewEndpoint(fmt.Sprintf("file://%s", path))
+ ep, err := transport.NewEndpoint(path)
if err != nil {
return err
}
@@ -32,7 +32,7 @@ func ServeUploadPack(path string) error {
// input and error. This is meant to be used when implementing a
// git-receive-pack command.
func ServeReceivePack(path string) error {
- ep, err := transport.NewEndpoint(fmt.Sprintf("file://%s", path))
+ ep, err := transport.NewEndpoint(path)
if err != nil {
return err
}
diff --git a/plumbing/transport/file/server_test.go b/plumbing/transport/file/server_test.go
index a7b4e34..176d6ee 100644
--- a/plumbing/transport/file/server_test.go
+++ b/plumbing/transport/file/server_test.go
@@ -1,7 +1,6 @@
package file
import (
- "fmt"
"os"
"os/exec"
@@ -15,7 +14,6 @@ type ServerSuite struct {
RemoteName string
SrcPath string
DstPath string
- DstURL string
}
var _ = Suite(&ServerSuite{})
@@ -30,9 +28,8 @@ func (s *ServerSuite) SetUpSuite(c *C) {
fixture = fixtures.ByTag("empty").One()
s.DstPath = fixture.DotGit().Base()
- s.DstURL = fmt.Sprintf("file://%s", s.DstPath)
- cmd := exec.Command("git", "remote", "add", s.RemoteName, s.DstURL)
+ cmd := exec.Command("git", "remote", "add", s.RemoteName, s.DstPath)
cmd.Dir = s.SrcPath
c.Assert(cmd.Run(), IsNil)
}
diff --git a/plumbing/transport/file/upload_pack_test.go b/plumbing/transport/file/upload_pack_test.go
index e5915f0..f013683 100644
--- a/plumbing/transport/file/upload_pack_test.go
+++ b/plumbing/transport/file/upload_pack_test.go
@@ -1,8 +1,8 @@
package file
import (
- "fmt"
"os"
+ "path/filepath"
"github.com/src-d/go-git-fixtures"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
@@ -25,20 +25,18 @@ func (s *UploadPackSuite) SetUpSuite(c *C) {
fixture := fixtures.Basic().One()
path := fixture.DotGit().Base()
- url := fmt.Sprintf("file://%s", path)
- ep, err := transport.NewEndpoint(url)
+ ep, err := transport.NewEndpoint(path)
c.Assert(err, IsNil)
s.Endpoint = ep
fixture = fixtures.ByTag("empty").One()
path = fixture.DotGit().Base()
- url = fmt.Sprintf("file://%s", path)
- ep, err = transport.NewEndpoint(url)
+ ep, err = transport.NewEndpoint(path)
c.Assert(err, IsNil)
s.EmptyEndpoint = ep
- url = fmt.Sprintf("file://%s/%s", fixtures.DataFolder, "non-existent")
- ep, err = transport.NewEndpoint(url)
+ path = filepath.Join(fixtures.DataFolder, "non-existent")
+ ep, err = transport.NewEndpoint(path)
c.Assert(err, IsNil)
s.NonExistentEndpoint = ep
}
diff --git a/plumbing/transport/git/common.go b/plumbing/transport/git/common.go
index 24134c1..753f125 100644
--- a/plumbing/transport/git/common.go
+++ b/plumbing/transport/git/common.go
@@ -5,7 +5,6 @@ import (
"fmt"
"io"
"net"
- "strings"
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
@@ -16,6 +15,8 @@ import (
// DefaultClient is the default git client.
var DefaultClient = common.NewClient(&runner{})
+const DefaultPort = 9418
+
type runner struct{}
// Command returns a new Command for the given cmd in the given Endpoint
@@ -62,12 +63,13 @@ func (c *command) connect() error {
}
func (c *command) getHostWithPort() string {
- host := c.endpoint.Host
- if strings.Index(c.endpoint.Host, ":") == -1 {
- host += ":9418"
+ host := c.endpoint.Host()
+ port := c.endpoint.Port()
+ if port <= 0 {
+ port = DefaultPort
}
- return host
+ return fmt.Sprintf("%s:%d", host, port)
}
// StderrPipe git protocol doesn't have any dedicated error channel
@@ -88,7 +90,7 @@ func (c *command) StdoutPipe() (io.Reader, error) {
}
func endpointToCommand(cmd string, ep transport.Endpoint) string {
- return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path, 0, ep.Host, 0)
+ return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path(), 0, ep.Host(), 0)
}
// Wait no-op function, required by the interface
diff --git a/plumbing/transport/http/common.go b/plumbing/transport/http/common.go
index 930e8eb..04b6121 100644
--- a/plumbing/transport/http/common.go
+++ b/plumbing/transport/http/common.go
@@ -73,18 +73,12 @@ type AuthMethod interface {
}
func basicAuthFromEndpoint(ep transport.Endpoint) *BasicAuth {
- info := ep.User
- if info == nil {
+ u := ep.User()
+ if u == "" {
return nil
}
- p, ok := info.Password()
- if !ok {
- return nil
- }
-
- u := info.Username()
- return NewBasicAuth(u, p)
+ return NewBasicAuth(u, ep.Password())
}
// BasicAuth represent a HTTP basic auth
diff --git a/plumbing/transport/http/upload_pack.go b/plumbing/transport/http/upload_pack.go
index 8f73789..2d1ea45 100644
--- a/plumbing/transport/http/upload_pack.go
+++ b/plumbing/transport/http/upload_pack.go
@@ -150,7 +150,7 @@ func (s *upSession) doRequest(method, url string, content *bytes.Buffer) (*http.
// it requires a bytes.Buffer, because we need to know the length
func (s *upSession) applyHeadersToRequest(req *http.Request, content *bytes.Buffer) {
req.Header.Add("User-Agent", "git/1.0")
- req.Header.Add("Host", s.endpoint.Host)
+ req.Header.Add("Host", s.endpoint.Host()) // host:port
if content == nil {
req.Header.Add("Accept", "*/*")
diff --git a/plumbing/transport/server/loader.go b/plumbing/transport/server/loader.go
index cfe9f04..b51a795 100644
--- a/plumbing/transport/server/loader.go
+++ b/plumbing/transport/server/loader.go
@@ -34,7 +34,7 @@ func NewFilesystemLoader(base billy.Filesystem) Loader {
// storer for it. Returns transport.ErrRepositoryNotFound if a repository does
// not exist in the given path.
func (l *fsLoader) Load(ep transport.Endpoint) (storer.Storer, error) {
- fs := l.base.Dir(ep.Path)
+ fs := l.base.Dir(ep.Path())
if _, err := fs.Stat("config"); err != nil {
return nil, transport.ErrRepositoryNotFound
}
diff --git a/plumbing/transport/server/loader_test.go b/plumbing/transport/server/loader_test.go
index b4a8c37..5f32cb0 100644
--- a/plumbing/transport/server/loader_test.go
+++ b/plumbing/transport/server/loader_test.go
@@ -1,7 +1,6 @@
package server
import (
- "fmt"
"os/exec"
"path/filepath"
@@ -33,7 +32,7 @@ func (s *LoaderSuite) endpoint(c *C, url string) transport.Endpoint {
}
func (s *LoaderSuite) TestLoadNonExistent(c *C) {
- sto, err := DefaultLoader.Load(s.endpoint(c, "file:///does-not-exist"))
+ sto, err := DefaultLoader.Load(s.endpoint(c, "does-not-exist"))
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
c.Assert(sto, IsNil)
}
@@ -45,13 +44,13 @@ func (s *LoaderSuite) TestLoadNonExistentIgnoreHost(c *C) {
}
func (s *LoaderSuite) TestLoad(c *C) {
- sto, err := DefaultLoader.Load(s.endpoint(c, fmt.Sprintf("file://%s", s.RepoPath)))
+ sto, err := DefaultLoader.Load(s.endpoint(c, s.RepoPath))
c.Assert(err, IsNil)
c.Assert(sto, NotNil)
}
func (s *LoaderSuite) TestLoadIgnoreHost(c *C) {
- sto, err := DefaultLoader.Load(s.endpoint(c, fmt.Sprintf("file://%s", s.RepoPath)))
+ sto, err := DefaultLoader.Load(s.endpoint(c, s.RepoPath))
c.Assert(err, IsNil)
c.Assert(sto, NotNil)
}
diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go
index a3e1ad1..84dfe14 100644
--- a/plumbing/transport/ssh/auth_method.go
+++ b/plumbing/transport/ssh/auth_method.go
@@ -179,9 +179,14 @@ type PublicKeysCallback struct {
// NewSSHAgentAuth returns a PublicKeysCallback based on a SSH agent, it opens
// a pipe with the SSH agent and uses the pipe as the implementer of the public
// key callback function.
-func NewSSHAgentAuth(user string) (AuthMethod, error) {
- if user == "" {
- user = DefaultUsername
+func NewSSHAgentAuth(u string) (AuthMethod, error) {
+ if u == "" {
+ usr, err := user.Current()
+ if err != nil {
+ return nil, fmt.Errorf("error getting current user: %q", err)
+ }
+
+ u = usr.Username
}
sshAgentAddr := os.Getenv("SSH_AUTH_SOCK")
@@ -195,7 +200,7 @@ func NewSSHAgentAuth(user string) (AuthMethod, error) {
}
return &PublicKeysCallback{
- User: user,
+ User: u,
Callback: agent.NewClient(pipe).Signers,
}, nil
}
diff --git a/plumbing/transport/ssh/common.go b/plumbing/transport/ssh/common.go
index 7b44a91..d53fc12 100644
--- a/plumbing/transport/ssh/common.go
+++ b/plumbing/transport/ssh/common.go
@@ -3,7 +3,6 @@ package ssh
import (
"fmt"
- "strings"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
@@ -20,6 +19,8 @@ var DefaultAuthBuilder = func(user string) (AuthMethod, error) {
return NewSSHAgentAuth(user)
}
+const DefaultPort = 22
+
type runner struct{}
func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod) (common.Command, error) {
@@ -110,25 +111,21 @@ func (c *command) connect() error {
}
func (c *command) getHostWithPort() string {
- host := c.endpoint.Host
- if strings.Index(c.endpoint.Host, ":") == -1 {
- host += ":22"
+ host := c.endpoint.Host()
+ port := c.endpoint.Port()
+ if port <= 0 {
+ port = DefaultPort
}
- return host
+ return fmt.Sprintf("%s:%d", host, port)
}
func (c *command) setAuthFromEndpoint() error {
- var u string
- if info := c.endpoint.User; info != nil {
- u = info.Username()
- }
-
var err error
- c.auth, err = DefaultAuthBuilder(u)
+ c.auth, err = DefaultAuthBuilder(c.endpoint.User())
return err
}
func endpointToCommand(cmd string, ep transport.Endpoint) string {
- return fmt.Sprintf("%s '%s'", cmd, ep.Path)
+ return fmt.Sprintf("%s '%s'", cmd, ep.Path())
}
diff --git a/references_test.go b/references_test.go
index 3cd0b97..050f169 100644
--- a/references_test.go
+++ b/references_test.go
@@ -295,7 +295,7 @@ func (s *ReferencesSuite) TestObjectNotFoundError(c *C) {
url := fixtures.ByURL("https://github.com/git-fixtures/basic.git").One().DotGit().Base()
storer := memory.NewStorage()
r, err := Clone(storer, nil, &CloneOptions{
- URL: "file://" + url,
+ URL: url,
})
c.Assert(err, IsNil)
diff --git a/remote.go b/remote.go
index ae8a544..592f7b6 100644
--- a/remote.go
+++ b/remote.go
@@ -8,6 +8,7 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
+ "gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
@@ -56,9 +57,7 @@ func (r *Remote) Fetch(o *FetchOptions) error {
// remote was already up-to-date.
func (r *Remote) Push(o *PushOptions) (err error) {
// TODO: Support deletes.
- // TODO: Support pushing tags.
- // TODO: Check if force update is given, otherwise reject non-fast forward.
- // TODO: Sideband suppor
+ // TODO: Sideband support
if o.RemoteName == "" {
o.RemoteName = r.c.Name
@@ -196,12 +195,12 @@ func newSendPackSession(url string, auth transport.AuthMethod) (transport.Receiv
func newClient(url string) (transport.Transport, transport.Endpoint, error) {
ep, err := transport.NewEndpoint(url)
if err != nil {
- return nil, transport.Endpoint{}, err
+ return nil, nil, err
}
c, err := client.NewClient(ep)
if err != nil {
- return nil, transport.Endpoint{}, err
+ return nil, nil, err
}
return c, ep, err
@@ -265,37 +264,35 @@ func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
return nil
}
- dstName := rs.Dst(localRef.Name())
- oldHash := plumbing.ZeroHash
- newHash := localRef.Hash()
-
- iter, err := remoteRefs.IterReferences()
- if err != nil {
- return err
+ cmd := &packp.Command{
+ Name: rs.Dst(localRef.Name()),
+ Old: plumbing.ZeroHash,
+ New: localRef.Hash(),
}
- err = iter.ForEach(func(remoteRef *plumbing.Reference) error {
+ remoteRef, err := remoteRefs.Reference(cmd.Name)
+ if err == nil {
if remoteRef.Type() != plumbing.HashReference {
+ //TODO: check actual git behavior here
return nil
}
- if dstName != remoteRef.Name() {
- return nil
- }
+ cmd.Old = remoteRef.Hash()
+ } else if err != plumbing.ErrReferenceNotFound {
+ return err
+ }
- oldHash = remoteRef.Hash()
+ if cmd.Old == cmd.New {
return nil
- })
+ }
- if oldHash == newHash {
- return nil
+ if !rs.IsForceUpdate() {
+ if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil {
+ return err
+ }
}
- req.Commands = append(req.Commands, &packp.Command{
- Name: dstName,
- Old: oldHash,
- New: newHash,
- })
+ req.Commands = append(req.Commands, cmd)
return nil
}
@@ -390,6 +387,50 @@ func objectExists(s storer.EncodedObjectStorer, h plumbing.Hash) (bool, error) {
return true, err
}
+func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.ReferenceStorer, cmd *packp.Command) error {
+ if cmd.Old == plumbing.ZeroHash {
+ _, err := remoteRefs.Reference(cmd.Name)
+ if err == plumbing.ErrReferenceNotFound {
+ return nil
+ }
+
+ if err != nil {
+ return err
+ }
+
+ return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
+ }
+
+ ff, err := isFastForward(s, cmd.Old, cmd.New)
+ if err != nil {
+ return err
+ }
+
+ if !ff {
+ return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
+ }
+
+ return nil
+}
+
+func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) {
+ c, err := object.GetCommit(s, new)
+ if err != nil {
+ return false, err
+ }
+
+ found := false
+ iter := object.NewCommitPreIterator(c)
+ return found, iter.ForEach(func(c *object.Commit) error {
+ if c.Hash != old {
+ return nil
+ }
+
+ found = true
+ return storer.ErrStop
+ })
+}
+
func (r *Remote) newUploadPackRequest(o *FetchOptions,
ar *packp.AdvRefs) (*packp.UploadPackRequest, error) {
diff --git a/remote_test.go b/remote_test.go
index 2cd80cf..8b2f71c 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -2,7 +2,6 @@ package git
import (
"bytes"
- "fmt"
"io"
"io/ioutil"
"os"
@@ -26,9 +25,9 @@ type RemoteSuite struct {
var _ = Suite(&RemoteSuite{})
func (s *RemoteSuite) TestFetchInvalidEndpoint(c *C) {
- r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux"})
- err := r.Fetch(&FetchOptions{})
- c.Assert(err, ErrorMatches, ".*invalid endpoint.*")
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "http://\\"})
+ err := r.Fetch(&FetchOptions{RemoteName: "foo"})
+ c.Assert(err, ErrorMatches, ".*invalid character.*")
}
func (s *RemoteSuite) TestFetchNonExistentEndpoint(c *C) {
@@ -215,7 +214,7 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
c.Assert(err, IsNil)
dstFs := fixtures.ByTag("empty").One().DotGit()
- url := fmt.Sprintf("file://%s", dstFs.Base())
+ url := dstFs.Base()
r := newRemote(sto, &config.RemoteConfig{
Name: DefaultRemoteName,
@@ -249,11 +248,56 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
c.Assert(err, IsNil)
}
+func (s *RemoteSuite) TestPushTags(c *C) {
+ srcFs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit()
+ sto, err := filesystem.NewStorage(srcFs)
+ c.Assert(err, IsNil)
+
+ dstFs := fixtures.ByTag("empty").One().DotGit()
+ url := dstFs.Base()
+
+ r := newRemote(sto, &config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: url,
+ })
+
+ rs := config.RefSpec("refs/tags/*:refs/tags/*")
+ err = r.Push(&PushOptions{
+ RefSpecs: []config.RefSpec{rs},
+ })
+ c.Assert(err, IsNil)
+
+ dstSto, err := filesystem.NewStorage(dstFs)
+ c.Assert(err, IsNil)
+ dstRepo, err := Open(dstSto, nil)
+ c.Assert(err, IsNil)
+
+ ref, err := dstRepo.Storer.Reference(plumbing.ReferenceName("refs/tags/lightweight-tag"))
+ c.Assert(err, IsNil)
+ c.Assert(ref, DeepEquals, plumbing.NewReferenceFromStrings("refs/tags/lightweight-tag", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"))
+
+ ref, err = dstRepo.Storer.Reference(plumbing.ReferenceName("refs/tags/annotated-tag"))
+ c.Assert(err, IsNil)
+ c.Assert(ref, DeepEquals, plumbing.NewReferenceFromStrings("refs/tags/annotated-tag", "b742a2a9fa0afcfa9a6fad080980fbc26b007c69"))
+
+ ref, err = dstRepo.Storer.Reference(plumbing.ReferenceName("refs/tags/commit-tag"))
+ c.Assert(err, IsNil)
+ c.Assert(ref, DeepEquals, plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"))
+
+ ref, err = dstRepo.Storer.Reference(plumbing.ReferenceName("refs/tags/blob-tag"))
+ c.Assert(err, IsNil)
+ c.Assert(ref, DeepEquals, plumbing.NewReferenceFromStrings("refs/tags/blob-tag", "fe6cb94756faa81e5ed9240f9191b833db5f40ae"))
+
+ ref, err = dstRepo.Storer.Reference(plumbing.ReferenceName("refs/tags/tree-tag"))
+ c.Assert(err, IsNil)
+ c.Assert(ref, DeepEquals, plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"))
+}
+
func (s *RemoteSuite) TestPushNoErrAlreadyUpToDate(c *C) {
f := fixtures.Basic().One()
sto, err := filesystem.NewStorage(f.DotGit())
c.Assert(err, IsNil)
- url := fmt.Sprintf("file://%s", f.DotGit().Base())
+ url := f.DotGit().Base()
r := newRemote(sto, &config.RemoteConfig{
Name: DefaultRemoteName,
URL: url,
@@ -266,10 +310,97 @@ func (s *RemoteSuite) TestPushNoErrAlreadyUpToDate(c *C) {
c.Assert(err, Equals, NoErrAlreadyUpToDate)
}
+func (s *RemoteSuite) TestPushRejectNonFastForward(c *C) {
+ f := fixtures.Basic().One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+ c.Assert(err, IsNil)
+
+ dstFs := f.DotGit()
+ dstSto, err := filesystem.NewStorage(dstFs)
+ c.Assert(err, IsNil)
+
+ url := dstFs.Base()
+ r := newRemote(sto, &config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: url,
+ })
+
+ oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
+ c.Assert(err, IsNil)
+ c.Assert(oldRef, NotNil)
+
+ err = r.Push(&PushOptions{RefSpecs: []config.RefSpec{
+ config.RefSpec("refs/heads/master:refs/heads/branch"),
+ }})
+ c.Assert(err, ErrorMatches, "non-fast-forward update: refs/heads/branch")
+
+ newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
+ c.Assert(err, IsNil)
+ c.Assert(newRef, DeepEquals, oldRef)
+}
+
+func (s *RemoteSuite) TestPushForce(c *C) {
+ f := fixtures.Basic().One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+ c.Assert(err, IsNil)
+
+ dstFs := f.DotGit()
+ dstSto, err := filesystem.NewStorage(dstFs)
+ c.Assert(err, IsNil)
+
+ url := dstFs.Base()
+ r := newRemote(sto, &config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: url,
+ })
+
+ oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
+ c.Assert(err, IsNil)
+ c.Assert(oldRef, NotNil)
+
+ err = r.Push(&PushOptions{RefSpecs: []config.RefSpec{
+ config.RefSpec("+refs/heads/master:refs/heads/branch"),
+ }})
+ c.Assert(err, IsNil)
+
+ newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
+ c.Assert(err, IsNil)
+ c.Assert(newRef, Not(DeepEquals), oldRef)
+}
+
+func (s *RemoteSuite) TestPushNewReference(c *C) {
+ f := fixtures.Basic().One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+ c.Assert(err, IsNil)
+
+ dstFs := f.DotGit()
+ dstSto, err := filesystem.NewStorage(dstFs)
+ c.Assert(err, IsNil)
+
+ url := dstFs.Base()
+ r := newRemote(sto, &config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: url,
+ })
+
+ oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
+ c.Assert(err, IsNil)
+ c.Assert(oldRef, NotNil)
+
+ err = r.Push(&PushOptions{RefSpecs: []config.RefSpec{
+ config.RefSpec("refs/heads/branch:refs/heads/branch2"),
+ }})
+ c.Assert(err, IsNil)
+
+ newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch2"))
+ c.Assert(err, IsNil)
+ c.Assert(newRef.Hash(), Equals, oldRef.Hash())
+}
+
func (s *RemoteSuite) TestPushInvalidEndpoint(c *C) {
- r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux"})
- err := r.Push(&PushOptions{})
- c.Assert(err, ErrorMatches, ".*invalid endpoint.*")
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "http://\\"})
+ err := r.Push(&PushOptions{RemoteName: "foo"})
+ c.Assert(err, ErrorMatches, ".*invalid character.*")
}
func (s *RemoteSuite) TestPushNonExistentEndpoint(c *C) {
@@ -294,7 +425,7 @@ func (s *RemoteSuite) TestPushInvalidFetchOptions(c *C) {
func (s *RemoteSuite) TestPushInvalidRefSpec(c *C) {
r := newRemote(nil, &config.RemoteConfig{
Name: DefaultRemoteName,
- URL: "file:///some-url",
+ URL: "some-url",
})
rs := config.RefSpec("^*$**")
@@ -307,7 +438,7 @@ func (s *RemoteSuite) TestPushInvalidRefSpec(c *C) {
func (s *RemoteSuite) TestPushWrongRemoteName(c *C) {
r := newRemote(nil, &config.RemoteConfig{
Name: DefaultRemoteName,
- URL: "file:///some-url",
+ URL: "some-url",
})
err := r.Push(&PushOptions{
diff --git a/repository.go b/repository.go
index bb59afe..8a7b348 100644
--- a/repository.go
+++ b/repository.go
@@ -3,8 +3,10 @@ package git
import (
"errors"
"fmt"
+ stdioutil "io/ioutil"
"os"
"path/filepath"
+ "strings"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/internal/revision"
@@ -13,6 +15,7 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage"
"gopkg.in/src-d/go-git.v4/storage/filesystem"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
"gopkg.in/src-d/go-billy.v2"
"gopkg.in/src-d/go-billy.v2/osfs"
@@ -193,26 +196,69 @@ func PlainInit(path string, isBare bool) (*Repository, error) {
// repository is bare or a normal one. If the path doesn't contain a valid
// repository ErrRepositoryNotExists is returned
func PlainOpen(path string) (*Repository, error) {
- var wt, dot billy.Filesystem
+ dot, wt, err := dotGitToFilesystems(path)
+ if err != nil {
+ return nil, err
+ }
+
+ s, err := filesystem.NewStorage(dot)
+ if err != nil {
+ return nil, err
+ }
+ return Open(s, wt)
+}
+
+func dotGitToFilesystems(path string) (dot, wt billy.Filesystem, err error) {
fs := osfs.New(path)
- if _, err := fs.Stat(".git"); err != nil {
+ fi, err := fs.Stat(".git")
+ if err != nil {
if !os.IsNotExist(err) {
- return nil, err
+ return nil, nil, err
}
- dot = fs
- } else {
- wt = fs
- dot = fs.Dir(".git")
+ return fs, nil, nil
}
- s, err := filesystem.NewStorage(dot)
+ if fi.IsDir() {
+ return fs.Dir(".git"), fs, nil
+ }
+
+ dot, err = dotGitFileToFilesystem(fs)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return dot, fs, nil
+}
+
+func dotGitFileToFilesystem(fs billy.Filesystem) (billy.Filesystem, error) {
+ var err error
+
+ f, err := fs.Open(".git")
if err != nil {
return nil, err
}
+ defer ioutil.CheckClose(f, &err)
- return Open(s, wt)
+ b, err := stdioutil.ReadAll(f)
+ if err != nil {
+ return nil, err
+ }
+
+ line := string(b)
+ const prefix = "gitdir: "
+ if !strings.HasPrefix(line, prefix) {
+ return nil, fmt.Errorf(".git file has no %s prefix", prefix)
+ }
+
+ gitdir := line[len(prefix):]
+ gitdir = strings.TrimSpace(gitdir)
+ if filepath.IsAbs(gitdir) {
+ return osfs.New(gitdir), nil
+ }
+
+ return fs.Dir(gitdir), err
}
// PlainClone a repository into the path with the given options, isBare defines
diff --git a/repository_test.go b/repository_test.go
index 77bfde2..deb3f58 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -272,6 +272,93 @@ func (s *RepositorySuite) TestPlainOpenNotBare(c *C) {
c.Assert(r, IsNil)
}
+func (s *RepositorySuite) testPlainOpenGitFile(c *C, f func(string, string) string) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ altDir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(altDir)
+
+ err = ioutil.WriteFile(filepath.Join(altDir, ".git"), []byte(f(dir, altDir)), 0644)
+ c.Assert(err, IsNil)
+
+ r, err = PlainOpen(altDir)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenBareAbsoluteGitDirFile(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ return fmt.Sprintf("gitdir: %s\n", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareAbsoluteGitDirFileNoEOL(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ return fmt.Sprintf("gitdir: %s", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFile(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ dir, err := filepath.Rel(altDir, dir)
+ c.Assert(err, IsNil)
+ return fmt.Sprintf("gitdir: %s\n", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileNoEOL(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ dir, err := filepath.Rel(altDir, dir)
+ c.Assert(err, IsNil)
+ return fmt.Sprintf("gitdir: %s\n", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileTrailingGarbage(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ altDir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(altDir, ".git"), []byte(fmt.Sprintf("gitdir: %s\nTRAILING", dir)), 0644)
+ c.Assert(err, IsNil)
+
+ r, err = PlainOpen(altDir)
+ c.Assert(err, Equals, ErrRepositoryNotExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileBadPrefix(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ altDir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(altDir, ".git"), []byte(fmt.Sprintf("xgitdir: %s\n", dir)), 0644)
+ c.Assert(err, IsNil)
+
+ r, err = PlainOpen(altDir)
+ c.Assert(err, ErrorMatches, ".*gitdir.*")
+ c.Assert(r, IsNil)
+}
+
func (s *RepositorySuite) TestPlainOpenNotExists(c *C) {
r, err := PlainOpen("/not-exists/")
c.Assert(err, Equals, ErrRepositoryNotExists)
@@ -301,7 +388,7 @@ func (s *RepositorySuite) TestPlainCloneWithRecurseSubmodules(c *C) {
path := fixtures.ByTag("submodule").One().Worktree().Base()
r, err := PlainClone(dir, false, &CloneOptions{
- URL: fmt.Sprintf("file://%s", path),
+ URL: path,
RecurseSubmodules: DefaultSubmoduleRecursionDepth,
})
@@ -591,7 +678,7 @@ func (s *RepositorySuite) TestPullProgressWithRecursion(c *C) {
r, _ := PlainInit(dir, false)
r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
- URL: fmt.Sprintf("file://%s", path),
+ URL: path,
})
err = r.Pull(&PullOptions{
@@ -607,7 +694,7 @@ func (s *RepositorySuite) TestPullAdd(c *C) {
path := fixtures.Basic().ByTag("worktree").One().Worktree().Base()
r, err := Clone(memory.NewStorage(), nil, &CloneOptions{
- URL: fmt.Sprintf("file://%s", filepath.Join(path, ".git")),
+ URL: filepath.Join(path, ".git"),
})
c.Assert(err, IsNil)
@@ -642,7 +729,7 @@ func (s *RepositorySuite) TestPushToEmptyRepository(c *C) {
c.Assert(err, IsNil)
dstFs := fixtures.ByTag("empty").One().DotGit()
- url := fmt.Sprintf("file://%s", dstFs.Base())
+ url := dstFs.Base()
r, err := Open(sto, srcFs)
c.Assert(err, IsNil)
diff --git a/submodule_test.go b/submodule_test.go
index 6e06191..fdbe4a8 100644
--- a/submodule_test.go
+++ b/submodule_test.go
@@ -1,15 +1,14 @@
package git
import (
- "fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/src-d/go-git-fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing"
. "gopkg.in/check.v1"
- "gopkg.in/src-d/go-git.v4/plumbing"
)
type SubmoduleSuite struct {
@@ -27,7 +26,7 @@ func (s *SubmoduleSuite) SetUpTest(c *C) {
c.Assert(err, IsNil)
r, err := PlainClone(filepath.Join(dir, "worktree"), false, &CloneOptions{
- URL: fmt.Sprintf("file://%s", path),
+ URL: path,
})
c.Assert(err, IsNil)
diff --git a/worktree_status.go b/worktree_status.go
index 46953c7..632f102 100644
--- a/worktree_status.go
+++ b/worktree_status.go
@@ -233,42 +233,36 @@ func (w *Worktree) addOrUpdateFileToIndex(filename string, h plumbing.Hash) erro
return err
}
- _, err = idx.Entry(filename)
- if err == index.ErrEntryNotFound {
- err = w.doAddFileToIndex(idx, filename)
- }
-
- if err != nil {
+ e, err := idx.Entry(filename)
+ if err != nil && err != index.ErrEntryNotFound {
return err
}
- err = w.doUpdateFileToIndex(idx, filename, h)
- if err != nil {
- return err
+ if err == index.ErrEntryNotFound {
+ if err := w.doAddFileToIndex(idx, filename, h); err != nil {
+ return err
+ }
+ } else {
+ if err := w.doUpdateFileToIndex(e, filename, h); err != nil {
+ return err
+ }
}
-
return w.r.Storer.SetIndex(idx)
}
-func (w *Worktree) doAddFileToIndex(idx *index.Index, filename string) error {
- idx.Entries = append(idx.Entries, &index.Entry{
- Name: filename,
- })
+func (w *Worktree) doAddFileToIndex(idx *index.Index, filename string, h plumbing.Hash) error {
+ e := &index.Entry{Name: filename}
+ idx.Entries = append(idx.Entries, e)
- return nil
+ return w.doUpdateFileToIndex(e, filename, h)
}
-func (w *Worktree) doUpdateFileToIndex(idx *index.Index, filename string, h plumbing.Hash) error {
+func (w *Worktree) doUpdateFileToIndex(e *index.Entry, filename string, h plumbing.Hash) error {
info, err := w.fs.Stat(filename)
if err != nil {
return err
}
- e, err := idx.Entry(filename)
- if err != nil {
- return err
- }
-
e.Hash = h
e.ModifiedAt = info.ModTime()
e.Mode, err = filemode.NewFromOSFileMode(info.Mode())