aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SECURITY.md38
-rw-r--r--options.go3
-rw-r--r--plumbing/format/gitignore/dir.go16
-rw-r--r--plumbing/format/gitignore/dir_test.go52
-rw-r--r--repository.go9
-rw-r--r--repository_test.go86
-rw-r--r--submodule.go14
-rw-r--r--submodule_test.go26
8 files changed, 218 insertions, 26 deletions
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..0d2f8d0
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,38 @@
+# go-git Security Policy
+
+The purpose of this security policy is to outline `go-git`'s process
+for reporting, handling and disclosing security sensitive information.
+
+## Supported Versions
+
+The project follows a version support policy where only the latest minor
+release is actively supported. Therefore, only issues that impact the latest
+minor release will be fixed. Users are encouraged to upgrade to the latest
+minor/patch release to benefit from the most up-to-date features, bug fixes,
+and security enhancements.​
+
+The supported versions policy applies to both the `go-git` library and its
+associated repositories within the `go-git` org.
+
+## Reporting Security Issues
+
+Please report any security vulnerabilities or potential weaknesses in `go-git`
+privately via go-git-security@googlegroups.com. Do not publicly disclose the
+details of the vulnerability until a fix has been implemented and released.
+
+During the process the project maintainers will investigate the report, so please
+provide detailed information, including steps to reproduce, affected versions, and any mitigations if known.
+
+The project maintainers will acknowledge the receipt of the report and work with
+the reporter to validate and address the issue.
+
+Please note that `go-git` does not have any bounty programs, and therefore do
+not provide financial compensation for disclosures.
+
+## Security Disclosure Process
+
+The project maintainers will make every effort to promptly address security issues.
+
+Once a security vulnerability is fixed, a security advisory will be published to notify users and provide appropriate mitigation measures.
+
+All `go-git` advisories can be found at https://github.com/go-git/go-git/security/advisories.
diff --git a/options.go b/options.go
index d607b30..6d802d1 100644
--- a/options.go
+++ b/options.go
@@ -62,6 +62,9 @@ type CloneOptions struct {
// within, using their default settings. This option is ignored if the
// cloned repository does not have a worktree.
RecurseSubmodules SubmoduleRescursivity
+ // ShallowSubmodules limit cloning submodules to the 1 level of depth.
+ // It matches the git command --shallow-submodules.
+ ShallowSubmodules bool
// Progress is where the human readable information sent by the server is
// stored, if nil nothing is stored and the capability (if supported)
// no-progress, is sent to the server to avoid send this information.
diff --git a/plumbing/format/gitignore/dir.go b/plumbing/format/gitignore/dir.go
index bf6a1c1..3c4469a 100644
--- a/plumbing/format/gitignore/dir.go
+++ b/plumbing/format/gitignore/dir.go
@@ -5,6 +5,7 @@ import (
"bytes"
"io"
"os"
+ "os/user"
"strings"
"github.com/go-git/go-billy/v5"
@@ -27,9 +28,18 @@ const (
func readIgnoreFile(fs billy.Filesystem, path []string, ignoreFile string) (ps []Pattern, err error) {
if strings.HasPrefix(ignoreFile, "~") {
- home, err := os.UserHomeDir()
- if err == nil {
- ignoreFile = strings.Replace(ignoreFile, "~", home, 1)
+ firstSlash := strings.Index(ignoreFile, "/")
+ if firstSlash == 1 {
+ home, err := os.UserHomeDir()
+ if err == nil {
+ ignoreFile = strings.Replace(ignoreFile, "~", home, 1)
+ }
+ } else if firstSlash > 1 {
+ username := ignoreFile[1:firstSlash]
+ userAccount, err := user.Lookup(username)
+ if err == nil {
+ ignoreFile = strings.Replace(ignoreFile, ignoreFile[:firstSlash], userAccount.HomeDir, 1)
+ }
}
}
diff --git a/plumbing/format/gitignore/dir_test.go b/plumbing/format/gitignore/dir_test.go
index 4bbba68..465c571 100644
--- a/plumbing/format/gitignore/dir_test.go
+++ b/plumbing/format/gitignore/dir_test.go
@@ -2,7 +2,9 @@ package gitignore
import (
"os"
+ "os/user"
"strconv"
+ "strings"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
@@ -12,7 +14,8 @@ import (
type MatcherSuite struct {
GFS billy.Filesystem // git repository root
RFS billy.Filesystem // root that contains user home
- RFSR billy.Filesystem // root that contains user home, but with with relative ~/.gitignore_global
+ RFSR billy.Filesystem // root that contains user home, but with relative ~/.gitignore_global
+ RFSU billy.Filesystem // root that contains user home, but with relative ~user/.gitignore_global
MCFS billy.Filesystem // root that contains user home, but missing ~/.gitconfig
MEFS billy.Filesystem // root that contains user home, but missing excludesfile entry
MIFS billy.Filesystem // root that contains user home, but missing .gitignore
@@ -144,6 +147,37 @@ func (s *MatcherSuite) SetUpTest(c *C) {
s.RFSR = fs
+ // root that contains user home, but with relative ~user/.gitignore_global
+ fs = memfs.New()
+ err = fs.MkdirAll(home, os.ModePerm)
+ c.Assert(err, IsNil)
+
+ f, err = fs.Create(fs.Join(home, gitconfigFile))
+ c.Assert(err, IsNil)
+ _, err = f.Write([]byte("[core]\n"))
+ c.Assert(err, IsNil)
+ currentUser, err := user.Current()
+ c.Assert(err, IsNil)
+ // remove domain for windows
+ username := currentUser.Username[strings.Index(currentUser.Username, "\\")+1:]
+ _, err = f.Write([]byte(" excludesfile = ~" + username + "/.gitignore_global" + "\n"))
+ c.Assert(err, IsNil)
+ err = f.Close()
+ c.Assert(err, IsNil)
+
+ f, err = fs.Create(fs.Join(home, ".gitignore_global"))
+ c.Assert(err, IsNil)
+ _, err = f.Write([]byte("# IntelliJ\n"))
+ c.Assert(err, IsNil)
+ _, err = f.Write([]byte(".idea/\n"))
+ c.Assert(err, IsNil)
+ _, err = f.Write([]byte("*.iml\n"))
+ c.Assert(err, IsNil)
+ err = f.Close()
+ c.Assert(err, IsNil)
+
+ s.RFSU = fs
+
// root that contains user home, but missing ~/.gitconfig
fs = memfs.New()
err = fs.MkdirAll(home, os.ModePerm)
@@ -255,14 +289,16 @@ func (s *MatcherSuite) TestDir_ReadPatterns(c *C) {
}
func (s *MatcherSuite) TestDir_ReadRelativeGlobalGitIgnore(c *C) {
- ps, err := LoadGlobalPatterns(s.RFSR)
- c.Assert(err, IsNil)
- c.Assert(ps, HasLen, 2)
+ for _, fs := range []billy.Filesystem{s.RFSR, s.RFSU} {
+ ps, err := LoadGlobalPatterns(fs)
+ c.Assert(err, IsNil)
+ c.Assert(ps, HasLen, 2)
- m := NewMatcher(ps)
- c.Assert(m.Match([]string{".idea/"}, true), Equals, false)
- c.Assert(m.Match([]string{"*.iml"}, true), Equals, true)
- c.Assert(m.Match([]string{"IntelliJ"}, true), Equals, false)
+ m := NewMatcher(ps)
+ c.Assert(m.Match([]string{".idea/"}, true), Equals, false)
+ c.Assert(m.Match([]string{"*.iml"}, true), Equals, true)
+ c.Assert(m.Match([]string{"IntelliJ"}, true), Equals, false)
+ }
}
func (s *MatcherSuite) TestDir_LoadGlobalPatterns(c *C) {
diff --git a/repository.go b/repository.go
index 17fa5dd..dd822a5 100644
--- a/repository.go
+++ b/repository.go
@@ -916,7 +916,13 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
if o.RecurseSubmodules != NoRecurseSubmodules {
if err := w.updateSubmodules(&SubmoduleUpdateOptions{
RecurseSubmodules: o.RecurseSubmodules,
- Auth: o.Auth,
+ Depth: func() int {
+ if o.ShallowSubmodules {
+ return 1
+ }
+ return 0
+ }(),
+ Auth: o.Auth,
}); err != nil {
return err
}
@@ -967,7 +973,6 @@ func (r *Repository) cloneRefSpec(o *CloneOptions) []config.RefSpec {
case o.SingleBranch && o.ReferenceName == plumbing.HEAD:
return []config.RefSpec{
config.RefSpec(fmt.Sprintf(refspecSingleBranchHEAD, o.RemoteName)),
- config.RefSpec(fmt.Sprintf(refspecSingleBranch, plumbing.Master.Short(), o.RemoteName)),
}
case o.SingleBranch:
return []config.RefSpec{
diff --git a/repository_test.go b/repository_test.go
index 965f028..d18816f 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -932,6 +932,43 @@ func (s *RepositorySuite) TestPlainCloneWithRecurseSubmodules(c *C) {
c.Assert(cfg.Submodules, HasLen, 2)
}
+func (s *RepositorySuite) TestPlainCloneWithShallowSubmodules(c *C) {
+ if testing.Short() {
+ c.Skip("skipping test in short mode.")
+ }
+
+ dir, clean := s.TemporalDir()
+ defer clean()
+
+ path := fixtures.ByTag("submodule").One().Worktree().Root()
+ mainRepo, err := PlainClone(dir, false, &CloneOptions{
+ URL: path,
+ RecurseSubmodules: 1,
+ ShallowSubmodules: true,
+ })
+ c.Assert(err, IsNil)
+
+ mainWorktree, err := mainRepo.Worktree()
+ c.Assert(err, IsNil)
+
+ submodule, err := mainWorktree.Submodule("basic")
+ c.Assert(err, IsNil)
+
+ subRepo, err := submodule.Repository()
+ c.Assert(err, IsNil)
+
+ lr, err := subRepo.Log(&LogOptions{})
+ c.Assert(err, IsNil)
+
+ commitCount := 0
+ for _, err := lr.Next(); err == nil; _, err = lr.Next() {
+ commitCount++
+ }
+ c.Assert(err, IsNil)
+
+ c.Assert(commitCount, Equals, 1)
+}
+
func (s *RepositorySuite) TestPlainCloneNoCheckout(c *C) {
dir, clean := s.TemporalDir()
defer clean()
@@ -1119,6 +1156,49 @@ func (s *RepositorySuite) testCloneSingleBranchAndNonHEADReference(c *C, ref str
c.Assert(branch.Hash().String(), Equals, "e8d3ffab552895c19b9fcf7aa264d277cde33881")
}
+func (s *RepositorySuite) TestCloneSingleBranchHEADMain(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+
+ head, err := r.Head()
+ c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
+ c.Assert(head, IsNil)
+
+ err = r.clone(context.Background(), &CloneOptions{
+ URL: s.GetLocalRepositoryURL(fixtures.ByTag("no-master-head").One()),
+ SingleBranch: true,
+ })
+
+ c.Assert(err, IsNil)
+
+ remotes, err := r.Remotes()
+ c.Assert(err, IsNil)
+ c.Assert(remotes, HasLen, 1)
+
+ cfg, err := r.Config()
+ c.Assert(err, IsNil)
+ c.Assert(cfg.Branches, HasLen, 1)
+ c.Assert(cfg.Branches["main"].Name, Equals, "main")
+ c.Assert(cfg.Branches["main"].Remote, Equals, "origin")
+ c.Assert(cfg.Branches["main"].Merge, Equals, plumbing.ReferenceName("refs/heads/main"))
+
+ head, err = r.Reference(plumbing.HEAD, false)
+ c.Assert(err, IsNil)
+ c.Assert(head, NotNil)
+ c.Assert(head.Type(), Equals, plumbing.SymbolicReference)
+ c.Assert(head.Target().String(), Equals, "refs/heads/main")
+
+ branch, err := r.Reference(head.Target(), false)
+ c.Assert(err, IsNil)
+ c.Assert(branch, NotNil)
+ c.Assert(branch.Hash().String(), Equals, "786dafbd351e587da1ae97e5fb9fbdf868b4a28f")
+
+ branch, err = r.Reference("refs/remotes/origin/HEAD", false)
+ c.Assert(err, IsNil)
+ c.Assert(branch, NotNil)
+ c.Assert(branch.Type(), Equals, plumbing.HashReference)
+ c.Assert(branch.Hash().String(), Equals, "786dafbd351e587da1ae97e5fb9fbdf868b4a28f")
+}
+
func (s *RepositorySuite) TestCloneSingleBranch(c *C) {
r, _ := Init(memory.NewStorage(), nil)
@@ -1154,12 +1234,6 @@ func (s *RepositorySuite) TestCloneSingleBranch(c *C) {
c.Assert(err, IsNil)
c.Assert(branch, NotNil)
c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
-
- branch, err = r.Reference("refs/remotes/origin/master", false)
- c.Assert(err, IsNil)
- c.Assert(branch, NotNil)
- c.Assert(branch.Type(), Equals, plumbing.HashReference)
- c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
}
func (s *RepositorySuite) TestCloneSingleTag(c *C) {
diff --git a/submodule.go b/submodule.go
index b0c4169..84f020d 100644
--- a/submodule.go
+++ b/submodule.go
@@ -5,13 +5,13 @@ import (
"context"
"errors"
"fmt"
- "net/url"
"path"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/index"
+ "github.com/go-git/go-git/v5/plumbing/transport"
)
var (
@@ -133,29 +133,29 @@ func (s *Submodule) Repository() (*Repository, error) {
return nil, err
}
- moduleURL, err := url.Parse(s.c.URL)
+ moduleEndpoint, err := transport.NewEndpoint(s.c.URL)
if err != nil {
return nil, err
}
- if !path.IsAbs(moduleURL.Path) {
+ if !path.IsAbs(moduleEndpoint.Path) && moduleEndpoint.Protocol == "file" {
remotes, err := s.w.r.Remotes()
if err != nil {
return nil, err
}
- rootURL, err := url.Parse(remotes[0].c.URLs[0])
+ rootEndpoint, err := transport.NewEndpoint(remotes[0].c.URLs[0])
if err != nil {
return nil, err
}
- rootURL.Path = path.Join(rootURL.Path, moduleURL.Path)
- *moduleURL = *rootURL
+ rootEndpoint.Path = path.Join(rootEndpoint.Path, moduleEndpoint.Path)
+ *moduleEndpoint = *rootEndpoint
}
_, err = r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
- URLs: []string{moduleURL.String()},
+ URLs: []string{moduleEndpoint.String()},
})
return r, err
diff --git a/submodule_test.go b/submodule_test.go
index 92943e1..0e88391 100644
--- a/submodule_test.go
+++ b/submodule_test.go
@@ -5,7 +5,10 @@ import (
"path/filepath"
"testing"
+ "github.com/go-git/go-billy/v5/memfs"
+ "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/storage/memory"
fixtures "github.com/go-git/go-git-fixtures/v4"
. "gopkg.in/check.v1"
@@ -258,3 +261,26 @@ func (s *SubmoduleSuite) TestSubmodulesFetchDepth(c *C) {
c.Assert(commitCount, Equals, 1)
}
+
+func (s *SubmoduleSuite) TestSubmoduleParseScp(c *C) {
+ repo := &Repository{
+ Storer: memory.NewStorage(),
+ wt: memfs.New(),
+ }
+ worktree := &Worktree{
+ Filesystem: memfs.New(),
+ r: repo,
+ }
+ submodule := &Submodule{
+ initialized: true,
+ c: nil,
+ w: worktree,
+ }
+
+ submodule.c = &config.Submodule{
+ URL: "git@github.com:username/submodule_repo",
+ }
+
+ _, err := submodule.Repository()
+ c.Assert(err, IsNil)
+}