aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-git
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2020-06-18 00:29:38 +0100
committerGitHub <noreply@github.com>2020-06-17 19:29:38 -0400
commit14261266901d388ef1f9709bf342e5a367824a70 (patch)
treea5c6740296078062083276228219b00be7c2a187 /vendor/github.com/go-git
parent6bf78d2b576a5caa77a670db9bbf2572d0272f25 (diff)
downloadgitea-14261266901d388ef1f9709bf342e5a367824a70.tar.gz
gitea-14261266901d388ef1f9709bf342e5a367824a70.zip
Update to go-git v5.1.0 (#11936)
Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'vendor/github.com/go-git')
-rw-r--r--vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md2
-rw-r--r--vendor/github.com/go-git/go-git/v5/README.md8
-rw-r--r--vendor/github.com/go-git/go-git/v5/config/config.go157
-rw-r--r--vendor/github.com/go-git/go-git/v5/config/refspec.go9
-rw-r--r--vendor/github.com/go-git/go-git/v5/go.mod1
-rw-r--r--vendor/github.com/go-git/go-git/v5/go.sum2
-rw-r--r--vendor/github.com/go-git/go-git/v5/options.go49
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/color/color.go38
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/format/diff/colorconfig.go97
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/format/diff/unified_encoder.go397
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go10
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/encoder.go10
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go5
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go15
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go7
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/hash.go10
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/object/change.go4
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go15
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/object/difftree.go67
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go2
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/object/rename.go813
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go35
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/advrefs.go8
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go7
-rw-r--r--vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go12
-rw-r--r--vendor/github.com/go-git/go-git/v5/remote.go42
-rw-r--r--vendor/github.com/go-git/go-git/v5/repository.go69
-rw-r--r--vendor/github.com/go-git/go-git/v5/storage/filesystem/config.go17
-rw-r--r--vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go11
-rw-r--r--vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go2
-rw-r--r--vendor/github.com/go-git/go-git/v5/submodule.go2
-rw-r--r--vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go12
32 files changed, 1657 insertions, 278 deletions
diff --git a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md
index 395088b7b9..2a72b501e2 100644
--- a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md
+++ b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md
@@ -101,7 +101,7 @@ is supported by go-git.
| http(s):// (smart) | ✔ |
| git:// | ✔ |
| ssh:// | ✔ |
-| file:// | ✔ |
+| file:// | partial | Warning: this is not pure Golang. This shells out to the `git` binary. |
| custom | ✔ |
| **other features** |
| gitignore | ✔ |
diff --git a/vendor/github.com/go-git/go-git/v5/README.md b/vendor/github.com/go-git/go-git/v5/README.md
index bca3277a07..ff0c9b72ba 100644
--- a/vendor/github.com/go-git/go-git/v5/README.md
+++ b/vendor/github.com/go-git/go-git/v5/README.md
@@ -1,9 +1,9 @@
![go-git logo](https://cdn.rawgit.com/src-d/artwork/02036484/go-git/files/go-git-github-readme-header.png)
-[![GoDoc](https://godoc.org/github.com/go-git/go-git/v5?status.svg)](https://godoc.org/github.com/src-d/go-git) [![Build Status](https://github.com/go-git/go-git/workflows/Test%20&%20Coverage/badge.svg)](https://github.com/go-git/go-git/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/src-d/go-git)](https://goreportcard.com/report/github.com/src-d/go-git)
+[![GoDoc](https://godoc.org/github.com/go-git/go-git/v5?status.svg)](https://pkg.go.dev/github.com/go-git/go-git/v5) [![Build Status](https://github.com/go-git/go-git/workflows/Test/badge.svg)](https://github.com/go-git/go-git/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/go-git/go-git)](https://goreportcard.com/report/github.com/go-git/go-git)
*go-git* is a highly extensible git implementation library written in **pure Go**.
-It can be used to manipulate git repositories at low level *(plumbing)* or high level *(porcelain)*, through an idiomatic Go API. It also supports several types of storage, such as in-memory filesystems, or custom implementations, thanks to the [`Storer`](https://godoc.org/github.com/go-git/go-git/v5/plumbing/storer) interface.
+It can be used to manipulate git repositories at low level *(plumbing)* or high level *(porcelain)*, through an idiomatic Go API. It also supports several types of storage, such as in-memory filesystems, or custom implementations, thanks to the [`Storer`](https://pkg.go.dev/github.com/go-git/go-git/v5/plumbing/storer) interface.
It's being actively developed since 2015 and is being used extensively by [Keybase](https://keybase.io/blog/encrypted-git-for-everyone), [Gitea](https://gitea.io/en-us/) or [Pulumi](https://github.com/search?q=org%3Apulumi+go-git&type=Code), and by many other libraries and tools.
@@ -12,7 +12,7 @@ Project Status
After the legal issues with the [`src-d`](https://github.com/src-d) organization, the lack of update for four months and the requirement to make a hard fork, the project is **now back to normality**.
-The project is currently actively maintained by individual contributors, including several of the original authors, but also backed by a new company `gitsigth` where `go-git` is a critical component used at scale.
+The project is currently actively maintained by individual contributors, including several of the original authors, but also backed by a new company, [gitsight](https://github.com/gitsight), where `go-git` is a critical component used at scale.
Comparison with git
@@ -37,7 +37,7 @@ import "github.com/go-git/go-git" // with go modules disabled
Examples
--------
-> Please note that the `CheckIfError` and `Info` functions used in the examples are from the [examples package](https://github.com/src-d/go-git/blob/master/_examples/common.go#L17) just to be used in the examples.
+> Please note that the `CheckIfError` and `Info` functions used in the examples are from the [examples package](https://github.com/go-git/go-git/blob/master/_examples/common.go#L19) just to be used in the examples.
### Basic example
diff --git a/vendor/github.com/go-git/go-git/v5/config/config.go b/vendor/github.com/go-git/go-git/v5/config/config.go
index bec35b0c27..7d6ab5886b 100644
--- a/vendor/github.com/go-git/go-git/v5/config/config.go
+++ b/vendor/github.com/go-git/go-git/v5/config/config.go
@@ -5,11 +5,16 @@ import (
"bytes"
"errors"
"fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"sort"
"strconv"
"github.com/go-git/go-git/v5/internal/url"
format "github.com/go-git/go-git/v5/plumbing/format/config"
+ "github.com/mitchellh/go-homedir"
)
const (
@@ -32,6 +37,16 @@ var (
ErrRemoteConfigEmptyName = errors.New("remote config: empty name")
)
+// Scope defines the scope of a config file, such as local, global or system.
+type Scope int
+
+// Available ConfigScope's
+const (
+ LocalScope Scope = iota
+ GlobalScope
+ SystemScope
+)
+
// Config contains the repository configuration
// https://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES
type Config struct {
@@ -46,6 +61,27 @@ type Config struct {
CommentChar string
}
+ User struct {
+ // Name is the personal name of the author and the commiter of a commit.
+ Name string
+ // Email is the email of the author and the commiter of a commit.
+ Email string
+ }
+
+ Author struct {
+ // Name is the personal name of the author of a commit.
+ Name string
+ // Email is the email of the author of a commit.
+ Email string
+ }
+
+ Committer struct {
+ // Name is the personal name of the commiter of a commit.
+ Name string
+ // Email is the email of the the commiter of a commit.
+ Email string
+ }
+
Pack struct {
// Window controls the size of the sliding window for delta
// compression. The default is 10. A value of 0 turns off
@@ -82,6 +118,77 @@ func NewConfig() *Config {
return config
}
+// ReadConfig reads a config file from a io.Reader.
+func ReadConfig(r io.Reader) (*Config, error) {
+ b, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+
+ cfg := NewConfig()
+ if err = cfg.Unmarshal(b); err != nil {
+ return nil, err
+ }
+
+ return cfg, nil
+}
+
+// LoadConfig loads a config file from a given scope. The returned Config,
+// contains exclusively information fom the given scope. If couldn't find a
+// config file to the given scope, a empty one is returned.
+func LoadConfig(scope Scope) (*Config, error) {
+ if scope == LocalScope {
+ return nil, fmt.Errorf("LocalScope should be read from the a ConfigStorer.")
+ }
+
+ files, err := Paths(scope)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, file := range files {
+ f, err := os.Open(file)
+ if err != nil {
+ if os.IsNotExist(err) {
+ continue
+ }
+
+ return nil, err
+ }
+
+ defer f.Close()
+ return ReadConfig(f)
+ }
+
+ return NewConfig(), nil
+}
+
+// Paths returns the config file location for a given scope.
+func Paths(scope Scope) ([]string, error) {
+ var files []string
+ switch scope {
+ case GlobalScope:
+ xdg := os.Getenv("XDG_CONFIG_HOME")
+ if xdg != "" {
+ files = append(files, filepath.Join(xdg, "git/config"))
+ }
+
+ home, err := homedir.Dir()
+ if err != nil {
+ return nil, err
+ }
+
+ files = append(files,
+ filepath.Join(home, ".gitconfig"),
+ filepath.Join(home, ".config/git/config"),
+ )
+ case SystemScope:
+ files = append(files, "/etc/gitconfig")
+ }
+
+ return files, nil
+}
+
// Validate validates the fields and sets the default values.
func (c *Config) Validate() error {
for name, r := range c.Remotes {
@@ -113,6 +220,9 @@ const (
branchSection = "branch"
coreSection = "core"
packSection = "pack"
+ userSection = "user"
+ authorSection = "author"
+ committerSection = "committer"
fetchKey = "fetch"
urlKey = "url"
bareKey = "bare"
@@ -121,6 +231,8 @@ const (
windowKey = "window"
mergeKey = "merge"
rebaseKey = "rebase"
+ nameKey = "name"
+ emailKey = "email"
// DefaultPackWindow holds the number of previous objects used to
// generate deltas. The value 10 is the same used by git command.
@@ -138,6 +250,7 @@ func (c *Config) Unmarshal(b []byte) error {
}
c.unmarshalCore()
+ c.unmarshalUser()
if err := c.unmarshalPack(); err != nil {
return err
}
@@ -160,6 +273,20 @@ func (c *Config) unmarshalCore() {
c.Core.CommentChar = s.Options.Get(commentCharKey)
}
+func (c *Config) unmarshalUser() {
+ s := c.Raw.Section(userSection)
+ c.User.Name = s.Options.Get(nameKey)
+ c.User.Email = s.Options.Get(emailKey)
+
+ s = c.Raw.Section(authorSection)
+ c.Author.Name = s.Options.Get(nameKey)
+ c.Author.Email = s.Options.Get(emailKey)
+
+ s = c.Raw.Section(committerSection)
+ c.Committer.Name = s.Options.Get(nameKey)
+ c.Committer.Email = s.Options.Get(emailKey)
+}
+
func (c *Config) unmarshalPack() error {
s := c.Raw.Section(packSection)
window := s.Options.Get(windowKey)
@@ -220,6 +347,7 @@ func (c *Config) unmarshalBranches() error {
// Marshal returns Config encoded as a git-config file.
func (c *Config) Marshal() ([]byte, error) {
c.marshalCore()
+ c.marshalUser()
c.marshalPack()
c.marshalRemotes()
c.marshalSubmodules()
@@ -242,6 +370,35 @@ func (c *Config) marshalCore() {
}
}
+func (c *Config) marshalUser() {
+ s := c.Raw.Section(userSection)
+ if c.User.Name != "" {
+ s.SetOption(nameKey, c.User.Name)
+ }
+
+ if c.User.Email != "" {
+ s.SetOption(emailKey, c.User.Email)
+ }
+
+ s = c.Raw.Section(authorSection)
+ if c.Author.Name != "" {
+ s.SetOption(nameKey, c.Author.Name)
+ }
+
+ if c.Author.Email != "" {
+ s.SetOption(emailKey, c.Author.Email)
+ }
+
+ s = c.Raw.Section(committerSection)
+ if c.Committer.Name != "" {
+ s.SetOption(nameKey, c.Committer.Name)
+ }
+
+ if c.Committer.Email != "" {
+ s.SetOption(emailKey, c.Committer.Email)
+ }
+}
+
func (c *Config) marshalPack() {
s := c.Raw.Section(packSection)
if c.Pack.Window != DefaultPackWindow {
diff --git a/vendor/github.com/go-git/go-git/v5/config/refspec.go b/vendor/github.com/go-git/go-git/v5/config/refspec.go
index 87cf2a601f..4bfaa37bbb 100644
--- a/vendor/github.com/go-git/go-git/v5/config/refspec.go
+++ b/vendor/github.com/go-git/go-git/v5/config/refspec.go
@@ -25,7 +25,7 @@ var (
// reference even if it isn’t a fast-forward.
// eg.: "+refs/heads/*:refs/remotes/origin/*"
//
-// https://git-scm.com/book/es/v2/Git-Internals-The-Refspec
+// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
type RefSpec string
// Validate validates the RefSpec
@@ -59,6 +59,11 @@ func (s RefSpec) IsDelete() bool {
return s[0] == refSpecSeparator[0]
}
+// IsExactSHA1 returns true if the source is a SHA1 hash.
+func (s RefSpec) IsExactSHA1() bool {
+ return plumbing.IsHash(s.Src())
+}
+
// Src return the src side.
func (s RefSpec) Src() string {
spec := string(s)
@@ -69,8 +74,8 @@ func (s RefSpec) Src() string {
} else {
start = 0
}
- end := strings.Index(spec, refSpecSeparator)
+ end := strings.Index(spec, refSpecSeparator)
return spec[start:end]
}
diff --git a/vendor/github.com/go-git/go-git/v5/go.mod b/vendor/github.com/go-git/go-git/v5/go.mod
index 6d8da35c32..0c9cfd2cae 100644
--- a/vendor/github.com/go-git/go-git/v5/go.mod
+++ b/vendor/github.com/go-git/go-git/v5/go.mod
@@ -10,6 +10,7 @@ require (
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git-fixtures/v4 v4.0.1
github.com/google/go-cmp v0.3.0
+ github.com/imdario/mergo v0.3.9
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jessevdk/go-flags v1.4.0
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd
diff --git a/vendor/github.com/go-git/go-git/v5/go.sum b/vendor/github.com/go-git/go-git/v5/go.sum
index a73585e900..e14e29ae39 100644
--- a/vendor/github.com/go-git/go-git/v5/go.sum
+++ b/vendor/github.com/go-git/go-git/v5/go.sum
@@ -22,6 +22,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
+github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
diff --git a/vendor/github.com/go-git/go-git/v5/options.go b/vendor/github.com/go-git/go-git/v5/options.go
index a147f58b99..5367031f42 100644
--- a/vendor/github.com/go-git/go-git/v5/options.go
+++ b/vendor/github.com/go-git/go-git/v5/options.go
@@ -6,12 +6,12 @@ import (
"strings"
"time"
- "golang.org/x/crypto/openpgp"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v5/plumbing/transport"
+ "golang.org/x/crypto/openpgp"
)
// SubmoduleRescursivity defines how depth will affect any submodule recursive
@@ -190,6 +190,9 @@ type PushOptions struct {
// Prune specify that remote refs that match given RefSpecs and that do
// not exist locally will be removed.
Prune bool
+ // Force allows the push to update a remote branch even when the local
+ // branch does not descend from it.
+ Force bool
}
// Validate validates the fields and sets the default values.
@@ -375,7 +378,8 @@ type CommitOptions struct {
// All automatically stage files that have been modified and deleted, but
// new files you have not told Git about are not affected.
All bool
- // Author is the author's signature of the commit.
+ // Author is the author's signature of the commit. If Author is empty the
+ // Name and Email is read from the config, and time.Now it's used as When.
Author *object.Signature
// Committer is the committer's signature of the commit. If Committer is
// nil the Author signature is used.
@@ -392,7 +396,9 @@ type CommitOptions struct {
// Validate validates the fields and sets the default values.
func (o *CommitOptions) Validate(r *Repository) error {
if o.Author == nil {
- return ErrMissingAuthor
+ if err := o.loadConfigAuthorAndCommitter(r); err != nil {
+ return err
+ }
}
if o.Committer == nil {
@@ -413,6 +419,43 @@ func (o *CommitOptions) Validate(r *Repository) error {
return nil
}
+func (o *CommitOptions) loadConfigAuthorAndCommitter(r *Repository) error {
+ cfg, err := r.ConfigScoped(config.SystemScope)
+ if err != nil {
+ return err
+ }
+
+ if o.Author == nil && cfg.Author.Email != "" && cfg.Author.Name != "" {
+ o.Author = &object.Signature{
+ Name: cfg.Author.Name,
+ Email: cfg.Author.Email,
+ When: time.Now(),
+ }
+ }
+
+ if o.Committer == nil && cfg.Committer.Email != "" && cfg.Committer.Name != "" {
+ o.Committer = &object.Signature{
+ Name: cfg.Committer.Name,
+ Email: cfg.Committer.Email,
+ When: time.Now(),
+ }
+ }
+
+ if o.Author == nil && cfg.User.Email != "" && cfg.User.Name != "" {
+ o.Author = &object.Signature{
+ Name: cfg.User.Name,
+ Email: cfg.User.Email,
+ When: time.Now(),
+ }
+ }
+
+ if o.Author == nil {
+ return ErrMissingAuthor
+ }
+
+ return nil
+}
+
var (
ErrMissingName = errors.New("name field is required")
ErrMissingTagger = errors.New("tagger field is required")
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/color/color.go b/vendor/github.com/go-git/go-git/v5/plumbing/color/color.go
new file mode 100644
index 0000000000..2cd74bdc1a
--- /dev/null
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/color/color.go
@@ -0,0 +1,38 @@
+package color
+
+// TODO read colors from a github.com/go-git/go-git/plumbing/format/config.Config struct
+// TODO implement color parsing, see https://github.com/git/git/blob/v2.26.2/color.c
+
+// Colors. See https://github.com/git/git/blob/v2.26.2/color.h#L24-L53.
+const (
+ Normal = ""
+ Reset = "\033[m"
+ Bold = "\033[1m"
+ Red = "\033[31m"
+ Green = "\033[32m"
+ Yellow = "\033[33m"
+ Blue = "\033[34m"
+ Magenta = "\033[35m"
+ Cyan = "\033[36m"
+ BoldRed = "\033[1;31m"
+ BoldGreen = "\033[1;32m"
+ BoldYellow = "\033[1;33m"
+ BoldBlue = "\033[1;34m"
+ BoldMagenta = "\033[1;35m"
+ BoldCyan = "\033[1;36m"
+ FaintRed = "\033[2;31m"
+ FaintGreen = "\033[2;32m"
+ FaintYellow = "\033[2;33m"
+ FaintBlue = "\033[2;34m"
+ FaintMagenta = "\033[2;35m"
+ FaintCyan = "\033[2;36m"
+ BgRed = "\033[41m"
+ BgGreen = "\033[42m"
+ BgYellow = "\033[43m"
+ BgBlue = "\033[44m"
+ BgMagenta = "\033[45m"
+ BgCyan = "\033[46m"
+ Faint = "\033[2m"
+ FaintItalic = "\033[2;3m"
+ Reverse = "\033[7m"
+)
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/diff/colorconfig.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/diff/colorconfig.go
new file mode 100644
index 0000000000..6fd4158462
--- /dev/null
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/diff/colorconfig.go
@@ -0,0 +1,97 @@
+package diff
+
+import "github.com/go-git/go-git/v5/plumbing/color"
+
+// A ColorKey is a key into a ColorConfig map and also equal to the key in the
+// diff.color subsection of the config. See
+// https://github.com/git/git/blob/v2.26.2/diff.c#L83-L106.
+type ColorKey string
+
+// ColorKeys.
+const (
+ Context ColorKey = "context"
+ Meta ColorKey = "meta"
+ Frag ColorKey = "frag"
+ Old ColorKey = "old"
+ New ColorKey = "new"
+ Commit ColorKey = "commit"
+ Whitespace ColorKey = "whitespace"
+ Func ColorKey = "func"
+ OldMoved ColorKey = "oldMoved"
+ OldMovedAlternative ColorKey = "oldMovedAlternative"
+ OldMovedDimmed ColorKey = "oldMovedDimmed"
+ OldMovedAlternativeDimmed ColorKey = "oldMovedAlternativeDimmed"
+ NewMoved ColorKey = "newMoved"
+ NewMovedAlternative ColorKey = "newMovedAlternative"
+ NewMovedDimmed ColorKey = "newMovedDimmed"
+ NewMovedAlternativeDimmed ColorKey = "newMovedAlternativeDimmed"
+ ContextDimmed ColorKey = "contextDimmed"
+ OldDimmed ColorKey = "oldDimmed"
+ NewDimmed ColorKey = "newDimmed"
+ ContextBold ColorKey = "contextBold"
+ OldBold ColorKey = "oldBold"
+ NewBold ColorKey = "newBold"
+)
+
+// A ColorConfig is a color configuration. A nil or empty ColorConfig
+// corresponds to no color.
+type ColorConfig map[ColorKey]string
+
+// A ColorConfigOption sets an option on a ColorConfig.
+type ColorConfigOption func(ColorConfig)
+
+// WithColor sets the color for key.
+func WithColor(key ColorKey, color string) ColorConfigOption {
+ return func(cc ColorConfig) {
+ cc[key] = color
+ }
+}
+
+// defaultColorConfig is the default color configuration. See
+// https://github.com/git/git/blob/v2.26.2/diff.c#L57-L81.
+var defaultColorConfig = ColorConfig{
+ Context: color.Normal,
+ Meta: color.Bold,
+ Frag: color.Cyan,
+ Old: color.Red,
+ New: color.Green,
+ Commit: color.Yellow,
+ Whitespace: color.BgRed,
+ Func: color.Normal,
+ OldMoved: color.BoldMagenta,
+ OldMovedAlternative: color.BoldBlue,
+ OldMovedDimmed: color.Faint,
+ OldMovedAlternativeDimmed: color.FaintItalic,
+ NewMoved: color.BoldCyan,
+ NewMovedAlternative: color.BoldYellow,
+ NewMovedDimmed: color.Faint,
+ NewMovedAlternativeDimmed: color.FaintItalic,
+ ContextDimmed: color.Faint,
+ OldDimmed: color.FaintRed,
+ NewDimmed: color.FaintGreen,
+ ContextBold: color.Bold,
+ OldBold: color.BoldRed,
+ NewBold: color.BoldGreen,
+}
+
+// NewColorConfig returns a new ColorConfig.
+func NewColorConfig(options ...ColorConfigOption) ColorConfig {
+ cc := make(ColorConfig)
+ for key, value := range defaultColorConfig {
+ cc[key] = value
+ }
+ for _, option := range options {
+ option(cc)
+ }
+ return cc
+}
+
+// Reset returns the ANSI escape sequence to reset the color with key set from
+// cc. If no color was set then no reset is needed so it returns the empty
+// string.
+func (cc ColorConfig) Reset(key ColorKey) string {
+ if cc[key] == "" {
+ return ""
+ }
+ return color.Reset
+}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/diff/unified_encoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/diff/unified_encoder.go
index f2bc910a25..413984aa54 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/diff/unified_encoder.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/diff/unified_encoder.go
@@ -1,157 +1,158 @@
package diff
import (
- "bytes"
"fmt"
"io"
"regexp"
+ "strconv"
"strings"
"github.com/go-git/go-git/v5/plumbing"
)
-const (
- diffInit = "diff --git a/%s b/%s\n"
+// DefaultContextLines is the default number of context lines.
+const DefaultContextLines = 3
- chunkStart = "@@ -"
- chunkMiddle = " +"
- chunkEnd = " @@%s\n"
- chunkCount = "%d,%d"
+var (
+ splitLinesRegexp = regexp.MustCompile(`[^\n]*(\n|$)`)
- noFilePath = "/dev/null"
- aDir = "a/"
- bDir = "b/"
-
- fPath = "--- %s\n"
- tPath = "+++ %s\n"
- binary = "Binary files %s and %s differ\n"
-
- addLine = "+%s%s"
- deleteLine = "-%s%s"
- equalLine = " %s%s"
- noNewLine = "\n\\ No newline at end of file\n"
-
- oldMode = "old mode %o\n"
- newMode = "new mode %o\n"
- deletedFileMode = "deleted file mode %o\n"
- newFileMode = "new file mode %o\n"
-
- renameFrom = "from"
- renameTo = "to"
- renameFileMode = "rename %s %s\n"
-
- indexAndMode = "index %s..%s %o\n"
- indexNoMode = "index %s..%s\n"
+ operationChar = map[Operation]byte{
+ Add: '+',
+ Delete: '-',
+ Equal: ' ',
+ }
- DefaultContextLines = 3
+ operationColorKey = map[Operation]ColorKey{
+ Add: New,
+ Delete: Old,
+ Equal: Context,
+ }
)
-// UnifiedEncoder encodes an unified diff into the provided Writer.
-// There are some unsupported features:
-// - Similarity index for renames
-// - Sort hash representation
+// UnifiedEncoder encodes an unified diff into the provided Writer. It does not
+// support similarity index for renames or sorting hash representations.
type UnifiedEncoder struct {
io.Writer
- // ctxLines is the count of unchanged lines that will appear
- // surrounding a change.
- ctxLines int
+ // contextLines is the count of unchanged lines that will appear surrounding
+ // a change.
+ contextLines int
- buf bytes.Buffer
+ // colorConfig is the color configuration. The default is no color.
+ color ColorConfig
}
-func NewUnifiedEncoder(w io.Writer, ctxLines int) *UnifiedEncoder {
- return &UnifiedEncoder{ctxLines: ctxLines, Writer: w}
-}
-
-func (e *UnifiedEncoder) Encode(patch Patch) error {
- e.printMessage(patch.Message())
-
- if err := e.encodeFilePatch(patch.FilePatches()); err != nil {
- return err
+// NewUnifiedEncoder returns a new UnifiedEncoder that writes to w.
+func NewUnifiedEncoder(w io.Writer, contextLines int) *UnifiedEncoder {
+ return &UnifiedEncoder{
+ Writer: w,
+ contextLines: contextLines,
}
+}
- _, err := e.buf.WriteTo(e)
-
- return err
+// SetColor sets e's color configuration and returns e.
+func (e *UnifiedEncoder) SetColor(colorConfig ColorConfig) *UnifiedEncoder {
+ e.color = colorConfig
+ return e
}
-func (e *UnifiedEncoder) encodeFilePatch(filePatches []FilePatch) error {
- for _, p := range filePatches {
- f, t := p.Files()
- if err := e.header(f, t, p.IsBinary()); err != nil {
- return err
+// Encode encodes patch.
+func (e *UnifiedEncoder) Encode(patch Patch) error {
+ sb := &strings.Builder{}
+
+ if message := patch.Message(); message != "" {
+ sb.WriteString(message)
+ if !strings.HasSuffix(message, "\n") {
+ sb.WriteByte('\n')
}
+ }
- g := newHunksGenerator(p.Chunks(), e.ctxLines)
- for _, c := range g.Generate() {
- c.WriteTo(&e.buf)
+ for _, filePatch := range patch.FilePatches() {
+ e.writeFilePatchHeader(sb, filePatch)
+ g := newHunksGenerator(filePatch.Chunks(), e.contextLines)
+ for _, hunk := range g.Generate() {
+ hunk.writeTo(sb, e.color)
}
}
- return nil
+ _, err := e.Write([]byte(sb.String()))
+ return err
}
-func (e *UnifiedEncoder) printMessage(message string) {
- isEmpty := message == ""
- hasSuffix := strings.HasSuffix(message, "\n")
- if !isEmpty && !hasSuffix {
- message += "\n"
+func (e *UnifiedEncoder) writeFilePatchHeader(sb *strings.Builder, filePatch FilePatch) {
+ from, to := filePatch.Files()
+ if from == nil && to == nil {
+ return
}
+ isBinary := filePatch.IsBinary()
- e.buf.WriteString(message)
-}
-
-func (e *UnifiedEncoder) header(from, to File, isBinary bool) error {
+ var lines []string
switch {
- case from == nil && to == nil:
- return nil
case from != nil && to != nil:
hashEquals := from.Hash() == to.Hash()
-
- fmt.Fprintf(&e.buf, diffInit, from.Path(), to.Path())
-
+ lines = append(lines,
+ fmt.Sprintf("diff --git a/%s b/%s", from.Path(), to.Path()),
+ )
if from.Mode() != to.Mode() {
- fmt.Fprintf(&e.buf, oldMode+newMode, from.Mode(), to.Mode())
+ lines = append(lines,
+ fmt.Sprintf("old mode %o", from.Mode()),
+ fmt.Sprintf("new mode %o", to.Mode()),
+ )
}
-
if from.Path() != to.Path() {
- fmt.Fprintf(&e.buf,
- renameFileMode+renameFileMode,
- renameFrom, from.Path(), renameTo, to.Path())
+ lines = append(lines,
+ fmt.Sprintf("rename from %s", from.Path()),
+ fmt.Sprintf("rename to %s", to.Path()),
+ )
}
-
if from.Mode() != to.Mode() && !hashEquals {
- fmt.Fprintf(&e.buf, indexNoMode, from.Hash(), to.Hash())
+ lines = append(lines,
+ fmt.Sprintf("index %s..%s", from.Hash(), to.Hash()),
+ )
} else if !hashEquals {
- fmt.Fprintf(&e.buf, indexAndMode, from.Hash(), to.Hash(), from.Mode())
+ lines = append(lines,
+ fmt.Sprintf("index %s..%s %o", from.Hash(), to.Hash(), from.Mode()),
+ )
}
-
if !hashEquals {
- e.pathLines(isBinary, aDir+from.Path(), bDir+to.Path())
+ lines = e.appendPathLines(lines, "a/"+from.Path(), "b/"+to.Path(), isBinary)
}
case from == nil:
- fmt.Fprintf(&e.buf, diffInit, to.Path(), to.Path())
- fmt.Fprintf(&e.buf, newFileMode, to.Mode())
- fmt.Fprintf(&e.buf, indexNoMode, plumbing.ZeroHash, to.Hash())
- e.pathLines(isBinary, noFilePath, bDir+to.Path())
+ lines = append(lines,
+ fmt.Sprintf("diff --git a/%s b/%s", to.Path(), to.Path()),
+ fmt.Sprintf("new file mode %o", to.Mode()),
+ fmt.Sprintf("index %s..%s", plumbing.ZeroHash, to.Hash()),
+ )
+ lines = e.appendPathLines(lines, "/dev/null", "b/"+to.Path(), isBinary)
case to == nil:
- fmt.Fprintf(&e.buf, diffInit, from.Path(), from.Path())
- fmt.Fprintf(&e.buf, deletedFileMode, from.Mode())
- fmt.Fprintf(&e.buf, indexNoMode, from.Hash(), plumbing.ZeroHash)
- e.pathLines(isBinary, aDir+from.Path(), noFilePath)
+ lines = append(lines,
+ fmt.Sprintf("diff --git a/%s b/%s", from.Path(), from.Path()),
+ fmt.Sprintf("deleted file mode %o", from.Mode()),
+ fmt.Sprintf("index %s..%s", from.Hash(), plumbing.ZeroHash),
+ )
+ lines = e.appendPathLines(lines, "a/"+from.Path(), "/dev/null", isBinary)
}
- return nil
+ sb.WriteString(e.color[Meta])
+ sb.WriteString(lines[0])
+ for _, line := range lines[1:] {
+ sb.WriteByte('\n')
+ sb.WriteString(line)
+ }
+ sb.WriteString(e.color.Reset(Meta))
+ sb.WriteByte('\n')
}
-func (e *UnifiedEncoder) pathLines(isBinary bool, fromPath, toPath string) {
- format := fPath + tPath
+func (e *UnifiedEncoder) appendPathLines(lines []string, fromPath, toPath string, isBinary bool) []string {
if isBinary {
- format = binary
+ return append(lines,
+ fmt.Sprintf("Binary files %s and %s differ", fromPath, toPath),
+ )
}
-
- fmt.Fprintf(&e.buf, format, fromPath, toPath)
+ return append(lines,
+ fmt.Sprintf("--- %s", fromPath),
+ fmt.Sprintf("+++ %s", toPath),
+ )
}
type hunksGenerator struct {
@@ -170,84 +171,84 @@ func newHunksGenerator(chunks []Chunk, ctxLines int) *hunksGenerator {
}
}
-func (c *hunksGenerator) Generate() []*hunk {
- for i, chunk := range c.chunks {
- ls := splitLines(chunk.Content())
- lsLen := len(ls)
+func (g *hunksGenerator) Generate() []*hunk {
+ for i, chunk := range g.chunks {
+ lines := splitLines(chunk.Content())
+ nLines := len(lines)
switch chunk.Type() {
case Equal:
- c.fromLine += lsLen
- c.toLine += lsLen
- c.processEqualsLines(ls, i)
+ g.fromLine += nLines
+ g.toLine += nLines
+ g.processEqualsLines(lines, i)
case Delete:
- if lsLen != 0 {
- c.fromLine++
+ if nLines != 0 {
+ g.fromLine++
}
- c.processHunk(i, chunk.Type())
- c.fromLine += lsLen - 1
- c.current.AddOp(chunk.Type(), ls...)
+ g.processHunk(i, chunk.Type())
+ g.fromLine += nLines - 1
+ g.current.AddOp(chunk.Type(), lines...)
case Add:
- if lsLen != 0 {
- c.toLine++
+ if nLines != 0 {
+ g.toLine++
}
- c.processHunk(i, chunk.Type())
- c.toLine += lsLen - 1
- c.current.AddOp(chunk.Type(), ls...)
+ g.processHunk(i, chunk.Type())
+ g.toLine += nLines - 1
+ g.current.AddOp(chunk.Type(), lines...)
}
- if i == len(c.chunks)-1 && c.current != nil {
- c.hunks = append(c.hunks, c.current)
+ if i == len(g.chunks)-1 && g.current != nil {
+ g.hunks = append(g.hunks, g.current)
}
}
- return c.hunks
+ return g.hunks
}
-func (c *hunksGenerator) processHunk(i int, op Operation) {
- if c.current != nil {
+func (g *hunksGenerator) processHunk(i int, op Operation) {
+ if g.current != nil {
return
}
var ctxPrefix string
- linesBefore := len(c.beforeContext)
- if linesBefore > c.ctxLines {
- ctxPrefix = " " + c.beforeContext[linesBefore-c.ctxLines-1]
- c.beforeContext = c.beforeContext[linesBefore-c.ctxLines:]
- linesBefore = c.ctxLines
+ linesBefore := len(g.beforeContext)
+ if linesBefore > g.ctxLines {
+ ctxPrefix = g.beforeContext[linesBefore-g.ctxLines-1]
+ g.beforeContext = g.beforeContext[linesBefore-g.ctxLines:]
+ linesBefore = g.ctxLines
}
- c.current = &hunk{ctxPrefix: strings.TrimSuffix(ctxPrefix, "\n")}
- c.current.AddOp(Equal, c.beforeContext...)
+ g.current = &hunk{ctxPrefix: strings.TrimSuffix(ctxPrefix, "\n")}
+ g.current.AddOp(Equal, g.beforeContext...)
switch op {
case Delete:
- c.current.fromLine, c.current.toLine =
- c.addLineNumbers(c.fromLine, c.toLine, linesBefore, i, Add)
+ g.current.fromLine, g.current.toLine =
+ g.addLineNumbers(g.fromLine, g.toLine, linesBefore, i, Add)
case Add:
- c.current.toLine, c.current.fromLine =
- c.addLineNumbers(c.toLine, c.fromLine, linesBefore, i, Delete)
+ g.current.toLine, g.current.fromLine =
+ g.addLineNumbers(g.toLine, g.fromLine, linesBefore, i, Delete)
}
- c.beforeContext = nil
+ g.beforeContext = nil
}
-// addLineNumbers obtains the line numbers in a new chunk
-func (c *hunksGenerator) addLineNumbers(la, lb int, linesBefore int, i int, op Operation) (cla, clb int) {
+// addLineNumbers obtains the line numbers in a new chunk.
+func (g *hunksGenerator) addLineNumbers(la, lb int, linesBefore int, i int, op Operation) (cla, clb int) {
cla = la - linesBefore
// we need to search for a reference for the next diff
switch {
- case linesBefore != 0 && c.ctxLines != 0:
- if lb > c.ctxLines {
- clb = lb - c.ctxLines + 1
+ case linesBefore != 0 && g.ctxLines != 0:
+ if lb > g.ctxLines {
+ clb = lb - g.ctxLines + 1
} else {
clb = 1
}
- case c.ctxLines == 0:
+ case g.ctxLines == 0:
clb = lb
- case i != len(c.chunks)-1:
- next := c.chunks[i+1]
+ case i != len(g.chunks)-1:
+ next := g.chunks[i+1]
if next.Type() == op || next.Type() == Equal {
// this diff will be into this chunk
clb = lb + 1
@@ -257,34 +258,32 @@ func (c *hunksGenerator) addLineNumbers(la, lb int, linesBefore int, i int, op O
return
}
-func (c *hunksGenerator) processEqualsLines(ls []string, i int) {
- if c.current == nil {
- c.beforeContext = append(c.beforeContext, ls...)
+func (g *hunksGenerator) processEqualsLines(ls []string, i int) {
+ if g.current == nil {
+ g.beforeContext = append(g.beforeContext, ls...)
return
}
- c.afterContext = append(c.afterContext, ls...)
- if len(c.afterContext) <= c.ctxLines*2 && i != len(c.chunks)-1 {
- c.current.AddOp(Equal, c.afterContext...)
- c.afterContext = nil
+ g.afterContext = append(g.afterContext, ls...)
+ if len(g.afterContext) <= g.ctxLines*2 && i != len(g.chunks)-1 {
+ g.current.AddOp(Equal, g.afterContext...)
+ g.afterContext = nil
} else {
- ctxLines := c.ctxLines
- if ctxLines > len(c.afterContext) {
- ctxLines = len(c.afterContext)
+ ctxLines := g.ctxLines
+ if ctxLines > len(g.afterContext) {
+ ctxLines = len(g.afterContext)
}
- c.current.AddOp(Equal, c.afterContext[:ctxLines]...)
- c.hunks = append(c.hunks, c.current)
+ g.current.AddOp(Equal, g.afterContext[:ctxLines]...)
+ g.hunks = append(g.hunks, g.current)
- c.current = nil
- c.beforeContext = c.afterContext[ctxLines:]
- c.afterContext = nil
+ g.current = nil
+ g.beforeContext = g.afterContext[ctxLines:]
+ g.afterContext = nil
}
}
-var splitLinesRE = regexp.MustCompile(`[^\n]*(\n|$)`)
-
func splitLines(s string) []string {
- out := splitLinesRE.FindAllString(s, -1)
+ out := splitLinesRegexp.FindAllString(s, -1)
if out[len(out)-1] == "" {
out = out[:len(out)-1]
}
@@ -302,44 +301,59 @@ type hunk struct {
ops []*op
}
-func (c *hunk) WriteTo(buf *bytes.Buffer) {
- buf.WriteString(chunkStart)
+func (h *hunk) writeTo(sb *strings.Builder, color ColorConfig) {
+ sb.WriteString(color[Frag])
+ sb.WriteString("@@ -")
- if c.fromCount == 1 {
- fmt.Fprintf(buf, "%d", c.fromLine)
+ if h.fromCount == 1 {
+ sb.WriteString(strconv.Itoa(h.fromLine))
} else {
- fmt.Fprintf(buf, chunkCount, c.fromLine, c.fromCount)
+ sb.WriteString(strconv.Itoa(h.fromLine))
+ sb.WriteByte(',')
+ sb.WriteString(strconv.Itoa(h.fromCount))
}
- buf.WriteString(chunkMiddle)
+ sb.WriteString(" +")
- if c.toCount == 1 {
- fmt.Fprintf(buf, "%d", c.toLine)
+ if h.toCount == 1 {
+ sb.WriteString(strconv.Itoa(h.toLine))
} else {
- fmt.Fprintf(buf, chunkCount, c.toLine, c.toCount)
+ sb.WriteString(strconv.Itoa(h.toLine))
+ sb.WriteByte(',')
+ sb.WriteString(strconv.Itoa(h.toCount))
}
- fmt.Fprintf(buf, chunkEnd, c.ctxPrefix)
+ sb.WriteString(" @@")
+ sb.WriteString(color.Reset(Frag))
- for _, d := range c.ops {
- buf.WriteString(d.String())
+ if h.ctxPrefix != "" {
+ sb.WriteByte(' ')
+ sb.WriteString(color[Func])
+ sb.WriteString(h.ctxPrefix)
+ sb.WriteString(color.Reset(Func))
+ }
+
+ sb.WriteByte('\n')
+
+ for _, op := range h.ops {
+ op.writeTo(sb, color)
}
}
-func (c *hunk) AddOp(t Operation, s ...string) {
- ls := len(s)
+func (h *hunk) AddOp(t Operation, ss ...string) {
+ n := len(ss)
switch t {
case Add:
- c.toCount += ls
+ h.toCount += n
case Delete:
- c.fromCount += ls
+ h.fromCount += n
case Equal:
- c.toCount += ls
- c.fromCount += ls
+ h.toCount += n
+ h.fromCount += n
}
- for _, l := range s {
- c.ops = append(c.ops, &op{l, t})
+ for _, s := range ss {
+ h.ops = append(h.ops, &op{s, t})
}
}
@@ -348,20 +362,15 @@ type op struct {
t Operation
}
-func (o *op) String() string {
- var prefix, suffix string
- switch o.t {
- case Add:
- prefix = addLine
- case Delete:
- prefix = deleteLine
- case Equal:
- prefix = equalLine
- }
- n := len(o.text)
- if n > 0 && o.text[n-1] != '\n' {
- suffix = noNewLine
+func (o *op) writeTo(sb *strings.Builder, color ColorConfig) {
+ colorKey := operationColorKey[o.t]
+ sb.WriteString(color[colorKey])
+ sb.WriteByte(operationChar[o.t])
+ if strings.HasSuffix(o.text, "\n") {
+ sb.WriteString(strings.TrimSuffix(o.text, "\n"))
+ } else {
+ sb.WriteString(o.text + "\n\\ No newline at end of file")
}
-
- return fmt.Sprintf(prefix, o.text, suffix)
+ sb.WriteString(color.Reset(colorKey))
+ sb.WriteByte('\n')
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go
index 1d4b384a77..1951b34ef1 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go
@@ -4,6 +4,7 @@ import (
"bytes"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/utils/ioutil"
)
// See https://github.com/jelmer/dulwich/blob/master/dulwich/pack.py and
@@ -27,17 +28,20 @@ func GetDelta(base, target plumbing.EncodedObject) (plumbing.EncodedObject, erro
return getDelta(new(deltaIndex), base, target)
}
-func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (plumbing.EncodedObject, error) {
+func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (o plumbing.EncodedObject, err error) {
br, err := base.Reader()
if err != nil {
return nil, err
}
- defer br.Close()
+
+ defer ioutil.CheckClose(br, &err)
+
tr, err := target.Reader()
if err != nil {
return nil, err
}
- defer tr.Close()
+
+ defer ioutil.CheckClose(tr, &err)
bb := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(bb)
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/encoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/encoder.go
index 65fae523ae..5501f8861c 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/encoder.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/encoder.go
@@ -9,6 +9,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/binary"
+ "github.com/go-git/go-git/v5/utils/ioutil"
)
// Encoder gets the data from the storage and write it into the writer in PACK
@@ -80,7 +81,7 @@ func (e *Encoder) head(numEntries int) error {
)
}
-func (e *Encoder) entry(o *ObjectToPack) error {
+func (e *Encoder) entry(o *ObjectToPack) (err error) {
if o.WantWrite() {
// A cycle exists in this delta chain. This should only occur if a
// selected object representation disappeared during writing
@@ -119,17 +120,22 @@ func (e *Encoder) entry(o *ObjectToPack) error {
}
e.zw.Reset(e.w)
+
+ defer ioutil.CheckClose(e.zw, &err)
+
or, err := o.Object.Reader()
if err != nil {
return err
}
+ defer ioutil.CheckClose(or, &err)
+
_, err = io.Copy(e.zw, or)
if err != nil {
return err
}
- return e.zw.Close()
+ return nil
}
func (e *Encoder) writeBaseIfDelta(o *ObjectToPack) error {
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go
index d060041975..ddd7f62fce 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go
@@ -10,6 +10,7 @@ import (
"github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/plumbing/format/idxfile"
"github.com/go-git/go-git/v5/plumbing/storer"
+ "github.com/go-git/go-git/v5/utils/ioutil"
)
var (
@@ -307,12 +308,14 @@ func (p *Packfile) getNextMemoryObject(h *ObjectHeader) (plumbing.EncodedObject,
return obj, nil
}
-func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) error {
+func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) (err error) {
w, err := obj.Writer()
if err != nil {
return err
}
+ defer ioutil.CheckClose(w, &err)
+
_, _, err = p.s.NextObject(w)
p.cachePut(obj)
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go
index d411c5b57a..4b5a5708cc 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go
@@ -4,11 +4,12 @@ import (
"bytes"
"errors"
"io"
- "io/ioutil"
+ stdioutil "io/ioutil"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/plumbing/storer"
+ "github.com/go-git/go-git/v5/utils/ioutil"
)
var (
@@ -283,7 +284,7 @@ func (p *Parser) resolveDeltas() error {
if !obj.IsDelta() && len(obj.Children) > 0 {
for _, child := range obj.Children {
- if err := p.resolveObject(ioutil.Discard, child, content); err != nil {
+ if err := p.resolveObject(stdioutil.Discard, child, content); err != nil {
return err
}
}
@@ -298,7 +299,7 @@ func (p *Parser) resolveDeltas() error {
return nil
}
-func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) error {
+func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
if !o.ExternalRef { // skip cache check for placeholder parents
b, ok := p.cache.Get(o.Offset)
if ok {
@@ -310,17 +311,21 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) error {
// If it's not on the cache and is not a delta we can try to find it in the
// storage, if there's one. External refs must enter here.
if p.storage != nil && !o.Type.IsDelta() {
- e, err := p.storage.EncodedObject(plumbing.AnyObject, o.SHA1)
+ var e plumbing.EncodedObject
+ e, err = p.storage.EncodedObject(plumbing.AnyObject, o.SHA1)
if err != nil {
return err
}
o.Type = e.Type()
- r, err := e.Reader()
+ var r io.ReadCloser
+ r, err = e.Reader()
if err != nil {
return err
}
+ defer ioutil.CheckClose(r, &err)
+
_, err = buf.ReadFrom(io.LimitReader(r, e.Size()))
return err
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go
index 57c9da7ce8..1dc8b8b009 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go
@@ -6,6 +6,7 @@ import (
"io"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/utils/ioutil"
)
// See https://github.com/git/git/blob/49fa3dc76179e04b0833542fa52d0f287a4955ac/delta.h
@@ -16,17 +17,21 @@ import (
const deltaSizeMin = 4
// ApplyDelta writes to target the result of applying the modification deltas in delta to base.
-func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error {
+func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
r, err := base.Reader()
if err != nil {
return err
}
+ defer ioutil.CheckClose(r, &err)
+
w, err := target.Writer()
if err != nil {
return err
}
+ defer ioutil.CheckClose(w, &err)
+
buf := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(buf)
buf.Reset()
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/hash.go b/vendor/github.com/go-git/go-git/v5/plumbing/hash.go
index 637a4252ad..afc602a9ec 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/hash.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/hash.go
@@ -71,3 +71,13 @@ type HashSlice []Hash
func (p HashSlice) Len() int { return len(p) }
func (p HashSlice) Less(i, j int) bool { return bytes.Compare(p[i][:], p[j][:]) < 0 }
func (p HashSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+// IsHash returns true if the given string is a valid hash.
+func IsHash(s string) bool {
+ if len(s) != 40 {
+ return false
+ }
+
+ _, err := hex.DecodeString(s)
+ return err == nil
+}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/change.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/change.go
index 4474905e0a..c9d1615089 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/object/change.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/change.go
@@ -18,7 +18,7 @@ type Change struct {
To ChangeEntry
}
-var empty = ChangeEntry{}
+var empty ChangeEntry
// Action returns the kind of action represented by the change, an
// insertion, a deletion or a modification.
@@ -27,9 +27,11 @@ func (c *Change) Action() (merkletrie.Action, error) {
return merkletrie.Action(0),
fmt.Errorf("malformed change: empty from and to")
}
+
if c.From == empty {
return merkletrie.Insert, nil
}
+
if c.To == empty {
return merkletrie.Delete, nil
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go
index b37ff61d33..113cb29e54 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go
@@ -78,21 +78,30 @@ func (c *Commit) Tree() (*Tree, error) {
// PatchContext returns the Patch between the actual commit and the provided one.
// Error will be return if context expires. Provided context must be non-nil.
+//
+// NOTE: Since version 5.1.0 the renames are correctly handled, the settings
+// used are the recommended options DefaultDiffTreeOptions.
func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error) {
fromTree, err := c.Tree()
if err != nil {
return nil, err
}
- toTree, err := to.Tree()
- if err != nil {
- return nil, err
+ var toTree *Tree
+ if to != nil {
+ toTree, err = to.Tree()
+ if err != nil {
+ return nil, err
+ }
}
return fromTree.PatchContext(ctx, toTree)
}
// Patch returns the Patch between the actual commit and the provided one.
+//
+// NOTE: Since version 5.1.0 the renames are correctly handled, the settings
+// used are the recommended options DefaultDiffTreeOptions.
func (c *Commit) Patch(to *Commit) (*Patch, error) {
return c.PatchContext(context.Background(), to)
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/difftree.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/difftree.go
index 72411a590e..7c2222702c 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/object/difftree.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/difftree.go
@@ -10,14 +10,62 @@ import (
// DiffTree compares the content and mode of the blobs found via two
// tree objects.
+// DiffTree does not perform rename detection, use DiffTreeWithOptions
+// instead to detect renames.
func DiffTree(a, b *Tree) (Changes, error) {
return DiffTreeContext(context.Background(), a, b)
}
-// DiffTree compares the content and mode of the blobs found via two
+// DiffTreeContext compares the content and mode of the blobs found via two
// tree objects. Provided context must be non-nil.
-// An error will be return if context expires
+// An error will be returned if context expires.
func DiffTreeContext(ctx context.Context, a, b *Tree) (Changes, error) {
+ return DiffTreeWithOptions(ctx, a, b, nil)
+}
+
+// DiffTreeOptions are the configurable options when performing a diff tree.
+type DiffTreeOptions struct {
+ // DetectRenames is whether the diff tree will use rename detection.
+ DetectRenames bool
+ // RenameScore is the threshold to of similarity between files to consider
+ // that a pair of delete and insert are a rename. The number must be
+ // exactly between 0 and 100.
+ RenameScore uint
+ // RenameLimit is the maximum amount of files that can be compared when
+ // detecting renames. The number of comparisons that have to be performed
+ // is equal to the number of deleted files * the number of added files.
+ // That means, that if 100 files were deleted and 50 files were added, 5000
+ // file comparisons may be needed. So, if the rename limit is 50, the number
+ // of both deleted and added needs to be equal or less than 50.
+ // A value of 0 means no limit.
+ RenameLimit uint
+ // OnlyExactRenames performs only detection of exact renames and will not perform
+ // any detection of renames based on file similarity.
+ OnlyExactRenames bool
+}
+
+// DefaultDiffTreeOptions are the default and recommended options for the
+// diff tree.
+var DefaultDiffTreeOptions = &DiffTreeOptions{
+ DetectRenames: true,
+ RenameScore: 60,
+ RenameLimit: 0,
+ OnlyExactRenames: false,
+}
+
+// DiffTreeWithOptions compares the content and mode of the blobs found
+// via two tree objects with the given options. The provided context
+// must be non-nil.
+// If no options are passed, no rename detection will be performed. The
+// recommended options are DefaultDiffTreeOptions.
+// An error will be returned if the context expires.
+// This function will be deprecated and removed in v6 so the default
+// behaviour of DiffTree is to detect renames.
+func DiffTreeWithOptions(
+ ctx context.Context,
+ a, b *Tree,
+ opts *DiffTreeOptions,
+) (Changes, error) {
from := NewTreeRootNode(a)
to := NewTreeRootNode(b)
@@ -33,5 +81,18 @@ func DiffTreeContext(ctx context.Context, a, b *Tree) (Changes, error) {
return nil, err
}
- return newChanges(merkletrieChanges)
+ changes, err := newChanges(merkletrieChanges)
+ if err != nil {
+ return nil, err
+ }
+
+ if opts == nil {
+ opts = new(DiffTreeOptions)
+ }
+
+ if opts.DetectRenames {
+ return DetectRenames(changes, opts)
+ }
+
+ return changes, nil
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go
index be1e609270..1135a40a4a 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go
@@ -115,7 +115,7 @@ func fileContent(f *File) (content string, isBinary bool, err error) {
return
}
-// textPatch is an implementation of fdiff.Patch interface
+// Patch is an implementation of fdiff.Patch interface
type Patch struct {
message string
filePatches []fdiff.FilePatch
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/rename.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/rename.go
new file mode 100644
index 0000000000..35af1d62d5
--- /dev/null
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/rename.go
@@ -0,0 +1,813 @@
+package object
+
+import (
+ "errors"
+ "io"
+ "sort"
+ "strings"
+
+ "github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/plumbing/filemode"
+ "github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/merkletrie"
+)
+
+// DetectRenames detects the renames in the given changes on two trees with
+// the given options. It will return the given changes grouping additions and
+// deletions into modifications when possible.
+// If options is nil, the default diff tree options will be used.
+func DetectRenames(
+ changes Changes,
+ opts *DiffTreeOptions,
+) (Changes, error) {
+ if opts == nil {
+ opts = DefaultDiffTreeOptions
+ }
+
+ detector := &renameDetector{
+ renameScore: int(opts.RenameScore),
+ renameLimit: int(opts.RenameLimit),
+ onlyExact: opts.OnlyExactRenames,
+ }
+
+ for _, c := range changes {
+ action, err := c.Action()
+ if err != nil {
+ return nil, err
+ }
+
+ switch action {
+ case merkletrie.Insert:
+ detector.added = append(detector.added, c)
+ case merkletrie.Delete:
+ detector.deleted = append(detector.deleted, c)
+ default:
+ detector.modified = append(detector.modified, c)
+ }
+ }
+
+ return detector.detect()
+}
+
+// renameDetector will detect and resolve renames in a set of changes.
+// see: https://github.com/eclipse/jgit/blob/master/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+type renameDetector struct {
+ added []*Change
+ deleted []*Change
+ modified []*Change
+
+ renameScore int
+ renameLimit int
+ onlyExact bool
+}
+
+// detectExactRenames detects matches files that were deleted with files that
+// were added where the hash is the same on both. If there are multiple targets
+// the one with the most similar path will be chosen as the rename and the
+// rest as either deletions or additions.
+func (d *renameDetector) detectExactRenames() {
+ added := groupChangesByHash(d.added)
+ deletes := groupChangesByHash(d.deleted)
+ var uniqueAdds []*Change
+ var nonUniqueAdds [][]*Change
+ var addedLeft []*Change
+
+ for _, cs := range added {
+ if len(cs) == 1 {
+ uniqueAdds = append(uniqueAdds, cs[0])
+ } else {
+ nonUniqueAdds = append(nonUniqueAdds, cs)
+ }
+ }
+
+ for _, c := range uniqueAdds {
+ hash := changeHash(c)
+ deleted := deletes[hash]
+
+ if len(deleted) == 1 {
+ if sameMode(c, deleted[0]) {
+ d.modified = append(d.modified, &Change{From: deleted[0].From, To: c.To})
+ delete(deletes, hash)
+ } else {
+ addedLeft = append(addedLeft, c)
+ }
+ } else if len(deleted) > 1 {
+ bestMatch := bestNameMatch(c, deleted)
+ if bestMatch != nil && sameMode(c, bestMatch) {
+ d.modified = append(d.modified, &Change{From: bestMatch.From, To: c.To})
+ delete(deletes, hash)
+
+ var newDeletes = make([]*Change, 0, len(deleted)-1)
+ for _, d := range deleted {
+ if d != bestMatch {
+ newDeletes = append(newDeletes, d)
+ }
+ }
+ deletes[hash] = newDeletes
+ }
+ } else {
+ addedLeft = append(addedLeft, c)
+ }
+ }
+
+ for _, added := range nonUniqueAdds {
+ hash := changeHash(added[0])
+ deleted := deletes[hash]
+
+ if len(deleted) == 1 {
+ deleted := deleted[0]
+ bestMatch := bestNameMatch(deleted, added)
+ if bestMatch != nil && sameMode(deleted, bestMatch) {
+ d.modified = append(d.modified, &Change{From: deleted.From, To: bestMatch.To})
+ delete(deletes, hash)
+
+ for _, c := range added {
+ if c != bestMatch {
+ addedLeft = append(addedLeft, c)
+ }
+ }
+ } else {
+ addedLeft = append(addedLeft, added...)
+ }
+ } else if len(deleted) > 1 {
+ maxSize := len(deleted) * len(added)
+ if d.renameLimit > 0 && d.renameLimit < maxSize {
+ maxSize = d.renameLimit
+ }
+
+ matrix := make(similarityMatrix, 0, maxSize)
+
+ for delIdx, del := range deleted {
+ deletedName := changeName(del)
+
+ for addIdx, add := range added {
+ addedName := changeName(add)
+
+ score := nameSimilarityScore(addedName, deletedName)
+ matrix = append(matrix, similarityPair{added: addIdx, deleted: delIdx, score: score})
+
+ if len(matrix) >= maxSize {
+ break
+ }
+ }
+
+ if len(matrix) >= maxSize {
+ break
+ }
+ }
+
+ sort.Stable(matrix)
+
+ usedAdds := make(map[*Change]struct{})
+ usedDeletes := make(map[*Change]struct{})
+ for i := len(matrix) - 1; i >= 0; i-- {
+ del := deleted[matrix[i].deleted]
+ add := added[matrix[i].added]
+
+ if add == nil || del == nil {
+ // it was already matched
+ continue
+ }
+
+ usedAdds[add] = struct{}{}
+ usedDeletes[del] = struct{}{}
+ d.modified = append(d.modified, &Change{From: del.From, To: add.To})
+ added[matrix[i].added] = nil
+ deleted[matrix[i].deleted] = nil
+ }
+
+ for _, c := range added {
+ if _, ok := usedAdds[c]; !ok && c != nil {
+ addedLeft = append(addedLeft, c)
+ }
+ }
+
+ var newDeletes = make([]*Change, 0, len(deleted)-len(usedDeletes))
+ for _, c := range deleted {
+ if _, ok := usedDeletes[c]; !ok && c != nil {
+ newDeletes = append(newDeletes, c)
+ }
+ }
+ deletes[hash] = newDeletes
+ } else {
+ addedLeft = append(addedLeft, added...)
+ }
+ }
+
+ d.added = addedLeft
+ d.deleted = nil
+ for _, dels := range deletes {
+ d.deleted = append(d.deleted, dels...)
+ }
+}
+
+// detectContentRenames detects renames based on the similarity of the content
+// in the files by building a matrix of pairs between sources and destinations
+// and matching by the highest score.
+// see: https://github.com/eclipse/jgit/blob/master/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+func (d *renameDetector) detectContentRenames() error {
+ cnt := max(len(d.added), len(d.deleted))
+ if d.renameLimit > 0 && cnt > d.renameLimit {
+ return nil
+ }
+
+ srcs, dsts := d.deleted, d.added
+ matrix, err := buildSimilarityMatrix(srcs, dsts, d.renameScore)
+ if err != nil {
+ return err
+ }
+ renames := make([]*Change, 0, min(len(matrix), len(dsts)))
+
+ // Match rename pairs on a first come, first serve basis until
+ // we have looked at everything that is above the minimum score.
+ for i := len(matrix) - 1; i >= 0; i-- {
+ pair := matrix[i]
+ src := srcs[pair.deleted]
+ dst := dsts[pair.added]
+
+ if dst == nil || src == nil {
+ // It was already matched before
+ continue
+ }
+
+ renames = append(renames, &Change{From: src.From, To: dst.To})
+
+ // Claim destination and source as matched
+ dsts[pair.added] = nil
+ srcs[pair.deleted] = nil
+ }
+
+ d.modified = append(d.modified, renames...)
+ d.added = compactChanges(dsts)
+ d.deleted = compactChanges(srcs)
+
+ return nil
+}
+
+func (d *renameDetector) detect() (Changes, error) {
+ if len(d.added) > 0 && len(d.deleted) > 0 {
+ d.detectExactRenames()
+
+ if !d.onlyExact {
+ if err := d.detectContentRenames(); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ result := make(Changes, 0, len(d.added)+len(d.deleted)+len(d.modified))
+ result = append(result, d.added...)
+ result = append(result, d.deleted...)
+ result = append(result, d.modified...)
+
+ sort.Stable(result)
+
+ return result, nil
+}
+
+func bestNameMatch(change *Change, changes []*Change) *Change {
+ var best *Change
+ var bestScore int
+
+ cname := changeName(change)
+
+ for _, c := range changes {
+ score := nameSimilarityScore(cname, changeName(c))
+ if score > bestScore {
+ bestScore = score
+ best = c
+ }
+ }
+
+ return best
+}
+
+func nameSimilarityScore(a, b string) int {
+ aDirLen := strings.LastIndexByte(a, '/') + 1
+ bDirLen := strings.LastIndexByte(b, '/') + 1
+
+ dirMin := min(aDirLen, bDirLen)
+ dirMax := max(aDirLen, bDirLen)
+
+ var dirScoreLtr, dirScoreRtl int
+ if dirMax == 0 {
+ dirScoreLtr = 100
+ dirScoreRtl = 100
+ } else {
+ var dirSim int
+
+ for ; dirSim < dirMin; dirSim++ {
+ if a[dirSim] != b[dirSim] {
+ break
+ }
+ }
+
+ dirScoreLtr = dirSim * 100 / dirMax
+
+ if dirScoreLtr == 100 {
+ dirScoreRtl = 100
+ } else {
+ for dirSim = 0; dirSim < dirMin; dirSim++ {
+ if a[aDirLen-1-dirSim] != b[bDirLen-1-dirSim] {
+ break
+ }
+ }
+ dirScoreRtl = dirSim * 100 / dirMax
+ }
+ }
+
+ fileMin := min(len(a)-aDirLen, len(b)-bDirLen)
+ fileMax := max(len(a)-aDirLen, len(b)-bDirLen)
+
+ fileSim := 0
+ for ; fileSim < fileMin; fileSim++ {
+ if a[len(a)-1-fileSim] != b[len(b)-1-fileSim] {
+ break
+ }
+ }
+ fileScore := fileSim * 100 / fileMax
+
+ return (((dirScoreLtr + dirScoreRtl) * 25) + (fileScore * 50)) / 100
+}
+
+func changeName(c *Change) string {
+ if c.To != empty {
+ return c.To.Name
+ }
+ return c.From.Name
+}
+
+func changeHash(c *Change) plumbing.Hash {
+ if c.To != empty {
+ return c.To.TreeEntry.Hash
+ }
+
+ return c.From.TreeEntry.Hash
+}
+
+func changeMode(c *Change) filemode.FileMode {
+ if c.To != empty {
+ return c.To.TreeEntry.Mode
+ }
+
+ return c.From.TreeEntry.Mode
+}
+
+func sameMode(a, b *Change) bool {
+ return changeMode(a) == changeMode(b)
+}
+
+func groupChangesByHash(changes []*Change) map[plumbing.Hash][]*Change {
+ var result = make(map[plumbing.Hash][]*Change)
+ for _, c := range changes {
+ hash := changeHash(c)
+ result[hash] = append(result[hash], c)
+ }
+ return result
+}
+
+type similarityMatrix []similarityPair
+
+func (m similarityMatrix) Len() int { return len(m) }
+func (m similarityMatrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
+func (m similarityMatrix) Less(i, j int) bool {
+ if m[i].score == m[j].score {
+ if m[i].added == m[j].added {
+ return m[i].deleted < m[j].deleted
+ }
+ return m[i].added < m[j].added
+ }
+ return m[i].score < m[j].score
+}
+
+type similarityPair struct {
+ // index of the added file
+ added int
+ // index of the deleted file
+ deleted int
+ // similarity score
+ score int
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func buildSimilarityMatrix(srcs, dsts []*Change, renameScore int) (similarityMatrix, error) {
+ // Allocate for the worst-case scenario where every pair has a score
+ // that we need to consider. We might not need that many.
+ matrix := make(similarityMatrix, 0, len(srcs)*len(dsts))
+ srcSizes := make([]int64, len(srcs))
+ dstSizes := make([]int64, len(dsts))
+ dstTooLarge := make(map[int]bool)
+
+ // Consider each pair of files, if the score is above the minimum
+ // threshold we need to record that scoring in the matrix so we can
+ // later find the best matches.
+outerLoop:
+ for srcIdx, src := range srcs {
+ if changeMode(src) != filemode.Regular {
+ continue
+ }
+
+ // Declare the from file and the similarity index here to be able to
+ // reuse it inside the inner loop. The reason to not initialize them
+ // here is so we can skip the initialization in case they happen to
+ // not be needed later. They will be initialized inside the inner
+ // loop if and only if they're needed and reused in subsequent passes.
+ var from *File
+ var s *similarityIndex
+ var err error
+ for dstIdx, dst := range dsts {
+ if changeMode(dst) != filemode.Regular {
+ continue
+ }
+
+ if dstTooLarge[dstIdx] {
+ continue
+ }
+
+ var to *File
+ srcSize := srcSizes[srcIdx]
+ if srcSize == 0 {
+ from, _, err = src.Files()
+ if err != nil {
+ return nil, err
+ }
+ srcSize = from.Size + 1
+ srcSizes[srcIdx] = srcSize
+ }
+
+ dstSize := dstSizes[dstIdx]
+ if dstSize == 0 {
+ _, to, err = dst.Files()
+ if err != nil {
+ return nil, err
+ }
+ dstSize = to.Size + 1
+ dstSizes[dstIdx] = dstSize
+ }
+
+ min, max := srcSize, dstSize
+ if dstSize < srcSize {
+ min = dstSize
+ max = srcSize
+ }
+
+ if int(min*100/max) < renameScore {
+ // File sizes are too different to be a match
+ continue
+ }
+
+ if s == nil {
+ s, err = fileSimilarityIndex(from)
+ if err != nil {
+ if err == errIndexFull {
+ continue outerLoop
+ }
+ return nil, err
+ }
+ }
+
+ if to == nil {
+ _, to, err = dst.Files()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ di, err := fileSimilarityIndex(to)
+ if err != nil {
+ if err == errIndexFull {
+ dstTooLarge[dstIdx] = true
+ }
+
+ return nil, err
+ }
+
+ contentScore := s.score(di, 10000)
+ // The name score returns a value between 0 and 100, so we need to
+ // convert it to the same range as the content score.
+ nameScore := nameSimilarityScore(src.From.Name, dst.To.Name) * 100
+ score := (contentScore*99 + nameScore*1) / 10000
+
+ if score < renameScore {
+ continue
+ }
+
+ matrix = append(matrix, similarityPair{added: dstIdx, deleted: srcIdx, score: score})
+ }
+ }
+
+ sort.Stable(matrix)
+
+ return matrix, nil
+}
+
+func compactChanges(changes []*Change) []*Change {
+ var result []*Change
+ for _, c := range changes {
+ if c != nil {
+ result = append(result, c)
+ }
+ }
+ return result
+}
+
+const (
+ keyShift = 32
+ maxCountValue = (1 << keyShift) - 1
+)
+
+var errIndexFull = errors.New("index is full")
+
+// similarityIndex is an index structure of lines/blocks in one file.
+// This structure can be used to compute an approximation of the similarity
+// between two files.
+// To save space in memory, this index uses a space efficient encoding which
+// will not exceed 1MiB per instance. The index starts out at a smaller size
+// (closer to 2KiB), but may grow as more distinct blocks withing the scanned
+// file are discovered.
+// see: https://github.com/eclipse/jgit/blob/master/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
+type similarityIndex struct {
+ hashed uint64
+ // number of non-zero entries in hashes
+ numHashes int
+ growAt int
+ hashes []keyCountPair
+ hashBits int
+}
+
+func fileSimilarityIndex(f *File) (*similarityIndex, error) {
+ idx := newSimilarityIndex()
+ if err := idx.hash(f); err != nil {
+ return nil, err
+ }
+
+ sort.Stable(keyCountPairs(idx.hashes))
+
+ return idx, nil
+}
+
+func newSimilarityIndex() *similarityIndex {
+ return &similarityIndex{
+ hashBits: 8,
+ hashes: make([]keyCountPair, 1<<8),
+ growAt: shouldGrowAt(8),
+ }
+}
+
+func (i *similarityIndex) hash(f *File) error {
+ isBin, err := f.IsBinary()
+ if err != nil {
+ return err
+ }
+
+ r, err := f.Reader()
+ if err != nil {
+ return err
+ }
+
+ defer ioutil.CheckClose(r, &err)
+
+ return i.hashContent(r, f.Size, isBin)
+}
+
+func (i *similarityIndex) hashContent(r io.Reader, size int64, isBin bool) error {
+ var buf = make([]byte, 4096)
+ var ptr, cnt int
+ remaining := size
+
+ for 0 < remaining {
+ hash := 5381
+ var blockHashedCnt uint64
+
+ // Hash one line or block, whatever happens first
+ n := int64(0)
+ for {
+ if ptr == cnt {
+ ptr = 0
+ var err error
+ cnt, err = io.ReadFull(r, buf)
+ if err != nil && err != io.ErrUnexpectedEOF {
+ return err
+ }
+
+ if cnt == 0 {
+ return io.EOF
+ }
+ }
+ n++
+ c := buf[ptr] & 0xff
+ ptr++
+
+ // Ignore CR in CRLF sequence if it's text
+ if !isBin && c == '\r' && ptr < cnt && buf[ptr] == '\n' {
+ continue
+ }
+ blockHashedCnt++
+
+ if c == '\n' {
+ break
+ }
+
+ hash = (hash << 5) + hash + int(c)
+
+ if n >= 64 || n >= remaining {
+ break
+ }
+ }
+ i.hashed += blockHashedCnt
+ if err := i.add(hash, blockHashedCnt); err != nil {
+ return err
+ }
+ remaining -= n
+ }
+
+ return nil
+}
+
+// score computes the similarity score between this index and another one.
+// A region of a file is defined as a line in a text file or a fixed-size
+// block in a binary file. To prepare an index, each region in the file is
+// hashed; the values and counts of hashes are retained in a sorted table.
+// Define the similarity fraction F as the count of matching regions between
+// the two files divided between the maximum count of regions in either file.
+// The similarity score is F multiplied by the maxScore constant, yielding a
+// range [0, maxScore]. It is defined as maxScore for the degenerate case of
+// two empty files.
+// The similarity score is symmetrical; i.e. a.score(b) == b.score(a).
+func (i *similarityIndex) score(other *similarityIndex, maxScore int) int {
+ var maxHashed = i.hashed
+ if maxHashed < other.hashed {
+ maxHashed = other.hashed
+ }
+ if maxHashed == 0 {
+ return maxScore
+ }
+
+ return int(i.common(other) * uint64(maxScore) / maxHashed)
+}
+
+func (i *similarityIndex) common(dst *similarityIndex) uint64 {
+ srcIdx, dstIdx := 0, 0
+ if i.numHashes == 0 || dst.numHashes == 0 {
+ return 0
+ }
+
+ var common uint64
+ srcKey, dstKey := i.hashes[srcIdx].key(), dst.hashes[dstIdx].key()
+
+ for {
+ if srcKey == dstKey {
+ srcCnt, dstCnt := i.hashes[srcIdx].count(), dst.hashes[dstIdx].count()
+ if srcCnt < dstCnt {
+ common += srcCnt
+ } else {
+ common += dstCnt
+ }
+
+ srcIdx++
+ if srcIdx == len(i.hashes) {
+ break
+ }
+ srcKey = i.hashes[srcIdx].key()
+
+ dstIdx++
+ if dstIdx == len(dst.hashes) {
+ break
+ }
+ dstKey = dst.hashes[dstIdx].key()
+ } else if srcKey < dstKey {
+ // Region of src that is not in dst
+ srcIdx++
+ if srcIdx == len(i.hashes) {
+ break
+ }
+ srcKey = i.hashes[srcIdx].key()
+ } else {
+ // Region of dst that is not in src
+ dstIdx++
+ if dstIdx == len(dst.hashes) {
+ break
+ }
+ dstKey = dst.hashes[dstIdx].key()
+ }
+ }
+
+ return common
+}
+
+func (i *similarityIndex) add(key int, cnt uint64) error {
+ key = int(uint32(key)*0x9e370001 >> 1)
+
+ j := i.slot(key)
+ for {
+ v := i.hashes[j]
+ if v == 0 {
+ // It's an empty slot, so we can store it here.
+ if i.growAt <= i.numHashes {
+ if err := i.grow(); err != nil {
+ return err
+ }
+ j = i.slot(key)
+ continue
+ }
+
+ var err error
+ i.hashes[j], err = newKeyCountPair(key, cnt)
+ if err != nil {
+ return err
+ }
+ i.numHashes++
+ return nil
+ } else if v.key() == key {
+ // It's the same key, so increment the counter.
+ var err error
+ i.hashes[j], err = newKeyCountPair(key, v.count()+cnt)
+ if err != nil {
+ return err
+ }
+ return nil
+ } else if j+1 >= len(i.hashes) {
+ j = 0
+ } else {
+ j++
+ }
+ }
+}
+
+type keyCountPair uint64
+
+func newKeyCountPair(key int, cnt uint64) (keyCountPair, error) {
+ if cnt > maxCountValue {
+ return 0, errIndexFull
+ }
+
+ return keyCountPair((uint64(key) << keyShift) | cnt), nil
+}
+
+func (p keyCountPair) key() int {
+ return int(p >> keyShift)
+}
+
+func (p keyCountPair) count() uint64 {
+ return uint64(p) & maxCountValue
+}
+
+func (i *similarityIndex) slot(key int) int {
+ // We use 31 - hashBits because the upper bit was already forced
+ // to be 0 and we want the remaining high bits to be used as the
+ // table slot.
+ return int(uint32(key) >> uint(31 - i.hashBits))
+}
+
+func shouldGrowAt(hashBits int) int {
+ return (1 << uint(hashBits)) * (hashBits - 3) / hashBits
+}
+
+func (i *similarityIndex) grow() error {
+ if i.hashBits == 30 {
+ return errIndexFull
+ }
+
+ old := i.hashes
+
+ i.hashBits++
+ i.growAt = shouldGrowAt(i.hashBits)
+
+ // TODO(erizocosmico): find a way to check if it will OOM and return
+ // errIndexFull instead.
+ i.hashes = make([]keyCountPair, 1<<uint(i.hashBits))
+
+ for _, v := range old {
+ if v != 0 {
+ j := i.slot(v.key())
+ for i.hashes[j] != 0 {
+ j++
+ if j >= len(i.hashes) {
+ j = 0
+ }
+ }
+ i.hashes[j] = v
+ }
+ }
+
+ return nil
+}
+
+type keyCountPairs []keyCountPair
+
+func (p keyCountPairs) Len() int { return len(p) }
+func (p keyCountPairs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p keyCountPairs) Less(i, j int) bool { return p[i] < p[j] }
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go
index d3c8c77d18..5e6378ca49 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go
@@ -304,29 +304,34 @@ func (t *Tree) buildMap() {
}
// Diff returns a list of changes between this tree and the provided one
-func (from *Tree) Diff(to *Tree) (Changes, error) {
- return DiffTree(from, to)
+func (t *Tree) Diff(to *Tree) (Changes, error) {
+ return t.DiffContext(context.Background(), to)
}
-// Diff returns a list of changes between this tree and the provided one
-// Error will be returned if context expires
-// Provided context must be non nil
-func (from *Tree) DiffContext(ctx context.Context, to *Tree) (Changes, error) {
- return DiffTreeContext(ctx, from, to)
+// DiffContext returns a list of changes between this tree and the provided one
+// Error will be returned if context expires. Provided context must be non nil.
+//
+// NOTE: Since version 5.1.0 the renames are correctly handled, the settings
+// used are the recommended options DefaultDiffTreeOptions.
+func (t *Tree) DiffContext(ctx context.Context, to *Tree) (Changes, error) {
+ return DiffTreeWithOptions(ctx, t, to, DefaultDiffTreeOptions)
}
// Patch returns a slice of Patch objects with all the changes between trees
// in chunks. This representation can be used to create several diff outputs.
-func (from *Tree) Patch(to *Tree) (*Patch, error) {
- return from.PatchContext(context.Background(), to)
+func (t *Tree) Patch(to *Tree) (*Patch, error) {
+ return t.PatchContext(context.Background(), to)
}
-// Patch returns a slice of Patch objects with all the changes between trees
-// in chunks. This representation can be used to create several diff outputs.
-// If context expires, an error will be returned
-// Provided context must be non-nil
-func (from *Tree) PatchContext(ctx context.Context, to *Tree) (*Patch, error) {
- changes, err := DiffTreeContext(ctx, from, to)
+// PatchContext returns a slice of Patch objects with all the changes between
+// trees in chunks. This representation can be used to create several diff
+// outputs. If context expires, an error will be returned. Provided context must
+// be non-nil.
+//
+// NOTE: Since version 5.1.0 the renames are correctly handled, the settings
+// used are the recommended options DefaultDiffTreeOptions.
+func (t *Tree) PatchContext(ctx context.Context, to *Tree) (*Patch, error) {
+ changes, err := t.DiffContext(ctx, to)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/advrefs.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/advrefs.go
index ab286c6386..1bd724cad5 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/advrefs.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/advrefs.go
@@ -201,3 +201,11 @@ func (a *AdvRefs) addSymbolicRefs(s storer.ReferenceStorer) error {
func (a *AdvRefs) supportSymrefs() bool {
return a.Capabilities.Supports(capability.SymRef)
}
+
+// IsEmpty returns true if doesn't contain any reference.
+func (a *AdvRefs) IsEmpty() bool {
+ return a.Head == nil &&
+ len(a.References) == 0 &&
+ len(a.Peeled) == 0 &&
+ len(a.Shallows) == 0
+}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go
index d564d25a64..89432e34c1 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go
@@ -175,6 +175,13 @@ func (s *session) AdvertisedReferences() (*packp.AdvRefs, error) {
}
}
+ // Some servers like jGit, announce capabilities instead of returning an
+ // packp message with a flush. This verifies that we received a empty
+ // adv-refs, even it contains capabilities.
+ if !s.isReceivePack && ar.IsEmpty() {
+ return nil, transport.ErrEmptyRemoteRepository
+ }
+
transport.FilterUnsupportedCapabilities(ar.Capabilities)
s.advRefs = ar
return ar, nil
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go
index 71845e3bbe..727f902150 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go
@@ -243,11 +243,13 @@ func (s *rpSession) ReceivePack(ctx context.Context, req *packp.ReferenceUpdateR
//TODO: Implement 'atomic' update of references.
- r := ioutil.NewContextReadCloser(ctx, req.Packfile)
- if err := s.writePackfile(r); err != nil {
- s.unpackErr = err
- s.firstErr = err
- return s.reportStatus(), err
+ if req.Packfile != nil {
+ r := ioutil.NewContextReadCloser(ctx, req.Packfile)
+ if err := s.writePackfile(r); err != nil {
+ s.unpackErr = err
+ s.firstErr = err
+ return s.reportStatus(), err
+ }
}
s.updateReferences(req)
diff --git a/vendor/github.com/go-git/go-git/v5/remote.go b/vendor/github.com/go-git/go-git/v5/remote.go
index 242df0d44a..e642c57291 100644
--- a/vendor/github.com/go-git/go-git/v5/remote.go
+++ b/vendor/github.com/go-git/go-git/v5/remote.go
@@ -29,6 +29,7 @@ var (
NoErrAlreadyUpToDate = errors.New("already up-to-date")
ErrDeleteRefNotSupported = errors.New("server does not support delete-refs")
ErrForceNeeded = errors.New("some refs were not updated")
+ ErrExactSHA1NotSupported = errors.New("server does not support exact SHA1 refspec")
)
const (
@@ -122,6 +123,15 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
return ErrDeleteRefNotSupported
}
+ if o.Force {
+ for i := 0; i < len(o.RefSpecs); i++ {
+ rs := &o.RefSpecs[i]
+ if !rs.IsForceUpdate() {
+ o.RefSpecs[i] = config.RefSpec("+" + rs.String())
+ }
+ }
+ }
+
localRefs, err := r.references()
if err != nil {
return err
@@ -303,6 +313,10 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
return nil, err
}
+ if err := r.isSupportedRefSpec(o.RefSpecs, ar); err != nil {
+ return nil, err
+ }
+
remoteRefs, err := ar.AllReferences()
if err != nil {
return nil, err
@@ -546,6 +560,7 @@ func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
func (r *Remote) references() ([]*plumbing.Reference, error) {
var localRefs []*plumbing.Reference
+
iter, err := r.s.IterReferences()
if err != nil {
return nil, err
@@ -701,6 +716,11 @@ func doCalculateRefs(
return err
}
+ if s.IsExactSHA1() {
+ ref := plumbing.NewHashReference(s.Dst(""), plumbing.NewHash(s.Src()))
+ return refs.SetReference(ref)
+ }
+
var matched bool
err = iter.ForEach(func(ref *plumbing.Reference) error {
if !s.Match(ref.Name()) {
@@ -850,6 +870,26 @@ func (r *Remote) newUploadPackRequest(o *FetchOptions,
return req, nil
}
+func (r *Remote) isSupportedRefSpec(refs []config.RefSpec, ar *packp.AdvRefs) error {
+ var containsIsExact bool
+ for _, ref := range refs {
+ if ref.IsExactSHA1() {
+ containsIsExact = true
+ }
+ }
+
+ if !containsIsExact {
+ return nil
+ }
+
+ if ar.Capabilities.Supports(capability.AllowReachableSHA1InWant) ||
+ ar.Capabilities.Supports(capability.AllowTipSHA1InWant) {
+ return nil
+ }
+
+ return ErrExactSHA1NotSupported
+}
+
func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.Progress) io.Reader {
var t sideband.Type
@@ -883,7 +923,7 @@ func (r *Remote) updateLocalReferenceStorage(
}
for _, ref := range fetchedRefs {
- if !spec.Match(ref.Name()) {
+ if !spec.Match(ref.Name()) && !spec.IsExactSHA1() {
continue
}
diff --git a/vendor/github.com/go-git/go-git/v5/repository.go b/vendor/github.com/go-git/go-git/v5/repository.go
index c83a136036..47318d113b 100644
--- a/vendor/github.com/go-git/go-git/v5/repository.go
+++ b/vendor/github.com/go-git/go-git/v5/repository.go
@@ -13,7 +13,6 @@ import (
"strings"
"time"
- "golang.org/x/crypto/openpgp"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/internal/revision"
"github.com/go-git/go-git/v5/plumbing"
@@ -24,6 +23,8 @@ import (
"github.com/go-git/go-git/v5/storage"
"github.com/go-git/go-git/v5/storage/filesystem"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/imdario/mergo"
+ "golang.org/x/crypto/openpgp"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs"
@@ -155,7 +156,7 @@ func setConfigWorktree(r *Repository, worktree, storage billy.Filesystem) error
return nil
}
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -434,14 +435,56 @@ func cleanUpDir(path string, all bool) error {
return err
}
-// Config return the repository config
+// Config return the repository config. In a filesystem backed repository this
+// means read the `.git/config`.
func (r *Repository) Config() (*config.Config, error) {
return r.Storer.Config()
}
+// SetConfig marshall and writes the repository config. In a filesystem backed
+// repository this means write the `.git/config`. This function should be called
+// with the result of `Repository.Config` and never with the output of
+// `Repository.ConfigScoped`.
+func (r *Repository) SetConfig(cfg *config.Config) error {
+ return r.Storer.SetConfig(cfg)
+}
+
+// ConfigScoped returns the repository config, merged with requested scope and
+// lower. For example if, config.GlobalScope is given the local and global config
+// are returned merged in one config value.
+func (r *Repository) ConfigScoped(scope config.Scope) (*config.Config, error) {
+ // TODO(mcuadros): v6, add this as ConfigOptions.Scoped
+
+ var err error
+ system := config.NewConfig()
+ if scope >= config.SystemScope {
+ system, err = config.LoadConfig(config.SystemScope)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ global := config.NewConfig()
+ if scope >= config.GlobalScope {
+ global, err = config.LoadConfig(config.GlobalScope)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ local, err := r.Storer.Config()
+ if err != nil {
+ return nil, err
+ }
+
+ _ = mergo.Merge(global, system)
+ _ = mergo.Merge(local, global)
+ return local, nil
+}
+
// Remote return a remote if exists
func (r *Repository) Remote(name string) (*Remote, error) {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -456,7 +499,7 @@ func (r *Repository) Remote(name string) (*Remote, error) {
// Remotes returns a list with all the remotes
func (r *Repository) Remotes() ([]*Remote, error) {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -480,7 +523,7 @@ func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error) {
remote := NewRemote(r.Storer, c)
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -511,7 +554,7 @@ func (r *Repository) CreateRemoteAnonymous(c *config.RemoteConfig) (*Remote, err
// DeleteRemote delete a remote from the repository and delete the config
func (r *Repository) DeleteRemote(name string) error {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -526,7 +569,7 @@ func (r *Repository) DeleteRemote(name string) error {
// Branch return a Branch if exists
func (r *Repository) Branch(name string) (*config.Branch, error) {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -545,7 +588,7 @@ func (r *Repository) CreateBranch(c *config.Branch) error {
return err
}
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -560,7 +603,7 @@ func (r *Repository) CreateBranch(c *config.Branch) error {
// DeleteBranch delete a Branch from the repository and delete the config
func (r *Repository) DeleteBranch(name string) error {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -835,7 +878,7 @@ func (r *Repository) cloneRefSpec(o *CloneOptions) []config.RefSpec {
}
func (r *Repository) setIsBare(isBare bool) error {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -851,7 +894,7 @@ func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.Remot
c.Fetch = r.cloneRefSpec(o)
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -1541,7 +1584,7 @@ func (r *Repository) createNewObjectPack(cfg *RepackConfig) (h plumbing.Hash, er
return h, err
}
defer ioutil.CheckClose(wc, &err)
- scfg, err := r.Storer.Config()
+ scfg, err := r.Config()
if err != nil {
return h, err
}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/config.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/config.go
index 01b35b4e36..78a646465a 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/config.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/config.go
@@ -1,7 +1,6 @@
package filesystem
import (
- stdioutil "io/ioutil"
"os"
"github.com/go-git/go-git/v5/config"
@@ -14,29 +13,17 @@ type ConfigStorage struct {
}
func (c *ConfigStorage) Config() (conf *config.Config, err error) {
- cfg := config.NewConfig()
-
f, err := c.dir.Config()
if err != nil {
if os.IsNotExist(err) {
- return cfg, nil
+ return config.NewConfig(), nil
}
return nil, err
}
defer ioutil.CheckClose(f, &err)
-
- b, err := stdioutil.ReadAll(f)
- if err != nil {
- return nil, err
- }
-
- if err = cfg.Unmarshal(b); err != nil {
- return nil, err
- }
-
- return cfg, err
+ return config.ReadConfig(f)
}
func (c *ConfigStorage) SetConfig(cfg *config.Config) (err error) {
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go
index 3ce9daeda2..83c7683071 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go
@@ -57,6 +57,9 @@ var (
// targeting a non-existing object. This usually means the repository
// is corrupt.
ErrSymRefTargetNotFound = errors.New("symbolic reference target not found")
+ // ErrIsDir is returned when a reference file is attempting to be read,
+ // but the path specified is a directory.
+ ErrIsDir = errors.New("reference path is a directory")
)
// Options holds configuration for the storage.
@@ -926,6 +929,14 @@ func (d *DotGit) addRefFromHEAD(refs *[]*plumbing.Reference) error {
func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference, err error) {
path = d.fs.Join(path, d.fs.Join(strings.Split(name, "/")...))
+ st, err := d.fs.Stat(path)
+ if err != nil {
+ return nil, err
+ }
+ if st.IsDir() {
+ return nil, ErrIsDir
+ }
+
f, err := d.fs.Open(path)
if err != nil {
return nil, err
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
index 862cc1b1ae..7437174390 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
@@ -408,6 +408,8 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
return nil, err
}
+ defer ioutil.CheckClose(w, &err)
+
s.objectCache.Put(obj)
_, err = io.Copy(w, r)
diff --git a/vendor/github.com/go-git/go-git/v5/submodule.go b/vendor/github.com/go-git/go-git/v5/submodule.go
index 92ccdb1c8a..dff26b0d80 100644
--- a/vendor/github.com/go-git/go-git/v5/submodule.go
+++ b/vendor/github.com/go-git/go-git/v5/submodule.go
@@ -35,7 +35,7 @@ func (s *Submodule) Config() *config.Submodule {
// Init initialize the submodule reading the recorded Entry in the index for
// the given submodule
func (s *Submodule) Init() error {
- cfg, err := s.w.r.Storer.Config()
+ cfg, err := s.w.r.Config()
if err != nil {
return err
}
diff --git a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
index 90d9c958d0..bd084b2ab0 100644
--- a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
+++ b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
@@ -23,7 +23,7 @@ package merkletrie
// # Cases
//
-// When comparing noders in both trees you will found yourself in
+// When comparing noders in both trees you will find yourself in
// one of 169 possible cases, but if we ignore moves, we can
// simplify a lot the search space into the following table:
//
@@ -256,17 +256,21 @@ import (
)
var (
+ // ErrCanceled is returned whenever the operation is canceled.
ErrCanceled = errors.New("operation canceled")
)
// DiffTree calculates the list of changes between two merkletries. It
// uses the provided hashEqual callback to compare noders.
-func DiffTree(fromTree, toTree noder.Noder,
- hashEqual noder.Equal) (Changes, error) {
+func DiffTree(
+ fromTree,
+ toTree noder.Noder,
+ hashEqual noder.Equal,
+) (Changes, error) {
return DiffTreeContext(context.Background(), fromTree, toTree, hashEqual)
}
-// DiffTree calculates the list of changes between two merkletries. It
+// DiffTreeContext calculates the list of changes between two merkletries. It
// uses the provided hashEqual callback to compare noders.
// Error will be returned if context expires
// Provided context must be non nil