chore(deps): bump github.com/go-git/go-git/v5 from 5.10.1 to 5.11.0 (#2898)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.10.1 to 5.11.0. - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.10.1...v5.11.0) --- updated-dependencies: - dependency-name: github.com/go-git/go-git/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
a7736ed523
commit
cfec1457ef
2
go.mod
2
go.mod
|
|
@ -19,7 +19,7 @@ require (
|
||||||
github.com/containerd/cgroups v1.1.0 // indirect
|
github.com/containerd/cgroups v1.1.0 // indirect
|
||||||
github.com/docker/docker v23.0.5+incompatible
|
github.com/docker/docker v23.0.5+incompatible
|
||||||
github.com/go-git/go-billy/v5 v5.5.0
|
github.com/go-git/go-billy/v5 v5.5.0
|
||||||
github.com/go-git/go-git/v5 v5.10.1
|
github.com/go-git/go-git/v5 v5.11.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/go-containerregistry v0.15.2
|
github.com/google/go-containerregistry v0.15.2
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -267,8 +267,8 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmS
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||||
github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk=
|
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
|
||||||
github.com/go-git/go-git/v5 v5.10.1/go.mod h1:uEuHjxkHap8kAl//V5F/nNWwqIYtP/402ddd05mp0wg=
|
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ func (b *Branch) Validate() error {
|
||||||
return errBranchInvalidRebase
|
return errBranchInvalidRebase
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return plumbing.NewBranchReferenceName(b.Name).Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Branch) marshal() *format.Subsection {
|
func (b *Branch) marshal() *format.Subsection {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5/osfs"
|
"github.com/go-git/go-billy/v5/osfs"
|
||||||
"github.com/go-git/go-git/v5/internal/url"
|
"github.com/go-git/go-git/v5/internal/url"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
format "github.com/go-git/go-git/v5/plumbing/format/config"
|
format "github.com/go-git/go-git/v5/plumbing/format/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -614,7 +615,7 @@ func (c *RemoteConfig) Validate() error {
|
||||||
c.Fetch = []RefSpec{RefSpec(fmt.Sprintf(DefaultFetchRefSpec, c.Name))}
|
c.Fetch = []RefSpec{RefSpec(fmt.Sprintf(DefaultFetchRefSpec, c.Name))}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return plumbing.NewRemoteHEADReferenceName(c.Name).Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RemoteConfig) unmarshal(s *format.Subsection) error {
|
func (c *RemoteConfig) unmarshal(s *format.Subsection) error {
|
||||||
|
|
|
||||||
|
|
@ -317,8 +317,8 @@ func getFileStatsFromFilePatches(filePatches []fdiff.FilePatch) FileStats {
|
||||||
// File is deleted.
|
// File is deleted.
|
||||||
cs.Name = from.Path()
|
cs.Name = from.Path()
|
||||||
} else if from.Path() != to.Path() {
|
} else if from.Path() != to.Path() {
|
||||||
// File is renamed. Not supported.
|
// File is renamed.
|
||||||
// cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path())
|
cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path())
|
||||||
} else {
|
} else {
|
||||||
cs.Name = from.Path()
|
cs.Name = from.Path()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package plumbing
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,6 +30,9 @@ var RefRevParseRules = []string{
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrReferenceNotFound = errors.New("reference not found")
|
ErrReferenceNotFound = errors.New("reference not found")
|
||||||
|
|
||||||
|
// ErrInvalidReferenceName is returned when a reference name is invalid.
|
||||||
|
ErrInvalidReferenceName = errors.New("invalid reference name")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReferenceType reference type's
|
// ReferenceType reference type's
|
||||||
|
|
@ -124,6 +128,91 @@ func (r ReferenceName) Short() string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctrlSeqs = regexp.MustCompile(`[\000-\037\177]`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate validates a reference name.
|
||||||
|
// This follows the git-check-ref-format rules.
|
||||||
|
// See https://git-scm.com/docs/git-check-ref-format
|
||||||
|
//
|
||||||
|
// It is important to note that this function does not check if the reference
|
||||||
|
// exists in the repository.
|
||||||
|
// It only checks if the reference name is valid.
|
||||||
|
// This functions does not support the --refspec-pattern, --normalize, and
|
||||||
|
// --allow-onelevel options.
|
||||||
|
//
|
||||||
|
// Git imposes the following rules on how references are named:
|
||||||
|
//
|
||||||
|
// 1. They can include slash / for hierarchical (directory) grouping, but no
|
||||||
|
// slash-separated component can begin with a dot . or end with the
|
||||||
|
// sequence .lock.
|
||||||
|
// 2. They must contain at least one /. This enforces the presence of a
|
||||||
|
// category like heads/, tags/ etc. but the actual names are not
|
||||||
|
// restricted. If the --allow-onelevel option is used, this rule is
|
||||||
|
// waived.
|
||||||
|
// 3. They cannot have two consecutive dots .. anywhere.
|
||||||
|
// 4. They cannot have ASCII control characters (i.e. bytes whose values are
|
||||||
|
// lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon :
|
||||||
|
// anywhere.
|
||||||
|
// 5. They cannot have question-mark ?, asterisk *, or open bracket [
|
||||||
|
// anywhere. See the --refspec-pattern option below for an exception to this
|
||||||
|
// rule.
|
||||||
|
// 6. They cannot begin or end with a slash / or contain multiple consecutive
|
||||||
|
// slashes (see the --normalize option below for an exception to this rule).
|
||||||
|
// 7. They cannot end with a dot ..
|
||||||
|
// 8. They cannot contain a sequence @{.
|
||||||
|
// 9. They cannot be the single character @.
|
||||||
|
// 10. They cannot contain a \.
|
||||||
|
func (r ReferenceName) Validate() error {
|
||||||
|
s := string(r)
|
||||||
|
if len(s) == 0 {
|
||||||
|
return ErrInvalidReferenceName
|
||||||
|
}
|
||||||
|
|
||||||
|
// HEAD is a special case
|
||||||
|
if r == HEAD {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rule 7
|
||||||
|
if strings.HasSuffix(s, ".") {
|
||||||
|
return ErrInvalidReferenceName
|
||||||
|
}
|
||||||
|
|
||||||
|
// rule 2
|
||||||
|
parts := strings.Split(s, "/")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return ErrInvalidReferenceName
|
||||||
|
}
|
||||||
|
|
||||||
|
isBranch := r.IsBranch()
|
||||||
|
isTag := r.IsTag()
|
||||||
|
for _, part := range parts {
|
||||||
|
// rule 6
|
||||||
|
if len(part) == 0 {
|
||||||
|
return ErrInvalidReferenceName
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(part, ".") || // rule 1
|
||||||
|
strings.Contains(part, "..") || // rule 3
|
||||||
|
ctrlSeqs.MatchString(part) || // rule 4
|
||||||
|
strings.ContainsAny(part, "~^:?*[ \t\n") || // rule 4 & 5
|
||||||
|
strings.Contains(part, "@{") || // rule 8
|
||||||
|
part == "@" || // rule 9
|
||||||
|
strings.Contains(part, "\\") || // rule 10
|
||||||
|
strings.HasSuffix(part, ".lock") { // rule 1
|
||||||
|
return ErrInvalidReferenceName
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBranch || isTag) && strings.HasPrefix(part, "-") { // branches & tags can't start with -
|
||||||
|
return ErrInvalidReferenceName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HEAD ReferenceName = "HEAD"
|
HEAD ReferenceName = "HEAD"
|
||||||
Master ReferenceName = "refs/heads/master"
|
Master ReferenceName = "refs/heads/master"
|
||||||
|
|
|
||||||
|
|
@ -1070,7 +1070,7 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe
|
||||||
return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
|
return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
ff, err := isFastForward(s, cmd.Old, cmd.New)
|
ff, err := isFastForward(s, cmd.Old, cmd.New, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1082,14 +1082,28 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) {
|
func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash, earliestShallow *plumbing.Hash) (bool, error) {
|
||||||
c, err := object.GetCommit(s, new)
|
c, err := object.GetCommit(s, new)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parentsToIgnore := []plumbing.Hash{}
|
||||||
|
if earliestShallow != nil {
|
||||||
|
earliestCommit, err := object.GetCommit(s, *earliestShallow)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parentsToIgnore = earliestCommit.ParentHashes
|
||||||
|
}
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
iter := object.NewCommitPreorderIter(c, nil, nil)
|
// stop iterating at the earlist shallow commit, ignoring its parents
|
||||||
|
// note: when pull depth is smaller than the number of new changes on the remote, this fails due to missing parents.
|
||||||
|
// as far as i can tell, without the commits in-between the shallow pull and the earliest shallow, there's no
|
||||||
|
// real way of telling whether it will be a fast-forward merge.
|
||||||
|
iter := object.NewCommitPreorderIter(c, nil, parentsToIgnore)
|
||||||
err = iter.ForEach(func(c *object.Commit) error {
|
err = iter.ForEach(func(c *object.Commit) error {
|
||||||
if c.Hash != old {
|
if c.Hash != old {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -1205,7 +1219,7 @@ func (r *Remote) updateLocalReferenceStorage(
|
||||||
// If the ref exists locally as a non-tag and force is not
|
// If the ref exists locally as a non-tag and force is not
|
||||||
// specified, only update if the new ref is an ancestor of the old
|
// specified, only update if the new ref is an ancestor of the old
|
||||||
if old != nil && !old.Name().IsTag() && !force && !spec.IsForceUpdate() {
|
if old != nil && !old.Name().IsTag() && !force && !spec.IsForceUpdate() {
|
||||||
ff, err := isFastForward(r.s, old.Hash(), new.Hash())
|
ff, err := isFastForward(r.s, old.Hash(), new.Hash(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return updated, err
|
return updated, err
|
||||||
}
|
}
|
||||||
|
|
@ -1390,7 +1404,6 @@ func pushHashes(
|
||||||
useRefDeltas bool,
|
useRefDeltas bool,
|
||||||
allDelete bool,
|
allDelete bool,
|
||||||
) (*packp.ReportStatus, error) {
|
) (*packp.ReportStatus, error) {
|
||||||
|
|
||||||
rd, wr := io.Pipe()
|
rd, wr := io.Pipe()
|
||||||
|
|
||||||
config, err := s.Config()
|
config, err := s.Config()
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,10 @@ func InitWithOptions(s storage.Storer, worktree billy.Filesystem, options InitOp
|
||||||
options.DefaultBranch = plumbing.Master
|
options.DefaultBranch = plumbing.Master
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := options.DefaultBranch.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
r := newRepository(s, worktree)
|
r := newRepository(s, worktree)
|
||||||
_, err := r.Reference(plumbing.HEAD, false)
|
_, err := r.Reference(plumbing.HEAD, false)
|
||||||
switch err {
|
switch err {
|
||||||
|
|
@ -724,7 +728,10 @@ func (r *Repository) DeleteBranch(name string) error {
|
||||||
// CreateTag creates a tag. If opts is included, the tag is an annotated tag,
|
// CreateTag creates a tag. If opts is included, the tag is an annotated tag,
|
||||||
// otherwise a lightweight tag is created.
|
// otherwise a lightweight tag is created.
|
||||||
func (r *Repository) CreateTag(name string, hash plumbing.Hash, opts *CreateTagOptions) (*plumbing.Reference, error) {
|
func (r *Repository) CreateTag(name string, hash plumbing.Hash, opts *CreateTagOptions) (*plumbing.Reference, error) {
|
||||||
rname := plumbing.ReferenceName(path.Join("refs", "tags", name))
|
rname := plumbing.NewTagReferenceName(name)
|
||||||
|
if err := rname.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
_, err := r.Storer.Reference(rname)
|
_, err := r.Storer.Reference(rname)
|
||||||
switch err {
|
switch err {
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,19 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5/osfs"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||||
"github.com/go-git/go-git/v5/storage"
|
"github.com/go-git/go-git/v5/storage"
|
||||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5"
|
"github.com/go-git/go-billy/v5"
|
||||||
|
"github.com/go-git/go-billy/v5/helper/chroot"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -81,6 +82,10 @@ type Options struct {
|
||||||
// KeepDescriptors makes the file descriptors to be reused but they will
|
// KeepDescriptors makes the file descriptors to be reused but they will
|
||||||
// need to be manually closed calling Close().
|
// need to be manually closed calling Close().
|
||||||
KeepDescriptors bool
|
KeepDescriptors bool
|
||||||
|
// AlternatesFS provides the billy filesystem to be used for Git Alternates.
|
||||||
|
// If none is provided, it falls back to using the underlying instance used for
|
||||||
|
// DotGit.
|
||||||
|
AlternatesFS billy.Filesystem
|
||||||
}
|
}
|
||||||
|
|
||||||
// The DotGit type represents a local git repository on disk. This
|
// The DotGit type represents a local git repository on disk. This
|
||||||
|
|
@ -1146,28 +1151,55 @@ func (d *DotGit) Alternates() ([]*DotGit, error) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
fs := d.options.AlternatesFS
|
||||||
|
if fs == nil {
|
||||||
|
fs = d.fs
|
||||||
|
}
|
||||||
|
|
||||||
var alternates []*DotGit
|
var alternates []*DotGit
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
|
||||||
// Read alternate paths line-by-line and create DotGit objects.
|
// Read alternate paths line-by-line and create DotGit objects.
|
||||||
scanner := bufio.NewScanner(f)
|
scanner := bufio.NewScanner(f)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
path := scanner.Text()
|
path := scanner.Text()
|
||||||
if !filepath.IsAbs(path) {
|
|
||||||
// For relative paths, we can perform an internal conversion to
|
// Avoid creating multiple dotgits for the same alternative path.
|
||||||
// slash so that they work cross-platform.
|
if _, ok := seen[path]; ok {
|
||||||
slashPath := filepath.ToSlash(path)
|
continue
|
||||||
// If the path is not absolute, it must be relative to object
|
|
||||||
// database (.git/objects/info).
|
|
||||||
// https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html
|
|
||||||
// Hence, derive a path relative to DotGit's root.
|
|
||||||
// "../../../reponame/.git/" -> "../../reponame/.git"
|
|
||||||
// Remove the first ../
|
|
||||||
relpath := filepath.Join(strings.Split(slashPath, "/")[1:]...)
|
|
||||||
normalPath := filepath.FromSlash(relpath)
|
|
||||||
path = filepath.Join(d.fs.Root(), normalPath)
|
|
||||||
}
|
}
|
||||||
fs := osfs.New(filepath.Dir(path))
|
|
||||||
alternates = append(alternates, New(fs))
|
seen[path] = struct{}{}
|
||||||
|
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
// Handling absolute paths should be straight-forward. However, the default osfs (Chroot)
|
||||||
|
// tries to concatenate an abs path with the root path in some operations (e.g. Stat),
|
||||||
|
// which leads to unexpected errors. Therefore, make the path relative to the current FS instead.
|
||||||
|
if reflect.TypeOf(fs) == reflect.TypeOf(&chroot.ChrootHelper{}) {
|
||||||
|
path, err = filepath.Rel(fs.Root(), path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot make path %q relative: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// By Git conventions, relative paths should be based on the object database (.git/objects/info)
|
||||||
|
// location as per: https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html
|
||||||
|
// However, due to the nature of go-git and its filesystem handling via Billy, paths cannot
|
||||||
|
// cross its "chroot boundaries". Therefore, ignore any "../" and treat the path from the
|
||||||
|
// fs root. If this is not correct based on the dotgit fs, set a different one via AlternatesFS.
|
||||||
|
abs := filepath.Join(string(filepath.Separator), filepath.ToSlash(path))
|
||||||
|
path = filepath.FromSlash(abs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligns with upstream behavior: exit if target path is not a valid directory.
|
||||||
|
if fi, err := fs.Stat(path); err != nil || !fi.IsDir() {
|
||||||
|
return nil, fmt.Errorf("invalid object directory %q: %w", path, err)
|
||||||
|
}
|
||||||
|
afs, err := fs.Chroot(filepath.Dir(path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot chroot %q: %w", path, err)
|
||||||
|
}
|
||||||
|
alternates = append(alternates, New(afs))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = scanner.Err(); err != nil {
|
if err = scanner.Err(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ type Options struct {
|
||||||
// LargeObjectThreshold maximum object size (in bytes) that will be read in to memory.
|
// LargeObjectThreshold maximum object size (in bytes) that will be read in to memory.
|
||||||
// If left unset or set to 0 there is no limit
|
// If left unset or set to 0 there is no limit
|
||||||
LargeObjectThreshold int64
|
LargeObjectThreshold int64
|
||||||
|
// AlternatesFS provides the billy filesystem to be used for Git Alternates.
|
||||||
|
// If none is provided, it falls back to using the underlying instance used for
|
||||||
|
// DotGit.
|
||||||
|
AlternatesFS billy.Filesystem
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStorage returns a new Storage backed by a given `fs.Filesystem` and cache.
|
// NewStorage returns a new Storage backed by a given `fs.Filesystem` and cache.
|
||||||
|
|
@ -49,6 +53,7 @@ func NewStorage(fs billy.Filesystem, cache cache.Object) *Storage {
|
||||||
func NewStorageWithOptions(fs billy.Filesystem, cache cache.Object, ops Options) *Storage {
|
func NewStorageWithOptions(fs billy.Filesystem, cache cache.Object, ops Options) *Storage {
|
||||||
dirOps := dotgit.Options{
|
dirOps := dotgit.Options{
|
||||||
ExclusiveAccess: ops.ExclusiveAccess,
|
ExclusiveAccess: ops.ExclusiveAccess,
|
||||||
|
AlternatesFS: ops.AlternatesFS,
|
||||||
}
|
}
|
||||||
dir := dotgit.NewWithOptions(fs, dirOps)
|
dir := dotgit.NewWithOptions(fs, dirOps)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5"
|
"github.com/go-git/go-billy/v5"
|
||||||
|
|
@ -95,7 +96,15 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
|
||||||
|
|
||||||
head, err := w.r.Head()
|
head, err := w.r.Head()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash())
|
// if we don't have a shallows list, just ignore it
|
||||||
|
shallowList, _ := w.r.Storer.Shallow()
|
||||||
|
|
||||||
|
var earliestShallow *plumbing.Hash
|
||||||
|
if len(shallowList) > 0 {
|
||||||
|
earliestShallow = &shallowList[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash(), earliestShallow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +113,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
|
||||||
return NoErrAlreadyUpToDate
|
return NoErrAlreadyUpToDate
|
||||||
}
|
}
|
||||||
|
|
||||||
ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash())
|
ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash(), earliestShallow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +197,12 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error {
|
||||||
|
|
||||||
return w.Reset(ro)
|
return w.Reset(ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Worktree) createBranch(opts *CheckoutOptions) error {
|
func (w *Worktree) createBranch(opts *CheckoutOptions) error {
|
||||||
|
if err := opts.Branch.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
_, err := w.r.Storer.Reference(opts.Branch)
|
_, err := w.r.Storer.Reference(opts.Branch)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("a branch named %q already exists", opts.Branch)
|
return fmt.Errorf("a branch named %q already exists", opts.Branch)
|
||||||
|
|
@ -381,6 +395,9 @@ func (w *Worktree) resetWorktree(t *object.Tree) error {
|
||||||
b := newIndexBuilder(idx)
|
b := newIndexBuilder(idx)
|
||||||
|
|
||||||
for _, ch := range changes {
|
for _, ch := range changes {
|
||||||
|
if err := w.validChange(ch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := w.checkoutChange(ch, t, b); err != nil {
|
if err := w.checkoutChange(ch, t, b); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -390,6 +407,104 @@ func (w *Worktree) resetWorktree(t *object.Tree) error {
|
||||||
return w.r.Storer.SetIndex(idx)
|
return w.r.Storer.SetIndex(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// worktreeDeny is a list of paths that are not allowed
|
||||||
|
// to be used when resetting the worktree.
|
||||||
|
var worktreeDeny = map[string]struct{}{
|
||||||
|
// .git
|
||||||
|
GitDirName: {},
|
||||||
|
|
||||||
|
// For other historical reasons, file names that do not conform to the 8.3
|
||||||
|
// format (up to eight characters for the basename, three for the file
|
||||||
|
// extension, certain characters not allowed such as `+`, etc) are associated
|
||||||
|
// with a so-called "short name", at least on the `C:` drive by default.
|
||||||
|
// Which means that `git~1/` is a valid way to refer to `.git/`.
|
||||||
|
"git~1": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// validPath checks whether paths are valid.
|
||||||
|
// The rules around invalid paths could differ from upstream based on how
|
||||||
|
// filesystems are managed within go-git, but they are largely the same.
|
||||||
|
//
|
||||||
|
// For upstream rules:
|
||||||
|
// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/read-cache.c#L946
|
||||||
|
// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/path.c#L1383
|
||||||
|
func validPath(paths ...string) error {
|
||||||
|
for _, p := range paths {
|
||||||
|
parts := strings.FieldsFunc(p, func(r rune) bool { return (r == '\\' || r == '/') })
|
||||||
|
if _, denied := worktreeDeny[strings.ToLower(parts[0])]; denied {
|
||||||
|
return fmt.Errorf("invalid path prefix: %q", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Volume names are not supported, in both formats: \\ and <DRIVE_LETTER>:.
|
||||||
|
if vol := filepath.VolumeName(p); vol != "" {
|
||||||
|
return fmt.Errorf("invalid path: %q", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !windowsValidPath(parts[0]) {
|
||||||
|
return fmt.Errorf("invalid path: %q", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, part := range parts {
|
||||||
|
if part == ".." {
|
||||||
|
return fmt.Errorf("invalid path %q: cannot use '..'", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// windowsPathReplacer defines the chars that need to be replaced
|
||||||
|
// as part of windowsValidPath.
|
||||||
|
var windowsPathReplacer *strings.Replacer
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
windowsPathReplacer = strings.NewReplacer(" ", "", ".", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowsValidPath(part string) bool {
|
||||||
|
if len(part) > 3 && strings.EqualFold(part[:4], GitDirName) {
|
||||||
|
// For historical reasons, file names that end in spaces or periods are
|
||||||
|
// automatically trimmed. Therefore, `.git . . ./` is a valid way to refer
|
||||||
|
// to `.git/`.
|
||||||
|
if windowsPathReplacer.Replace(part[4:]) == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// For yet other historical reasons, NTFS supports so-called "Alternate Data
|
||||||
|
// Streams", i.e. metadata associated with a given file, referred to via
|
||||||
|
// `<filename>:<stream-name>:<stream-type>`. There exists a default stream
|
||||||
|
// type for directories, allowing `.git/` to be accessed via
|
||||||
|
// `.git::$INDEX_ALLOCATION/`.
|
||||||
|
//
|
||||||
|
// For performance reasons, _all_ Alternate Data Streams of `.git/` are
|
||||||
|
// forbidden, not just `::$INDEX_ALLOCATION`.
|
||||||
|
if len(part) > 4 && part[4:5] == ":" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Worktree) validChange(ch merkletrie.Change) error {
|
||||||
|
action, err := ch.Action()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case merkletrie.Delete:
|
||||||
|
return validPath(ch.From.String())
|
||||||
|
case merkletrie.Insert:
|
||||||
|
return validPath(ch.To.String())
|
||||||
|
case merkletrie.Modify:
|
||||||
|
return validPath(ch.From.String(), ch.To.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Worktree) checkoutChange(ch merkletrie.Change, t *object.Tree, idx *indexBuilder) error {
|
func (w *Worktree) checkoutChange(ch merkletrie.Change, t *object.Tree, idx *indexBuilder) error {
|
||||||
a, err := ch.Action()
|
a, err := ch.Action()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -562,6 +677,11 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) {
|
func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) {
|
||||||
|
// https://github.com/git/git/commit/10ecfa76491e4923988337b2e2243b05376b40de
|
||||||
|
if strings.EqualFold(f.Name, gitmodulesFile) {
|
||||||
|
return ErrGitModulesSymlink
|
||||||
|
}
|
||||||
|
|
||||||
from, err := f.Reader()
|
from, err := f.Reader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -468,7 +468,7 @@ github.com/go-git/go-billy/v5/helper/polyfill
|
||||||
github.com/go-git/go-billy/v5/memfs
|
github.com/go-git/go-billy/v5/memfs
|
||||||
github.com/go-git/go-billy/v5/osfs
|
github.com/go-git/go-billy/v5/osfs
|
||||||
github.com/go-git/go-billy/v5/util
|
github.com/go-git/go-billy/v5/util
|
||||||
# github.com/go-git/go-git/v5 v5.10.1
|
# github.com/go-git/go-git/v5 v5.11.0
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/go-git/go-git/v5
|
github.com/go-git/go-git/v5
|
||||||
github.com/go-git/go-git/v5/config
|
github.com/go-git/go-git/v5/config
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue