Merge pull request #1142 from EmrysMyrddin/feature/export-new-remote

git : allows to create a Remote without a Repository
diff --git a/_examples/ls-remote/main.go b/_examples/ls-remote/main.go
new file mode 100644
index 0000000..68c0454
--- /dev/null
+++ b/_examples/ls-remote/main.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+	"log"
+
+	"gopkg.in/src-d/go-git.v4"
+	"gopkg.in/src-d/go-git.v4/config"
+	"gopkg.in/src-d/go-git.v4/storage/memory"
+)
+
+// Retrieve remote tags without cloning repository
+func main() {
+
+	// Create the remote with repository URL
+	rem := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
+		Name: "origin",
+		URLs: []string{"https://github.com/Zenika/MARCEL"},
+	})
+
+	log.Print("Fetching tags...")
+
+	// We can then use every Remote functions to retrieve wanted informations
+	refs, err := rem.List(&git.ListOptions{})
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Filters the references list and only keeps tags
+	var tags []string
+	for _, ref := range refs {
+		if ref.Name().IsTag() {
+			tags = append(tags, ref.Name().Short())
+		}
+	}
+
+	if len(tags) == 0 {
+		log.Println("No tags!")
+		return
+	}
+
+	log.Printf("Tags found: %v", tags)
+}
diff --git a/remote.go b/remote.go
index 8060409..bcc4c4c 100644
--- a/remote.go
+++ b/remote.go
@@ -45,7 +45,10 @@
 	s storage.Storer
 }
 
-func newRemote(s storage.Storer, c *config.RemoteConfig) *Remote {
+// NewRemote creates a new Remote.
+// The intended purpose is to use the Remote for tasks such as listing remote references (like using git ls-remote).
+// Otherwise Remotes should be created via the use of a Repository.
+func NewRemote(s storage.Storer, c *config.RemoteConfig) *Remote {
 	return &Remote{s: s, c: c}
 }
 
diff --git a/remote_test.go b/remote_test.go
index 58a0598..70a14e9 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -31,32 +31,32 @@
 var _ = Suite(&RemoteSuite{})
 
 func (s *RemoteSuite) TestFetchInvalidEndpoint(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}})
 	err := r.Fetch(&FetchOptions{RemoteName: "foo"})
 	c.Assert(err, ErrorMatches, ".*invalid character.*")
 }
 
 func (s *RemoteSuite) TestFetchNonExistentEndpoint(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}})
 	err := r.Fetch(&FetchOptions{})
 	c.Assert(err, NotNil)
 }
 
 func (s *RemoteSuite) TestFetchInvalidSchemaEndpoint(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
 	err := r.Fetch(&FetchOptions{})
 	c.Assert(err, ErrorMatches, ".*unsupported scheme.*")
 }
 
 func (s *RemoteSuite) TestFetchInvalidFetchOptions(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
 	invalid := config.RefSpec("^*$ñ")
 	err := r.Fetch(&FetchOptions{RefSpecs: []config.RefSpec{invalid}})
 	c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator)
 }
 
 func (s *RemoteSuite) TestFetchWildcard(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetBasicLocalRepositoryURL()},
 	})
 
@@ -72,7 +72,7 @@
 }
 
 func (s *RemoteSuite) TestFetchWildcardTags(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
 	})
 
@@ -91,7 +91,7 @@
 }
 
 func (s *RemoteSuite) TestFetch(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
 	})
 
@@ -105,7 +105,7 @@
 }
 
 func (s *RemoteSuite) TestFetchNonExistantReference(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
 	})
 
@@ -119,7 +119,7 @@
 }
 
 func (s *RemoteSuite) TestFetchContext(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
 	})
 
@@ -135,7 +135,7 @@
 }
 
 func (s *RemoteSuite) TestFetchWithAllTags(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
 	})
 
@@ -155,7 +155,7 @@
 }
 
 func (s *RemoteSuite) TestFetchWithNoTags(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
 	})
 
@@ -171,7 +171,7 @@
 }
 
 func (s *RemoteSuite) TestFetchWithDepth(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetBasicLocalRepositoryURL()},
 	})
 
@@ -212,7 +212,7 @@
 	sto := memory.NewStorage()
 	buf := bytes.NewBuffer(nil)
 
-	r := newRemote(sto, &config.RemoteConfig{Name: "foo", URLs: []string{url}})
+	r := NewRemote(sto, &config.RemoteConfig{Name: "foo", URLs: []string{url}})
 
 	refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
 	err := r.Fetch(&FetchOptions{
@@ -248,7 +248,7 @@
 	mock := &mockPackfileWriter{Storer: fss}
 
 	url := s.GetBasicLocalRepositoryURL()
-	r := newRemote(mock, &config.RemoteConfig{Name: "foo", URLs: []string{url}})
+	r := NewRemote(mock, &config.RemoteConfig{Name: "foo", URLs: []string{url}})
 
 	refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
 	err = r.Fetch(&FetchOptions{
@@ -276,7 +276,7 @@
 }
 
 func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDateButStillUpdateLocalRemoteRefs(c *C) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		URLs: []string{s.GetBasicLocalRepositoryURL()},
 	})
 
@@ -313,7 +313,7 @@
 }
 
 func (s *RemoteSuite) doTestFetchNoErrAlreadyUpToDate(c *C, url string) {
-	r := newRemote(memory.NewStorage(), &config.RemoteConfig{URLs: []string{url}})
+	r := NewRemote(memory.NewStorage(), &config.RemoteConfig{URLs: []string{url}})
 
 	o := &FetchOptions{
 		RefSpecs: []config.RefSpec{
@@ -328,7 +328,7 @@
 }
 
 func (s *RemoteSuite) testFetchFastForward(c *C, sto storage.Storer) {
-	r := newRemote(sto, &config.RemoteConfig{
+	r := NewRemote(sto, &config.RemoteConfig{
 		URLs: []string{s.GetBasicLocalRepositoryURL()},
 	})
 
@@ -386,7 +386,7 @@
 }
 
 func (s *RemoteSuite) TestString(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{
+	r := NewRemote(nil, &config.RemoteConfig{
 		Name: "foo",
 		URLs: []string{"https://github.com/git-fixtures/basic.git"},
 	})
@@ -405,7 +405,7 @@
 	srcFs := fixtures.Basic().One().DotGit()
 	sto := filesystem.NewStorage(srcFs, cache.NewObjectLRUDefault())
 
-	r := newRemote(sto, &config.RemoteConfig{
+	r := NewRemote(sto, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{url},
 	})
@@ -442,7 +442,7 @@
 	fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit()
 	sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
 
-	r := newRemote(sto, &config.RemoteConfig{
+	r := NewRemote(sto, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{url},
 	})
@@ -471,7 +471,7 @@
 	fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit()
 	sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
 
-	r := newRemote(sto, &config.RemoteConfig{
+	r := NewRemote(sto, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{url},
 	})
@@ -494,7 +494,7 @@
 	fs := fixtures.Basic().One().DotGit()
 	sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
 
-	r := newRemote(sto, &config.RemoteConfig{
+	r := NewRemote(sto, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{fs.Root()},
 	})
@@ -564,7 +564,7 @@
 	dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault())
 
 	url := dstFs.Root()
-	r := newRemote(sto, &config.RemoteConfig{
+	r := NewRemote(sto, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{url},
 	})
@@ -654,32 +654,32 @@
 }
 
 func (s *RemoteSuite) TestPushInvalidEndpoint(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}})
 	err := r.Push(&PushOptions{RemoteName: "foo"})
 	c.Assert(err, ErrorMatches, ".*invalid character.*")
 }
 
 func (s *RemoteSuite) TestPushNonExistentEndpoint(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}})
 	err := r.Push(&PushOptions{})
 	c.Assert(err, NotNil)
 }
 
 func (s *RemoteSuite) TestPushInvalidSchemaEndpoint(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "origin", URLs: []string{"qux://foo"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "origin", URLs: []string{"qux://foo"}})
 	err := r.Push(&PushOptions{})
 	c.Assert(err, ErrorMatches, ".*unsupported scheme.*")
 }
 
 func (s *RemoteSuite) TestPushInvalidFetchOptions(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
+	r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
 	invalid := config.RefSpec("^*$ñ")
 	err := r.Push(&PushOptions{RefSpecs: []config.RefSpec{invalid}})
 	c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator)
 }
 
 func (s *RemoteSuite) TestPushInvalidRefSpec(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{
+	r := NewRemote(nil, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{"some-url"},
 	})
@@ -692,7 +692,7 @@
 }
 
 func (s *RemoteSuite) TestPushWrongRemoteName(c *C) {
-	r := newRemote(nil, &config.RemoteConfig{
+	r := NewRemote(nil, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{"some-url"},
 	})
@@ -729,7 +729,7 @@
 
 func (s *RemoteSuite) TestList(c *C) {
 	repo := fixtures.Basic().One()
-	remote := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{repo.URL},
 	})
@@ -784,7 +784,7 @@
 		{nil, hashes[0:6]},
 	}
 
-	remote := newRemote(memory.NewStorage(), &config.RemoteConfig{
+	remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
 		Name: DefaultRemoteName,
 	})
 
@@ -817,7 +817,7 @@
 	fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit()
 	sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
 
-	r := newRemote(sto, &config.RemoteConfig{
+	r := NewRemote(sto, &config.RemoteConfig{
 		Name: DefaultRemoteName,
 		URLs: []string{url},
 	})
diff --git a/repository.go b/repository.go
index a94dc2f..2251d6c 100644
--- a/repository.go
+++ b/repository.go
@@ -451,7 +451,7 @@
 		return nil, ErrRemoteNotFound
 	}
 
-	return newRemote(r.Storer, c), nil
+	return NewRemote(r.Storer, c), nil
 }
 
 // Remotes returns a list with all the remotes
@@ -465,7 +465,7 @@
 
 	var i int
 	for _, c := range cfg.Remotes {
-		remotes[i] = newRemote(r.Storer, c)
+		remotes[i] = NewRemote(r.Storer, c)
 		i++
 	}
 
@@ -478,7 +478,7 @@
 		return nil, err
 	}
 
-	remote := newRemote(r.Storer, c)
+	remote := NewRemote(r.Storer, c)
 
 	cfg, err := r.Storer.Config()
 	if err != nil {
@@ -504,7 +504,7 @@
 		return nil, ErrAnonymousRemoteName
 	}
 
-	remote := newRemote(r.Storer, c)
+	remote := NewRemote(r.Storer, c)
 
 	return remote, nil
 }