diff options
76 files changed, 1003 insertions, 181 deletions
diff --git a/_examples/README.md b/_examples/README.md index 1d82fbd..80d4dd5 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -17,6 +17,7 @@ Here you can find a list of annotated _go-git_ examples: - [log](log/main.go) - Emulate `git log` command output iterating all the commit history from HEAD reference. - [branch](branch/main.go) - How to create and remove branches or any other kind of reference. - [tag](tag/main.go) - List/print repository tags. +- [tag create and push](tag-create-push/main.go) - Create and push a new tag. - [remotes](remotes/main.go) - Working with remotes: adding, removing, etc. - [progress](progress/main.go) - Printing the progress information from the sideband. - [revision](revision/main.go) - Solve a revision into a commit. diff --git a/_examples/tag-create-push/main.go b/_examples/tag-create-push/main.go new file mode 100644 index 0000000..c443641 --- /dev/null +++ b/_examples/tag-create-push/main.go @@ -0,0 +1,148 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + + "github.com/go-git/go-git/v5" + . "github.com/go-git/go-git/v5/_examples" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" +) + +// Example of how create a tag and push it to a remote. +func main() { + CheckArgs("<ssh-url>", "<directory>", "<tag>", "<name>", "<email>", "<public-key>") + url := os.Args[1] + directory := os.Args[2] + tag := os.Args[3] + key := os.Args[6] + + r, err := cloneRepo(url, directory, key) + + if err != nil { + log.Printf("clone repo error: %s", err) + return + } + + created, err := setTag(r, tag) + if err != nil { + log.Printf("create tag error: %s", err) + return + } + + if created { + err = pushTags(r, key) + if err != nil { + log.Printf("push tag error: %s", err) + return + } + } +} + +func cloneRepo(url, dir, publicKeyPath string) (*git.Repository, error) { + log.Printf("cloning %s into %s", url, dir) + auth, keyErr := publicKey(publicKeyPath) + if keyErr != nil { + return nil, keyErr + } + + Info("git clone %s", url) + r, err := git.PlainClone(dir, false, &git.CloneOptions{ + Progress: os.Stdout, + URL: url, + Auth: auth, + }) + + if err != nil { + log.Printf("clone git repo error: %s", err) + return nil, err + } + + return r, nil +} + +func publicKey(filePath string) (*ssh.PublicKeys, error) { + var publicKey *ssh.PublicKeys + sshKey, _ := ioutil.ReadFile(filePath) + publicKey, err := ssh.NewPublicKeys("git", []byte(sshKey), "") + if err != nil { + return nil, err + } + return publicKey, err +} + +func tagExists(tag string, r *git.Repository) bool { + tagFoundErr := "tag was found" + Info("git show-ref --tag") + tags, err := r.TagObjects() + if err != nil { + log.Printf("get tags error: %s", err) + return false + } + res := false + err = tags.ForEach(func(t *object.Tag) error { + if t.Name == tag { + res = true + return fmt.Errorf(tagFoundErr) + } + return nil + }) + if err != nil && err.Error() != tagFoundErr { + log.Printf("iterate tags error: %s", err) + return false + } + return res +} + +func setTag(r *git.Repository, tag string) (bool, error) { + if tagExists(tag, r) { + log.Printf("tag %s already exists", tag) + return false, nil + } + log.Printf("Set tag %s", tag) + h, err := r.Head() + if err != nil { + log.Printf("get HEAD error: %s", err) + return false, err + } + Info("git tag -a %s %s -m \"%s\"", tag, h.Hash(), tag) + _, err = r.CreateTag(tag, h.Hash(), &git.CreateTagOptions{ + Message: tag, + }) + + if err != nil { + log.Printf("create tag error: %s", err) + return false, err + } + + return true, nil +} + +func pushTags(r *git.Repository, publicKeyPath string) error { + + auth, _ := publicKey(publicKeyPath) + + po := &git.PushOptions{ + RemoteName: "origin", + Progress: os.Stdout, + RefSpecs: []config.RefSpec{config.RefSpec("refs/tags/*:refs/tags/*")}, + Auth: auth, + } + Info("git push --tags") + err := r.Push(po) + + if err != nil { + if err == git.NoErrAlreadyUpToDate { + log.Print("origin remote was up to date, no push done") + return nil + } + log.Printf("push to remote origin error: %s", err) + return err + } + + return nil +} diff --git a/blame_test.go b/blame_test.go index 398f839..7895b66 100644 --- a/blame_test.go +++ b/blame_test.go @@ -4,8 +4,8 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type BlameSuite struct { diff --git a/config/branch_test.go b/config/branch_test.go index a2c86cd..ae1fe85 100644 --- a/config/branch_test.go +++ b/config/branch_test.go @@ -1,8 +1,9 @@ package config import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" + + . "gopkg.in/check.v1" ) type BranchSuite struct{} diff --git a/config/config_test.go b/config/config_test.go index e68626b..5a88c19 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,6 +1,10 @@ package config import ( + "io/ioutil" + "os" + "path/filepath" + "github.com/go-git/go-git/v5/plumbing" . "gopkg.in/check.v1" ) @@ -172,8 +176,39 @@ func (s *ConfigSuite) TestUnmarshalMarshal(c *C) { func (s *ConfigSuite) TestLoadConfig(c *C) { cfg, err := LoadConfig(GlobalScope) - c.Assert(err, IsNil) c.Assert(cfg.User.Email, Not(Equals), "") + c.Assert(err, IsNil) + +} + +func (s *ConfigSuite) TestLoadConfigXDG(c *C) { + cfg := NewConfig() + cfg.User.Name = "foo" + cfg.User.Email = "foo@foo.com" + + tmp, err := ioutil.TempDir("", "test-commit-options") + c.Assert(err, IsNil) + defer os.RemoveAll(tmp) + + err = os.Mkdir(filepath.Join(tmp, "git"), 0777) + c.Assert(err, IsNil) + + os.Setenv("XDG_CONFIG_HOME", tmp) + defer func() { + os.Setenv("XDG_CONFIG_HOME", "") + }() + + content, err := cfg.Marshal() + c.Assert(err, IsNil) + + cfgFile := filepath.Join(tmp, "git/config") + err = ioutil.WriteFile(cfgFile, content, 0777) + c.Assert(err, IsNil) + + cfg, err = LoadConfig(GlobalScope) + c.Assert(err, IsNil) + + c.Assert(cfg.User.Email, Equals, "foo@foo.com") } func (s *ConfigSuite) TestValidateConfig(c *C) { @@ -8,7 +8,7 @@ require ( github.com/gliderlabs/ssh v0.2.2 github.com/go-git/gcfg v1.5.0 github.com/go-git/go-billy/v5 v5.0.0 - github.com/go-git/go-git-fixtures/v4 v4.0.1 + github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 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 @@ -20,6 +20,8 @@ github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agR github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc= github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/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= diff --git a/internal/revision/parser_test.go b/internal/revision/parser_test.go index fe45228..4bb007f 100644 --- a/internal/revision/parser_test.go +++ b/internal/revision/parser_test.go @@ -96,8 +96,8 @@ func (s *ParserSuite) TestParseWithValidExpression(c *C) { TildePath{3}, }, "@{2016-12-16T21:42:47Z}": []Revisioner{AtDate{tim}}, - "@{1}": []Revisioner{AtReflog{1}}, - "@{-1}": []Revisioner{AtCheckout{1}}, + "@{1}": []Revisioner{AtReflog{1}}, + "@{-1}": []Revisioner{AtCheckout{1}}, "master@{upstream}": []Revisioner{ Ref("master"), AtUpstream{}, @@ -211,12 +211,12 @@ func (s *ParserSuite) TestParseAtWithValidExpression(c *C) { tim, _ := time.Parse("2006-01-02T15:04:05Z", "2016-12-16T21:42:47Z") datas := map[string]Revisioner{ - "": Ref("HEAD"), - "{1}": AtReflog{1}, - "{-1}": AtCheckout{1}, - "{push}": AtPush{}, - "{upstream}": AtUpstream{}, - "{u}": AtUpstream{}, + "": Ref("HEAD"), + "{1}": AtReflog{1}, + "{-1}": AtCheckout{1}, + "{push}": AtPush{}, + "{upstream}": AtUpstream{}, + "{u}": AtUpstream{}, "{2016-12-16T21:42:47Z}": AtDate{tim}, } @@ -2,6 +2,7 @@ package git import ( "errors" + "fmt" "regexp" "strings" "time" @@ -373,6 +374,30 @@ var ( ErrMissingAuthor = errors.New("author field is required") ) +// AddOptions describes how a add operation should be performed +type AddOptions struct { + // All equivalent to `git add -A`, update the index not only where the + // working tree has a file matching `Path` but also where the index already + // has an entry. This adds, modifies, and removes index entries to match the + // working tree. If no `Path` nor `Glob` is given when `All` option is + // used, all files in the entire working tree are updated. + All bool + // Path is the exact filepath to a the file or directory to be added. + Path string + // Glob adds all paths, matching pattern, to the index. If pattern matches a + // directory path, all directory contents are added to the index recursively. + Glob string +} + +// Validate validates the fields and sets the default values. +func (o *AddOptions) Validate(r *Repository) error { + if o.Path != "" && o.Glob != "" { + return fmt.Errorf("fields Path and Glob are mutual exclusive") + } + + return nil +} + // CommitOptions describes how a commit operation should be performed. type CommitOptions struct { // All automatically stage files that have been modified and deleted, but @@ -464,7 +489,8 @@ var ( // CreateTagOptions describes how a tag object should be created. type CreateTagOptions struct { - // Tagger defines the signature of the tag creator. + // Tagger defines the signature of the tag creator. If Tagger is empty the + // Name and Email is read from the config, and time.Now it's used as When. Tagger *object.Signature // Message defines the annotation of the tag. It is canonicalized during // validation into the format expected by git - no leading whitespace and @@ -478,7 +504,9 @@ type CreateTagOptions struct { // Validate validates the fields and sets the default values. func (o *CreateTagOptions) Validate(r *Repository, hash plumbing.Hash) error { if o.Tagger == nil { - return ErrMissingTagger + if err := o.loadConfigTagger(r); err != nil { + return err + } } if o.Message == "" { @@ -491,6 +519,35 @@ func (o *CreateTagOptions) Validate(r *Repository, hash plumbing.Hash) error { return nil } +func (o *CreateTagOptions) loadConfigTagger(r *Repository) error { + cfg, err := r.ConfigScoped(config.SystemScope) + if err != nil { + return err + } + + if o.Tagger == nil && cfg.Author.Email != "" && cfg.Author.Name != "" { + o.Tagger = &object.Signature{ + Name: cfg.Author.Name, + Email: cfg.Author.Email, + When: time.Now(), + } + } + + if o.Tagger == nil && cfg.User.Email != "" && cfg.User.Name != "" { + o.Tagger = &object.Signature{ + Name: cfg.User.Name, + Email: cfg.User.Email, + When: time.Now(), + } + } + + if o.Tagger == nil { + return ErrMissingTagger + } + + return nil +} + // ListOptions describes how a remote list should be performed. type ListOptions struct { // Auth credentials, if required, to use with the remote repository. @@ -545,6 +602,9 @@ type PlainOpenOptions struct { // DetectDotGit defines whether parent directories should be // walked until a .git directory or file is found. DetectDotGit bool + // Enable .git/commondir support (see https://git-scm.com/docs/gitrepository-layout#Documentation/gitrepository-layout.txt). + // NOTE: This option will only work with the filesystem storage. + EnableDotGitCommonDir bool } // Validate validates the fields and sets the default values. diff --git a/options_test.go b/options_test.go index aa36dab..86d725a 100644 --- a/options_test.go +++ b/options_test.go @@ -1,6 +1,12 @@ package git import ( + "io/ioutil" + "os" + "path/filepath" + + "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" . "gopkg.in/check.v1" ) @@ -27,3 +33,81 @@ func (s *OptionsSuite) TestCommitOptionsCommitter(c *C) { c.Assert(o.Committer, Equals, o.Author) } + +func (s *OptionsSuite) TestCommitOptionsLoadGlobalConfigUser(c *C) { + cfg := config.NewConfig() + cfg.User.Name = "foo" + cfg.User.Email = "foo@foo.com" + + s.writeGlobalConfig(c, cfg) + defer s.clearGlobalConfig(c) + + o := CommitOptions{} + err := o.Validate(s.Repository) + c.Assert(err, IsNil) + + c.Assert(o.Author.Name, Equals, "foo") + c.Assert(o.Author.Email, Equals, "foo@foo.com") + c.Assert(o.Committer.Name, Equals, "foo") + c.Assert(o.Committer.Email, Equals, "foo@foo.com") +} + +func (s *OptionsSuite) TestCommitOptionsLoadGlobalCommitter(c *C) { + cfg := config.NewConfig() + cfg.User.Name = "foo" + cfg.User.Email = "foo@foo.com" + cfg.Committer.Name = "bar" + cfg.Committer.Email = "bar@bar.com" + + s.writeGlobalConfig(c, cfg) + defer s.clearGlobalConfig(c) + + o := CommitOptions{} + err := o.Validate(s.Repository) + c.Assert(err, IsNil) + + c.Assert(o.Author.Name, Equals, "foo") + c.Assert(o.Author.Email, Equals, "foo@foo.com") + c.Assert(o.Committer.Name, Equals, "bar") + c.Assert(o.Committer.Email, Equals, "bar@bar.com") +} + +func (s *OptionsSuite) TestCreateTagOptionsLoadGlobal(c *C) { + cfg := config.NewConfig() + cfg.User.Name = "foo" + cfg.User.Email = "foo@foo.com" + + s.writeGlobalConfig(c, cfg) + defer s.clearGlobalConfig(c) + + o := CreateTagOptions{ + Message: "foo", + } + + err := o.Validate(s.Repository, plumbing.ZeroHash) + c.Assert(err, IsNil) + + c.Assert(o.Tagger.Name, Equals, "foo") + c.Assert(o.Tagger.Email, Equals, "foo@foo.com") +} + +func (s *OptionsSuite) writeGlobalConfig(c *C, cfg *config.Config) { + tmp, err := ioutil.TempDir("", "test-options") + c.Assert(err, IsNil) + + err = os.Mkdir(filepath.Join(tmp, "git"), 0777) + c.Assert(err, IsNil) + + os.Setenv("XDG_CONFIG_HOME", tmp) + + content, err := cfg.Marshal() + c.Assert(err, IsNil) + + cfgFile := filepath.Join(tmp, "git/config") + err = ioutil.WriteFile(cfgFile, content, 0777) + c.Assert(err, IsNil) +} + +func (s *OptionsSuite) clearGlobalConfig(c *C) { + os.Setenv("XDG_CONFIG_HOME", "") +} diff --git a/plumbing/format/commitgraph/commitgraph_test.go b/plumbing/format/commitgraph/commitgraph_test.go index 7d7444b..de61ae9 100644 --- a/plumbing/format/commitgraph/commitgraph_test.go +++ b/plumbing/format/commitgraph/commitgraph_test.go @@ -6,10 +6,11 @@ import ( "path"
"testing"
- . "gopkg.in/check.v1"
- fixtures "github.com/go-git/go-git-fixtures/v4"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/commitgraph"
+
+ fixtures "github.com/go-git/go-git-fixtures/v4"
+ . "gopkg.in/check.v1"
)
func Test(t *testing.T) { TestingT(t) }
diff --git a/plumbing/format/idxfile/encoder_test.go b/plumbing/format/idxfile/encoder_test.go index 81abb3b..32b60f9 100644 --- a/plumbing/format/idxfile/encoder_test.go +++ b/plumbing/format/idxfile/encoder_test.go @@ -6,8 +6,8 @@ import ( . "github.com/go-git/go-git/v5/plumbing/format/idxfile" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) func (s *IdxfileSuite) TestDecodeEncode(c *C) { diff --git a/plumbing/format/idxfile/idxfile_test.go b/plumbing/format/idxfile/idxfile_test.go index 5ef73d7..7a3d6bb 100644 --- a/plumbing/format/idxfile/idxfile_test.go +++ b/plumbing/format/idxfile/idxfile_test.go @@ -10,8 +10,8 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/format/idxfile" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) func BenchmarkFindOffset(b *testing.B) { diff --git a/plumbing/format/idxfile/writer_test.go b/plumbing/format/idxfile/writer_test.go index f86342f..fba3e42 100644 --- a/plumbing/format/idxfile/writer_test.go +++ b/plumbing/format/idxfile/writer_test.go @@ -9,8 +9,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/format/idxfile" "github.com/go-git/go-git/v5/plumbing/format/packfile" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type WriterSuite struct { diff --git a/plumbing/format/index/decoder.go b/plumbing/format/index/decoder.go index 79d0b9e..d341d59 100644 --- a/plumbing/format/index/decoder.go +++ b/plumbing/format/index/decoder.go @@ -390,7 +390,9 @@ func (d *treeExtensionDecoder) readEntry() (*TreeEntry, error) { e.Trees = i _, err = io.ReadFull(d.r, e.Hash[:]) - + if err != nil { + return nil, err + } return e, nil } diff --git a/plumbing/format/index/decoder_test.go b/plumbing/format/index/decoder_test.go index 4e47dde..39ab336 100644 --- a/plumbing/format/index/decoder_test.go +++ b/plumbing/format/index/decoder_test.go @@ -6,8 +6,8 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) func Test(t *testing.T) { TestingT(t) } diff --git a/plumbing/format/index/encoder_test.go b/plumbing/format/index/encoder_test.go index 17585a0..b7a73cb 100644 --- a/plumbing/format/index/encoder_test.go +++ b/plumbing/format/index/encoder_test.go @@ -5,9 +5,10 @@ import ( "strings" "time" + "github.com/go-git/go-git/v5/plumbing" + "github.com/google/go-cmp/cmp" . "gopkg.in/check.v1" - "github.com/go-git/go-git/v5/plumbing" ) func (s *IndexSuite) TestEncode(c *C) { diff --git a/plumbing/format/objfile/common_test.go b/plumbing/format/objfile/common_test.go index ec8c280..de76902 100644 --- a/plumbing/format/objfile/common_test.go +++ b/plumbing/format/objfile/common_test.go @@ -4,8 +4,9 @@ import ( "encoding/base64" "testing" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" + + . "gopkg.in/check.v1" ) type objfileFixture struct { diff --git a/plumbing/format/objfile/reader_test.go b/plumbing/format/objfile/reader_test.go index 48e7f1c..d697d54 100644 --- a/plumbing/format/objfile/reader_test.go +++ b/plumbing/format/objfile/reader_test.go @@ -7,8 +7,9 @@ import ( "io" "io/ioutil" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" + + . "gopkg.in/check.v1" ) type SuiteReader struct{} diff --git a/plumbing/format/objfile/writer_test.go b/plumbing/format/objfile/writer_test.go index 73ee662..35a9510 100644 --- a/plumbing/format/objfile/writer_test.go +++ b/plumbing/format/objfile/writer_test.go @@ -6,8 +6,9 @@ import ( "fmt" "io" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" + + . "gopkg.in/check.v1" ) type SuiteWriter struct{} diff --git a/plumbing/format/packfile/encoder_advanced_test.go b/plumbing/format/packfile/encoder_advanced_test.go index 21bf3ae..95db5c0 100644 --- a/plumbing/format/packfile/encoder_advanced_test.go +++ b/plumbing/format/packfile/encoder_advanced_test.go @@ -6,7 +6,6 @@ import ( "math/rand" "testing" - "github.com/go-git/go-billy/v5/memfs" "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/format/idxfile" @@ -14,8 +13,9 @@ import ( "github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/storage/filesystem" + "github.com/go-git/go-billy/v5/memfs" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type EncoderAdvancedSuite struct { diff --git a/plumbing/format/packfile/encoder_test.go b/plumbing/format/packfile/encoder_test.go index 2689762..d2db892 100644 --- a/plumbing/format/packfile/encoder_test.go +++ b/plumbing/format/packfile/encoder_test.go @@ -5,13 +5,13 @@ import ( "io" stdioutil "io/ioutil" - "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/format/idxfile" "github.com/go-git/go-git/v5/storage/memory" + "github.com/go-git/go-billy/v5/memfs" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type EncoderSuite struct { diff --git a/plumbing/format/packfile/patch_delta.go b/plumbing/format/packfile/patch_delta.go index 1dc8b8b..9e90f30 100644 --- a/plumbing/format/packfile/patch_delta.go +++ b/plumbing/format/packfile/patch_delta.go @@ -49,7 +49,6 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) { return err } - target.SetSize(int64(dst.Len())) b := byteSlicePool.Get().([]byte) @@ -113,7 +112,7 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { invalidOffsetSize(offset, sz, srcSz) { break } - dst.Write(src[offset:offset+sz]) + dst.Write(src[offset : offset+sz]) remainingTargetSz -= sz } else if isCopyFromDelta(cmd) { sz := uint(cmd) // cmd is the size itself diff --git a/plumbing/object/commit.go b/plumbing/object/commit.go index 113cb29..98664a1 100644 --- a/plumbing/object/commit.go +++ b/plumbing/object/commit.go @@ -243,16 +243,16 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) { } // Encode transforms a Commit into a plumbing.EncodedObject. -func (b *Commit) Encode(o plumbing.EncodedObject) error { - return b.encode(o, true) +func (c *Commit) Encode(o plumbing.EncodedObject) error { + return c.encode(o, true) } // EncodeWithoutSignature export a Commit into a plumbing.EncodedObject without the signature (correspond to the payload of the PGP signature). -func (b *Commit) EncodeWithoutSignature(o plumbing.EncodedObject) error { - return b.encode(o, false) +func (c *Commit) EncodeWithoutSignature(o plumbing.EncodedObject) error { + return c.encode(o, false) } -func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { +func (c *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { o.SetType(plumbing.CommitObject) w, err := o.Writer() if err != nil { @@ -261,11 +261,11 @@ func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { defer ioutil.CheckClose(w, &err) - if _, err = fmt.Fprintf(w, "tree %s\n", b.TreeHash.String()); err != nil { + if _, err = fmt.Fprintf(w, "tree %s\n", c.TreeHash.String()); err != nil { return err } - for _, parent := range b.ParentHashes { + for _, parent := range c.ParentHashes { if _, err = fmt.Fprintf(w, "parent %s\n", parent.String()); err != nil { return err } @@ -275,7 +275,7 @@ func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { return err } - if err = b.Author.Encode(w); err != nil { + if err = c.Author.Encode(w); err != nil { return err } @@ -283,11 +283,11 @@ func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { return err } - if err = b.Committer.Encode(w); err != nil { + if err = c.Committer.Encode(w); err != nil { return err } - if b.PGPSignature != "" && includeSig { + if c.PGPSignature != "" && includeSig { if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil { return err } @@ -296,14 +296,14 @@ func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { // newline. Use join for this so it's clear that a newline should not be // added after this section, as it will be added when the message is // printed. - signature := strings.TrimSuffix(b.PGPSignature, "\n") + signature := strings.TrimSuffix(c.PGPSignature, "\n") lines := strings.Split(signature, "\n") if _, err = fmt.Fprint(w, strings.Join(lines, "\n ")); err != nil { return err } } - if _, err = fmt.Fprintf(w, "\n\n%s", b.Message); err != nil { + if _, err = fmt.Fprintf(w, "\n\n%s", c.Message); err != nil { return err } diff --git a/plumbing/object/commit_stats_test.go b/plumbing/object/commit_stats_test.go index bce2953..4078ce8 100644 --- a/plumbing/object/commit_stats_test.go +++ b/plumbing/object/commit_stats_test.go @@ -11,8 +11,9 @@ import ( "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-billy/v5/util" + + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type CommitStatsSuite struct { diff --git a/plumbing/object/commit_walker_bfs_filtered.go b/plumbing/object/commit_walker_bfs_filtered.go index e87c3db..9d51813 100644 --- a/plumbing/object/commit_walker_bfs_filtered.go +++ b/plumbing/object/commit_walker_bfs_filtered.go @@ -173,4 +173,3 @@ func (w *filterCommitIter) addToQueue( return nil } - diff --git a/plumbing/object/commit_walker_path.go b/plumbing/object/commit_walker_path.go index af6f745..aa0ca15 100644 --- a/plumbing/object/commit_walker_path.go +++ b/plumbing/object/commit_walker_path.go @@ -4,7 +4,6 @@ import ( "io" "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/storer" ) @@ -29,7 +28,7 @@ func NewCommitPathIterFromIter(pathFilter func(string) bool, commitIter CommitIt return iterator } -// this function is kept for compatibilty, can be replaced with NewCommitPathIterFromIter +// NewCommitFileIterFromIter is kept for compatibility, can be replaced with NewCommitPathIterFromIter func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, checkParent bool) CommitIter { return NewCommitPathIterFromIter( func(path string) bool { diff --git a/plumbing/object/commitgraph/commitnode_test.go b/plumbing/object/commitgraph/commitnode_test.go index 38d3bce..6c9a643 100644 --- a/plumbing/object/commitgraph/commitnode_test.go +++ b/plumbing/object/commitgraph/commitnode_test.go @@ -4,13 +4,14 @@ import ( "path"
"testing"
- . "gopkg.in/check.v1"
- fixtures "github.com/go-git/go-git-fixtures/v4"
"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/format/commitgraph"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
"github.com/go-git/go-git/v5/storage/filesystem"
+
+ fixtures "github.com/go-git/go-git-fixtures/v4"
+ . "gopkg.in/check.v1"
)
func Test(t *testing.T) { TestingT(t) }
diff --git a/plumbing/object/commitgraph/commitnode_walker_ctime.go b/plumbing/object/commitgraph/commitnode_walker_ctime.go index f2ed663..281f10b 100644 --- a/plumbing/object/commitgraph/commitnode_walker_ctime.go +++ b/plumbing/object/commitgraph/commitnode_walker_ctime.go @@ -3,10 +3,10 @@ package commitgraph import (
"io"
- "github.com/emirpasic/gods/trees/binaryheap"
-
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
+
+ "github.com/emirpasic/gods/trees/binaryheap"
)
type commitNodeIteratorByCTime struct {
diff --git a/plumbing/object/file_test.go b/plumbing/object/file_test.go index 4dfd950..ada6654 100644 --- a/plumbing/object/file_test.go +++ b/plumbing/object/file_test.go @@ -9,8 +9,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/storage/filesystem" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type FileSuite struct { diff --git a/plumbing/object/patch.go b/plumbing/object/patch.go index 1135a40..9b5f438 100644 --- a/plumbing/object/patch.go +++ b/plumbing/object/patch.go @@ -121,12 +121,12 @@ type Patch struct { filePatches []fdiff.FilePatch } -func (t *Patch) FilePatches() []fdiff.FilePatch { - return t.filePatches +func (p *Patch) FilePatches() []fdiff.FilePatch { + return p.filePatches } -func (t *Patch) Message() string { - return t.message +func (p *Patch) Message() string { + return p.message } func (p *Patch) Encode(w io.Writer) error { @@ -198,12 +198,12 @@ func (tf *textFilePatch) Files() (from fdiff.File, to fdiff.File) { return } -func (t *textFilePatch) IsBinary() bool { - return len(t.chunks) == 0 +func (tf *textFilePatch) IsBinary() bool { + return len(tf.chunks) == 0 } -func (t *textFilePatch) Chunks() []fdiff.Chunk { - return t.chunks +func (tf *textFilePatch) Chunks() []fdiff.Chunk { + return tf.chunks } // textChunk is an implementation of fdiff.Chunk interface diff --git a/plumbing/object/patch_test.go b/plumbing/object/patch_test.go index d4b6cd6..2cff795 100644 --- a/plumbing/object/patch_test.go +++ b/plumbing/object/patch_test.go @@ -1,11 +1,12 @@ package object import ( - . "gopkg.in/check.v1" - fixtures "github.com/go-git/go-git-fixtures/v4" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/cache" "github.com/go-git/go-git/v5/storage/filesystem" + + fixtures "github.com/go-git/go-git-fixtures/v4" + . "gopkg.in/check.v1" ) type PatchSuite struct { diff --git a/plumbing/object/rename.go b/plumbing/object/rename.go index 35af1d6..7fed72c 100644 --- a/plumbing/object/rename.go +++ b/plumbing/object/rename.go @@ -536,7 +536,7 @@ var errIndexFull = errors.New("index is full") // 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 +// (closer to 2KiB), but may grow as more distinct blocks within 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 { @@ -709,7 +709,7 @@ func (i *similarityIndex) common(dst *similarityIndex) uint64 { } func (i *similarityIndex) add(key int, cnt uint64) error { - key = int(uint32(key)*0x9e370001 >> 1) + key = int(uint32(key) * 0x9e370001 >> 1) j := i.slot(key) for { @@ -769,7 +769,7 @@ 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)) + return int(uint32(key) >> uint(31-i.hashBits)) } func shouldGrowAt(hashBits int) int { diff --git a/plumbing/protocol/packp/ulreq.go b/plumbing/protocol/packp/ulreq.go index 44db8e4..ddec06e 100644 --- a/plumbing/protocol/packp/ulreq.go +++ b/plumbing/protocol/packp/ulreq.go @@ -109,42 +109,42 @@ func NewUploadRequestFromCapabilities(adv *capability.List) *UploadRequest { // - is a DepthReference is given capability.DeepenNot MUST be present // - MUST contain only maximum of one of capability.Sideband and capability.Sideband64k // - MUST contain only maximum of one of capability.MultiACK and capability.MultiACKDetailed -func (r *UploadRequest) Validate() error { - if len(r.Wants) == 0 { +func (req *UploadRequest) Validate() error { + if len(req.Wants) == 0 { return fmt.Errorf("want can't be empty") } - if err := r.validateRequiredCapabilities(); err != nil { + if err := req.validateRequiredCapabilities(); err != nil { return err } - if err := r.validateConflictCapabilities(); err != nil { + if err := req.validateConflictCapabilities(); err != nil { return err } return nil } -func (r *UploadRequest) validateRequiredCapabilities() error { +func (req *UploadRequest) validateRequiredCapabilities() error { msg := "missing capability %s" - if len(r.Shallows) != 0 && !r.Capabilities.Supports(capability.Shallow) { + if len(req.Shallows) != 0 && !req.Capabilities.Supports(capability.Shallow) { return fmt.Errorf(msg, capability.Shallow) } - switch r.Depth.(type) { + switch req.Depth.(type) { case DepthCommits: - if r.Depth != DepthCommits(0) { - if !r.Capabilities.Supports(capability.Shallow) { + if req.Depth != DepthCommits(0) { + if !req.Capabilities.Supports(capability.Shallow) { return fmt.Errorf(msg, capability.Shallow) } } case DepthSince: - if !r.Capabilities.Supports(capability.DeepenSince) { + if !req.Capabilities.Supports(capability.DeepenSince) { return fmt.Errorf(msg, capability.DeepenSince) } case DepthReference: - if !r.Capabilities.Supports(capability.DeepenNot) { + if !req.Capabilities.Supports(capability.DeepenNot) { return fmt.Errorf(msg, capability.DeepenNot) } } @@ -152,15 +152,15 @@ func (r *UploadRequest) validateRequiredCapabilities() error { return nil } -func (r *UploadRequest) validateConflictCapabilities() error { +func (req *UploadRequest) validateConflictCapabilities() error { msg := "capabilities %s and %s are mutually exclusive" - if r.Capabilities.Supports(capability.Sideband) && - r.Capabilities.Supports(capability.Sideband64k) { + if req.Capabilities.Supports(capability.Sideband) && + req.Capabilities.Supports(capability.Sideband64k) { return fmt.Errorf(msg, capability.Sideband, capability.Sideband64k) } - if r.Capabilities.Supports(capability.MultiACK) && - r.Capabilities.Supports(capability.MultiACKDetailed) { + if req.Capabilities.Supports(capability.MultiACK) && + req.Capabilities.Supports(capability.MultiACKDetailed) { return fmt.Errorf(msg, capability.MultiACK, capability.MultiACKDetailed) } diff --git a/plumbing/protocol/packp/ulreq_decode.go b/plumbing/protocol/packp/ulreq_decode.go index 449b729..895a3bf 100644 --- a/plumbing/protocol/packp/ulreq_decode.go +++ b/plumbing/protocol/packp/ulreq_decode.go @@ -14,9 +14,9 @@ import ( // Decode reads the next upload-request form its input and // stores it in the UploadRequest. -func (u *UploadRequest) Decode(r io.Reader) error { +func (req *UploadRequest) Decode(r io.Reader) error { d := newUlReqDecoder(r) - return d.Decode(u) + return d.Decode(req) } type ulReqDecoder struct { diff --git a/plumbing/protocol/packp/ulreq_encode.go b/plumbing/protocol/packp/ulreq_encode.go index 4863076..c451e23 100644 --- a/plumbing/protocol/packp/ulreq_encode.go +++ b/plumbing/protocol/packp/ulreq_encode.go @@ -15,9 +15,9 @@ import ( // All the payloads will end with a newline character. Wants and // shallows are sorted alphabetically. A depth of 0 means no depth // request is sent. -func (u *UploadRequest) Encode(w io.Writer) error { +func (req *UploadRequest) Encode(w io.Writer) error { e := newUlReqEncoder(w) - return e.Encode(u) + return e.Encode(req) } type ulReqEncoder struct { diff --git a/plumbing/protocol/packp/updreq.go b/plumbing/protocol/packp/updreq.go index b63b023..4d927d8 100644 --- a/plumbing/protocol/packp/updreq.go +++ b/plumbing/protocol/packp/updreq.go @@ -68,12 +68,12 @@ func NewReferenceUpdateRequestFromCapabilities(adv *capability.List) *ReferenceU return r } -func (r *ReferenceUpdateRequest) validate() error { - if len(r.Commands) == 0 { +func (req *ReferenceUpdateRequest) validate() error { + if len(req.Commands) == 0 { return ErrEmptyCommands } - for _, c := range r.Commands { + for _, c := range req.Commands { if err := c.validate(); err != nil { return err } diff --git a/plumbing/protocol/packp/updreq_encode.go b/plumbing/protocol/packp/updreq_encode.go index 6a79653..2545e93 100644 --- a/plumbing/protocol/packp/updreq_encode.go +++ b/plumbing/protocol/packp/updreq_encode.go @@ -14,33 +14,33 @@ var ( ) // Encode writes the ReferenceUpdateRequest encoding to the stream. -func (r *ReferenceUpdateRequest) Encode(w io.Writer) error { - if err := r.validate(); err != nil { +func (req *ReferenceUpdateRequest) Encode(w io.Writer) error { + if err := req.validate(); err != nil { return err } e := pktline.NewEncoder(w) - if err := r.encodeShallow(e, r.Shallow); err != nil { + if err := req.encodeShallow(e, req.Shallow); err != nil { return err } - if err := r.encodeCommands(e, r.Commands, r.Capabilities); err != nil { + if err := req.encodeCommands(e, req.Commands, req.Capabilities); err != nil { return err } - if r.Packfile != nil { - if _, err := io.Copy(w, r.Packfile); err != nil { + if req.Packfile != nil { + if _, err := io.Copy(w, req.Packfile); err != nil { return err } - return r.Packfile.Close() + return req.Packfile.Close() } return nil } -func (r *ReferenceUpdateRequest) encodeShallow(e *pktline.Encoder, +func (req *ReferenceUpdateRequest) encodeShallow(e *pktline.Encoder, h *plumbing.Hash) error { if h == nil { @@ -51,7 +51,7 @@ func (r *ReferenceUpdateRequest) encodeShallow(e *pktline.Encoder, return e.Encodef("%s%s", shallow, objId) } -func (r *ReferenceUpdateRequest) encodeCommands(e *pktline.Encoder, +func (req *ReferenceUpdateRequest) encodeCommands(e *pktline.Encoder, cmds []*Command, cap *capability.List) error { if err := e.Encodef("%s\x00%s", diff --git a/plumbing/protocol/packp/uppackresp_test.go b/plumbing/protocol/packp/uppackresp_test.go index 8950fa9..260dc57 100644 --- a/plumbing/protocol/packp/uppackresp_test.go +++ b/plumbing/protocol/packp/uppackresp_test.go @@ -4,10 +4,10 @@ import ( "bytes" "io/ioutil" + "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability" . "gopkg.in/check.v1" - "github.com/go-git/go-git/v5/plumbing" ) type UploadPackResponseSuite struct{} diff --git a/plumbing/storer/object_test.go b/plumbing/storer/object_test.go index 8dc3623..30424ff 100644 --- a/plumbing/storer/object_test.go +++ b/plumbing/storer/object_test.go @@ -4,8 +4,9 @@ import ( "fmt" "testing" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" + + . "gopkg.in/check.v1" ) func Test(t *testing.T) { TestingT(t) } diff --git a/plumbing/storer/reference_test.go b/plumbing/storer/reference_test.go index 0660043..7a4d8b4 100644 --- a/plumbing/storer/reference_test.go +++ b/plumbing/storer/reference_test.go @@ -4,8 +4,9 @@ import ( "errors" "io" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" + + . "gopkg.in/check.v1" ) type ReferenceSuite struct{} diff --git a/plumbing/transport/file/receive_pack_test.go b/plumbing/transport/file/receive_pack_test.go index 2ee4b86..686bdcc 100644 --- a/plumbing/transport/file/receive_pack_test.go +++ b/plumbing/transport/file/receive_pack_test.go @@ -5,8 +5,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/test" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type ReceivePackSuite struct { diff --git a/plumbing/transport/git/common_test.go b/plumbing/transport/git/common_test.go index 551b50d..3391aaf 100644 --- a/plumbing/transport/git/common_test.go +++ b/plumbing/transport/git/common_test.go @@ -13,8 +13,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) func Test(t *testing.T) { TestingT(t) } diff --git a/plumbing/transport/git/receive_pack_test.go b/plumbing/transport/git/receive_pack_test.go index 1f730a4..b661d71 100644 --- a/plumbing/transport/git/receive_pack_test.go +++ b/plumbing/transport/git/receive_pack_test.go @@ -3,8 +3,8 @@ package git import ( "github.com/go-git/go-git/v5/plumbing/transport/test" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type ReceivePackSuite struct { diff --git a/plumbing/transport/git/upload_pack_test.go b/plumbing/transport/git/upload_pack_test.go index bbfdf58..5200953 100644 --- a/plumbing/transport/git/upload_pack_test.go +++ b/plumbing/transport/git/upload_pack_test.go @@ -3,8 +3,8 @@ package git import ( "github.com/go-git/go-git/v5/plumbing/transport/test" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type UploadPackSuite struct { diff --git a/plumbing/transport/http/common_test.go b/plumbing/transport/http/common_test.go index 5811139..4122e62 100644 --- a/plumbing/transport/http/common_test.go +++ b/plumbing/transport/http/common_test.go @@ -17,8 +17,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) func Test(t *testing.T) { TestingT(t) } diff --git a/plumbing/transport/http/receive_pack_test.go b/plumbing/transport/http/receive_pack_test.go index b977908..7e70986 100644 --- a/plumbing/transport/http/receive_pack_test.go +++ b/plumbing/transport/http/receive_pack_test.go @@ -3,8 +3,8 @@ package http import ( "github.com/go-git/go-git/v5/plumbing/transport/test" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type ReceivePackSuite struct { diff --git a/plumbing/transport/http/upload_pack_test.go b/plumbing/transport/http/upload_pack_test.go index b34441d..6fae443 100644 --- a/plumbing/transport/http/upload_pack_test.go +++ b/plumbing/transport/http/upload_pack_test.go @@ -11,8 +11,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/test" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type UploadPackSuite struct { diff --git a/plumbing/transport/server/receive_pack_test.go b/plumbing/transport/server/receive_pack_test.go index 2c5b0ae..6c704bd 100644 --- a/plumbing/transport/server/receive_pack_test.go +++ b/plumbing/transport/server/receive_pack_test.go @@ -60,4 +60,5 @@ func (s *ReceivePackSuite) TestReceivePackWithNilPackfile(c *C) { report, err := r.ReceivePack(context.Background(), req) c.Assert(report, IsNil, comment) + c.Assert(err, NotNil, comment) } diff --git a/plumbing/transport/ssh/common_test.go b/plumbing/transport/ssh/common_test.go index 22a8243..87c1148 100644 --- a/plumbing/transport/ssh/common_test.go +++ b/plumbing/transport/ssh/common_test.go @@ -3,12 +3,11 @@ package ssh import ( "testing" - "github.com/kevinburke/ssh_config" + "github.com/go-git/go-git/v5/plumbing/transport" + "github.com/kevinburke/ssh_config" "golang.org/x/crypto/ssh" - . "gopkg.in/check.v1" - "github.com/go-git/go-git/v5/plumbing/transport" ) func Test(t *testing.T) { TestingT(t) } diff --git a/plumbing/transport/test/upload_pack.go b/plumbing/transport/test/upload_pack.go index ee7b067..3ee029d 100644 --- a/plumbing/transport/test/upload_pack.go +++ b/plumbing/transport/test/upload_pack.go @@ -13,11 +13,11 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/format/packfile" "github.com/go-git/go-git/v5/plumbing/protocol/packp" + "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/storage/memory" . "gopkg.in/check.v1" - "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability" ) type UploadPackSuite struct { diff --git a/prune_test.go b/prune_test.go index bd5168d..8c726d0 100644 --- a/prune_test.go +++ b/prune_test.go @@ -9,8 +9,8 @@ import ( "github.com/go-git/go-git/v5/storage" "github.com/go-git/go-git/v5/storage/filesystem" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type PruneSuite struct { diff --git a/references_test.go b/references_test.go index 7c26ce2..28d1bb9 100644 --- a/references_test.go +++ b/references_test.go @@ -8,8 +8,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/storage/memory" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type ReferencesSuite struct { diff --git a/repository.go b/repository.go index 47318d1..1f6de76 100644 --- a/repository.go +++ b/repository.go @@ -13,6 +13,8 @@ import ( "strings" "time" + "github.com/go-git/go-git/v5/storage/filesystem/dotgit" + "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" @@ -47,6 +49,7 @@ var ( ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch") ErrRepositoryNotExists = errors.New("repository does not exist") + ErrRepositoryIncomplete = errors.New("repository's commondir path does not exist") ErrRepositoryAlreadyExists = errors.New("repository already exists") ErrRemoteNotFound = errors.New("remote not found") ErrRemoteExists = errors.New("remote already exists") @@ -253,7 +256,19 @@ func PlainOpenWithOptions(path string, o *PlainOpenOptions) (*Repository, error) return nil, err } - s := filesystem.NewStorage(dot, cache.NewObjectLRUDefault()) + var repositoryFs billy.Filesystem + + if o.EnableDotGitCommonDir { + dotGitCommon, err := dotGitCommonDirectory(dot) + if err != nil { + return nil, err + } + repositoryFs = dotgit.NewRepositoryFilesystem(dot, dotGitCommon) + } else { + repositoryFs = dot + } + + s := filesystem.NewStorage(repositoryFs, cache.NewObjectLRUDefault()) return Open(s, wt) } @@ -328,6 +343,38 @@ func dotGitFileToOSFilesystem(path string, fs billy.Filesystem) (bfs billy.Files return osfs.New(fs.Join(path, gitdir)), nil } +func dotGitCommonDirectory(fs billy.Filesystem) (commonDir billy.Filesystem, err error) { + f, err := fs.Open("commondir") + if os.IsNotExist(err) { + return nil, nil + } + if err != nil { + return nil, err + } + + b, err := stdioutil.ReadAll(f) + if err != nil { + return nil, err + } + if len(b) > 0 { + path := strings.TrimSpace(string(b)) + if filepath.IsAbs(path) { + commonDir = osfs.New(path) + } else { + commonDir = osfs.New(filepath.Join(fs.Root(), path)) + } + if _, err := commonDir.Stat(""); err != nil { + if os.IsNotExist(err) { + return nil, ErrRepositoryIncomplete + } + + return nil, err + } + } + + return commonDir, nil +} + // PlainClone a repository into the path with the given options, isBare defines // if the new repository will be bare or normal. If the path is not empty // ErrRepositoryAlreadyExists is returned. diff --git a/repository_test.go b/repository_test.go index 37cd914..bbed604 100644 --- a/repository_test.go +++ b/repository_test.go @@ -1875,6 +1875,7 @@ func (s *RepositorySuite) TestConfigScoped(c *C) { err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) + c.Assert(err, IsNil) cfg, err := r.ConfigScoped(config.LocalScope) c.Assert(err, IsNil) @@ -2103,15 +2104,7 @@ func (s *RepositorySuite) TestCreateTagAnnotatedBadOpts(c *C) { expectedHash := h.Hash() - ref, err := r.CreateTag("foobar", expectedHash, &CreateTagOptions{ - Message: "foo bar baz qux", - }) - c.Assert(ref, IsNil) - c.Assert(err, Equals, ErrMissingTagger) - - ref, err = r.CreateTag("foobar", expectedHash, &CreateTagOptions{ - Tagger: defaultSignature(), - }) + ref, err := r.CreateTag("foobar", expectedHash, &CreateTagOptions{}) c.Assert(ref, IsNil) c.Assert(err, Equals, ErrMissingMessage) } diff --git a/storage/filesystem/config_test.go b/storage/filesystem/config_test.go index fe84698..c092d14 100644 --- a/storage/filesystem/config_test.go +++ b/storage/filesystem/config_test.go @@ -4,12 +4,12 @@ import ( "io/ioutil" "os" + "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/storage/filesystem/dotgit" - "github.com/go-git/go-billy/v5/osfs" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type ConfigSuite struct { diff --git a/storage/filesystem/dotgit/dotgit.go b/storage/filesystem/dotgit/dotgit.go index 83c7683..3840ea7 100644 --- a/storage/filesystem/dotgit/dotgit.go +++ b/storage/filesystem/dotgit/dotgit.go @@ -30,6 +30,12 @@ const ( objectsPath = "objects" packPath = "pack" refsPath = "refs" + branchesPath = "branches" + hooksPath = "hooks" + infoPath = "info" + remotesPath = "remotes" + logsPath = "logs" + worktreesPath = "worktrees" tmpPackedRefsPrefix = "._packed-refs" diff --git a/storage/filesystem/dotgit/repository_filesystem.go b/storage/filesystem/dotgit/repository_filesystem.go new file mode 100644 index 0000000..8d243ef --- /dev/null +++ b/storage/filesystem/dotgit/repository_filesystem.go @@ -0,0 +1,111 @@ +package dotgit + +import ( + "os" + "path/filepath" + "strings" + + "github.com/go-git/go-billy/v5" +) + +// RepositoryFilesystem is a billy.Filesystem compatible object wrapper +// which handles dot-git filesystem operations and supports commondir according to git scm layout: +// https://github.com/git/git/blob/master/Documentation/gitrepository-layout.txt +type RepositoryFilesystem struct { + dotGitFs billy.Filesystem + commonDotGitFs billy.Filesystem +} + +func NewRepositoryFilesystem(dotGitFs, commonDotGitFs billy.Filesystem) *RepositoryFilesystem { + return &RepositoryFilesystem{ + dotGitFs: dotGitFs, + commonDotGitFs: commonDotGitFs, + } +} + +func (fs *RepositoryFilesystem) mapToRepositoryFsByPath(path string) billy.Filesystem { + // Nothing to decide if commondir not defined + if fs.commonDotGitFs == nil { + return fs.dotGitFs + } + + cleanPath := filepath.Clean(path) + + // Check exceptions for commondir (https://git-scm.com/docs/gitrepository-layout#Documentation/gitrepository-layout.txt) + switch cleanPath { + case fs.dotGitFs.Join(logsPath, "HEAD"): + return fs.dotGitFs + case fs.dotGitFs.Join(refsPath, "bisect"), fs.dotGitFs.Join(refsPath, "rewritten"), fs.dotGitFs.Join(refsPath, "worktree"): + return fs.dotGitFs + } + + // Determine dot-git root by first path element. + // There are some elements which should always use commondir when commondir defined. + // Usual dot-git root will be used for the rest of files. + switch strings.Split(cleanPath, string(filepath.Separator))[0] { + case objectsPath, refsPath, packedRefsPath, configPath, branchesPath, hooksPath, infoPath, remotesPath, logsPath, shallowPath, worktreesPath: + return fs.commonDotGitFs + default: + return fs.dotGitFs + } +} + +func (fs *RepositoryFilesystem) Create(filename string) (billy.File, error) { + return fs.mapToRepositoryFsByPath(filename).Create(filename) +} + +func (fs *RepositoryFilesystem) Open(filename string) (billy.File, error) { + return fs.mapToRepositoryFsByPath(filename).Open(filename) +} + +func (fs *RepositoryFilesystem) OpenFile(filename string, flag int, perm os.FileMode) (billy.File, error) { + return fs.mapToRepositoryFsByPath(filename).OpenFile(filename, flag, perm) +} + +func (fs *RepositoryFilesystem) Stat(filename string) (os.FileInfo, error) { + return fs.mapToRepositoryFsByPath(filename).Stat(filename) +} + +func (fs *RepositoryFilesystem) Rename(oldpath, newpath string) error { + return fs.mapToRepositoryFsByPath(oldpath).Rename(oldpath, newpath) +} + +func (fs *RepositoryFilesystem) Remove(filename string) error { + return fs.mapToRepositoryFsByPath(filename).Remove(filename) +} + +func (fs *RepositoryFilesystem) Join(elem ...string) string { + return fs.dotGitFs.Join(elem...) +} + +func (fs *RepositoryFilesystem) TempFile(dir, prefix string) (billy.File, error) { + return fs.mapToRepositoryFsByPath(dir).TempFile(dir, prefix) +} + +func (fs *RepositoryFilesystem) ReadDir(path string) ([]os.FileInfo, error) { + return fs.mapToRepositoryFsByPath(path).ReadDir(path) +} + +func (fs *RepositoryFilesystem) MkdirAll(filename string, perm os.FileMode) error { + return fs.mapToRepositoryFsByPath(filename).MkdirAll(filename, perm) +} + +func (fs *RepositoryFilesystem) Lstat(filename string) (os.FileInfo, error) { + return fs.mapToRepositoryFsByPath(filename).Lstat(filename) +} + +func (fs *RepositoryFilesystem) Symlink(target, link string) error { + return fs.mapToRepositoryFsByPath(target).Symlink(target, link) +} + +func (fs *RepositoryFilesystem) Readlink(link string) (string, error) { + return fs.mapToRepositoryFsByPath(link).Readlink(link) +} + +func (fs *RepositoryFilesystem) Chroot(path string) (billy.Filesystem, error) { + return fs.mapToRepositoryFsByPath(path).Chroot(path) +} + +func (fs *RepositoryFilesystem) Root() string { + return fs.dotGitFs.Root() +} diff --git a/storage/filesystem/dotgit/repository_filesystem_test.go b/storage/filesystem/dotgit/repository_filesystem_test.go new file mode 100644 index 0000000..880ec0d --- /dev/null +++ b/storage/filesystem/dotgit/repository_filesystem_test.go @@ -0,0 +1,124 @@ +package dotgit + +import ( + "io/ioutil" + "log" + "os" + + "github.com/go-git/go-billy/v5/osfs" + + . "gopkg.in/check.v1" +) + +func (s *SuiteDotGit) TestRepositoryFilesystem(c *C) { + dir, err := ioutil.TempDir("", "repository_filesystem") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + fs := osfs.New(dir) + + err = fs.MkdirAll("dotGit", 0777) + c.Assert(err, IsNil) + dotGitFs, err := fs.Chroot("dotGit") + c.Assert(err, IsNil) + + err = fs.MkdirAll("commonDotGit", 0777) + c.Assert(err, IsNil) + commonDotGitFs, err := fs.Chroot("commonDotGit") + c.Assert(err, IsNil) + + repositoryFs := NewRepositoryFilesystem(dotGitFs, commonDotGitFs) + c.Assert(repositoryFs.Root(), Equals, dotGitFs.Root()) + + somedir, err := repositoryFs.Chroot("somedir") + c.Assert(err, IsNil) + c.Assert(somedir.Root(), Equals, repositoryFs.Join(dotGitFs.Root(), "somedir")) + + _, err = repositoryFs.Create("somefile") + c.Assert(err, IsNil) + + _, err = repositoryFs.Stat("somefile") + c.Assert(err, IsNil) + + file, err := repositoryFs.Open("somefile") + c.Assert(err, IsNil) + err = file.Close() + c.Assert(err, IsNil) + + file, err = repositoryFs.OpenFile("somefile", os.O_RDONLY, 0666) + c.Assert(err, IsNil) + err = file.Close() + c.Assert(err, IsNil) + + file, err = repositoryFs.Create("somefile2") + c.Assert(err, IsNil) + err = file.Close() + c.Assert(err, IsNil) + _, err = repositoryFs.Stat("somefile2") + c.Assert(err, IsNil) + err = repositoryFs.Rename("somefile2", "newfile") + c.Assert(err, IsNil) + + tempDir, err := repositoryFs.TempFile("tmp", "myprefix") + c.Assert(err, IsNil) + c.Assert(repositoryFs.Join(repositoryFs.Root(), "tmp", tempDir.Name()), Equals, repositoryFs.Join(dotGitFs.Root(), "tmp", tempDir.Name())) + + err = repositoryFs.Symlink("newfile", "somelink") + c.Assert(err, IsNil) + + _, err = repositoryFs.Lstat("somelink") + c.Assert(err, IsNil) + + link, err := repositoryFs.Readlink("somelink") + c.Assert(err, IsNil) + c.Assert(link, Equals, "newfile") + + err = repositoryFs.Remove("somelink") + c.Assert(err, IsNil) + + _, err = repositoryFs.Stat("somelink") + c.Assert(os.IsNotExist(err), Equals, true) + + dirs := []string{objectsPath, refsPath, packedRefsPath, configPath, branchesPath, hooksPath, infoPath, remotesPath, logsPath, shallowPath, worktreesPath} + for _, dir := range dirs { + err := repositoryFs.MkdirAll(dir, 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat(dir) + c.Assert(err, IsNil) + _, err = dotGitFs.Stat(dir) + c.Assert(os.IsNotExist(err), Equals, true) + } + + exceptionsPaths := []string{repositoryFs.Join(logsPath, "HEAD"), repositoryFs.Join(refsPath, "bisect"), repositoryFs.Join(refsPath, "rewritten"), repositoryFs.Join(refsPath, "worktree")} + for _, path := range exceptionsPaths { + _, err := repositoryFs.Create(path) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat(path) + c.Assert(os.IsNotExist(err), Equals, true) + _, err = dotGitFs.Stat(path) + c.Assert(err, IsNil) + } + + err = repositoryFs.MkdirAll("refs/heads", 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat("refs/heads") + c.Assert(err, IsNil) + _, err = dotGitFs.Stat("refs/heads") + c.Assert(os.IsNotExist(err), Equals, true) + + err = repositoryFs.MkdirAll("objects/pack", 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat("objects/pack") + c.Assert(err, IsNil) + _, err = dotGitFs.Stat("objects/pack") + c.Assert(os.IsNotExist(err), Equals, true) + + err = repositoryFs.MkdirAll("a/b/c", 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat("a/b/c") + c.Assert(os.IsNotExist(err), Equals, true) + _, err = dotGitFs.Stat("a/b/c") + c.Assert(err, IsNil) +} diff --git a/storage/filesystem/dotgit/writers_test.go b/storage/filesystem/dotgit/writers_test.go index 246d310..7147aec 100644 --- a/storage/filesystem/dotgit/writers_test.go +++ b/storage/filesystem/dotgit/writers_test.go @@ -13,8 +13,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/format/packfile" "github.com/go-git/go-billy/v5/osfs" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) func (s *SuiteDotGit) TestNewObjectPack(c *C) { diff --git a/storage/memory/storage.go b/storage/memory/storage.go index fdf8fcf..a8e5669 100644 --- a/storage/memory/storage.go +++ b/storage/memory/storage.go @@ -195,10 +195,10 @@ func (o *ObjectStorage) DeleteOldObjectPackAndIndex(plumbing.Hash, time.Time) er var errNotSupported = fmt.Errorf("Not supported") -func (s *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) { +func (o *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) { return time.Time{}, errNotSupported } -func (s *ObjectStorage) DeleteLooseObject(plumbing.Hash) error { +func (o *ObjectStorage) DeleteLooseObject(plumbing.Hash) error { return errNotSupported } diff --git a/storage/transactional/config_test.go b/storage/transactional/config_test.go index ec7ae89..1f3a572 100644 --- a/storage/transactional/config_test.go +++ b/storage/transactional/config_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ConfigSuite{}) diff --git a/storage/transactional/index_test.go b/storage/transactional/index_test.go index 88fa1f5..0028c0e 100644 --- a/storage/transactional/index_test.go +++ b/storage/transactional/index_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing/format/index" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&IndexSuite{}) diff --git a/storage/transactional/object_test.go b/storage/transactional/object_test.go index e634409..df277c4 100644 --- a/storage/transactional/object_test.go +++ b/storage/transactional/object_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ObjectSuite{}) diff --git a/storage/transactional/reference_test.go b/storage/transactional/reference_test.go index a6bd1ce..05a4fcf 100644 --- a/storage/transactional/reference_test.go +++ b/storage/transactional/reference_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ReferenceSuite{}) diff --git a/storage/transactional/shallow_test.go b/storage/transactional/shallow_test.go index 1209fe6..15d423c 100644 --- a/storage/transactional/shallow_test.go +++ b/storage/transactional/shallow_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ShallowSuite{}) diff --git a/submodule_test.go b/submodule_test.go index 3d81965..418b3ee 100644 --- a/submodule_test.go +++ b/submodule_test.go @@ -9,8 +9,8 @@ import ( "github.com/go-git/go-git/v5/plumbing" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type SubmoduleSuite struct { diff --git a/utils/binary/read_test.go b/utils/binary/read_test.go index 3749258..bcd9dee 100644 --- a/utils/binary/read_test.go +++ b/utils/binary/read_test.go @@ -6,8 +6,9 @@ import ( "encoding/binary" "testing" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" + + . "gopkg.in/check.v1" ) func Test(t *testing.T) { TestingT(t) } diff --git a/utils/diff/diff.go b/utils/diff/diff.go index 6142ed0..7005494 100644 --- a/utils/diff/diff.go +++ b/utils/diff/diff.go @@ -29,7 +29,7 @@ func Do(src, dst string) (diffs []diffmatchpatch.Diff) { // a bulk delete+insert and the half-baked suboptimal result is returned at once. // The underlying algorithm is Meyers, its complexity is O(N*d) where N is // min(lines(src), lines(dst)) and d is the size of the diff. -func DoWithTimeout (src, dst string, timeout time.Duration) (diffs []diffmatchpatch.Diff) { +func DoWithTimeout(src, dst string, timeout time.Duration) (diffs []diffmatchpatch.Diff) { dmp := diffmatchpatch.New() dmp.DiffTimeout = timeout wSrc, wDst, warray := dmp.DiffLinesToRunes(src, dst) diff --git a/utils/merkletrie/filesystem/node.go b/utils/merkletrie/filesystem/node.go index 165bd42..2fc3d7a 100644 --- a/utils/merkletrie/filesystem/node.go +++ b/utils/merkletrie/filesystem/node.go @@ -91,8 +91,7 @@ func (n *node) calculateChildren() error { if os.IsNotExist(err) { return nil } - - return nil + return err } for _, file := range files { diff --git a/utils/merkletrie/filesystem/node_test.go b/utils/merkletrie/filesystem/node_test.go index 0f6ebe0..159e63d 100644 --- a/utils/merkletrie/filesystem/node_test.go +++ b/utils/merkletrie/filesystem/node_test.go @@ -7,12 +7,13 @@ import ( "path" "testing" - "github.com/go-git/go-billy/v5" - "github.com/go-git/go-billy/v5/memfs" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/utils/merkletrie" "github.com/go-git/go-git/v5/utils/merkletrie/noder" + + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/memfs" + . "gopkg.in/check.v1" ) func Test(t *testing.T) { TestingT(t) } diff --git a/utils/merkletrie/index/node_test.go b/utils/merkletrie/index/node_test.go index 4fa6c63..cc5600d 100644 --- a/utils/merkletrie/index/node_test.go +++ b/utils/merkletrie/index/node_test.go @@ -5,11 +5,12 @@ import ( "path/filepath" "testing" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/format/index" "github.com/go-git/go-git/v5/utils/merkletrie" "github.com/go-git/go-git/v5/utils/merkletrie/noder" + + . "gopkg.in/check.v1" ) func Test(t *testing.T) { TestingT(t) } diff --git a/worktree.go b/worktree.go index 7f394d4..62ad03b 100644 --- a/worktree.go +++ b/worktree.go @@ -93,7 +93,12 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error { head, err := w.r.Head() if err == nil { - if !updated && head.Hash() == ref.Hash() { + headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash()) + if err != nil { + return err + } + + if !updated && headAheadOfRef { return NoErrAlreadyUpToDate } diff --git a/worktree_commit.go b/worktree_commit.go index 63eb2e8..167f2e0 100644 --- a/worktree_commit.go +++ b/worktree_commit.go @@ -6,7 +6,6 @@ import ( "sort" "strings" - "golang.org/x/crypto/openpgp" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/format/index" @@ -14,6 +13,7 @@ import ( "github.com/go-git/go-git/v5/storage" "github.com/go-git/go-billy/v5" + "golang.org/x/crypto/openpgp" ) // Commit stores the current contents of the index in a new commit along with diff --git a/worktree_status.go b/worktree_status.go index 1542f5e..c639f13 100644 --- a/worktree_status.go +++ b/worktree_status.go @@ -7,6 +7,7 @@ import ( "os" "path" "path/filepath" + "strings" "github.com/go-git/go-billy/v5/util" "github.com/go-git/go-git/v5/plumbing" @@ -264,43 +265,23 @@ func diffTreeIsEquals(a, b noder.Hasher) bool { // the worktree to the index. If any of the files is already staged in the index // no error is returned. When path is a file, the blob.Hash is returned. func (w *Worktree) Add(path string) (plumbing.Hash, error) { - // TODO(mcuadros): remove plumbing.Hash from signature at v5. - s, err := w.Status() - if err != nil { - return plumbing.ZeroHash, err - } - - idx, err := w.r.Storer.Index() - if err != nil { - return plumbing.ZeroHash, err - } - - var h plumbing.Hash - var added bool - - fi, err := w.Filesystem.Lstat(path) - if err != nil || !fi.IsDir() { - added, h, err = w.doAddFile(idx, s, path) - } else { - added, err = w.doAddDirectory(idx, s, path) - } - - if err != nil { - return h, err - } - - if !added { - return h, nil - } - - return h, w.r.Storer.SetIndex(idx) + // TODO(mcuadros): deprecate in favor of AddWithOption in v6. + return w.doAdd(path, make([]gitignore.Pattern, 0)) } -func (w *Worktree) doAddDirectory(idx *index.Index, s Status, directory string) (added bool, err error) { +func (w *Worktree) doAddDirectory(idx *index.Index, s Status, directory string, ignorePattern []gitignore.Pattern) (added bool, err error) { files, err := w.Filesystem.ReadDir(directory) if err != nil { return false, err } + if len(ignorePattern) > 0 { + m := gitignore.NewMatcher(ignorePattern) + matchPath := strings.Split(directory, string(os.PathSeparator)) + if m.Match(matchPath, true) { + // ignore + return false, nil + } + } for _, file := range files { name := path.Join(directory, file.Name()) @@ -311,9 +292,9 @@ func (w *Worktree) doAddDirectory(idx *index.Index, s Status, directory string) // ignore special git directory continue } - a, err = w.doAddDirectory(idx, s, name) + a, err = w.doAddDirectory(idx, s, name, ignorePattern) } else { - a, _, err = w.doAddFile(idx, s, name) + a, _, err = w.doAddFile(idx, s, name, ignorePattern) } if err != nil { @@ -328,10 +309,69 @@ func (w *Worktree) doAddDirectory(idx *index.Index, s Status, directory string) return } +// AddWithOptions file contents to the index, updates the index using the +// current content found in the working tree, to prepare the content staged for +// the next commit. +// +// It typically adds the current content of existing paths as a whole, but with +// some options it can also be used to add content with only part of the changes +// made to the working tree files applied, or remove paths that do not exist in +// the working tree anymore. +func (w *Worktree) AddWithOptions(opts *AddOptions) error { + if err := opts.Validate(w.r); err != nil { + return err + } + + if opts.All { + _, err := w.doAdd(".", w.Excludes) + return err + } + + if opts.Glob != "" { + return w.AddGlob(opts.Glob) + } + + _, err := w.Add(opts.Path) + return err +} + +func (w *Worktree) doAdd(path string, ignorePattern []gitignore.Pattern) (plumbing.Hash, error) { + s, err := w.Status() + if err != nil { + return plumbing.ZeroHash, err + } + + idx, err := w.r.Storer.Index() + if err != nil { + return plumbing.ZeroHash, err + } + + var h plumbing.Hash + var added bool + + fi, err := w.Filesystem.Lstat(path) + if err != nil || !fi.IsDir() { + added, h, err = w.doAddFile(idx, s, path, ignorePattern) + } else { + added, err = w.doAddDirectory(idx, s, path, ignorePattern) + } + + if err != nil { + return h, err + } + + if !added { + return h, nil + } + + return h, w.r.Storer.SetIndex(idx) +} + // AddGlob adds all paths, matching pattern, to the index. If pattern matches a // directory path, all directory contents are added to the index recursively. No // error is returned if all matching paths are already staged in index. func (w *Worktree) AddGlob(pattern string) error { + // TODO(mcuadros): deprecate in favor of AddWithOption in v6. files, err := util.Glob(w.Filesystem, pattern) if err != nil { return err @@ -360,9 +400,9 @@ func (w *Worktree) AddGlob(pattern string) error { var added bool if fi.IsDir() { - added, err = w.doAddDirectory(idx, s, file) + added, err = w.doAddDirectory(idx, s, file, make([]gitignore.Pattern, 0)) } else { - added, _, err = w.doAddFile(idx, s, file) + added, _, err = w.doAddFile(idx, s, file, make([]gitignore.Pattern, 0)) } if err != nil { @@ -383,10 +423,18 @@ func (w *Worktree) AddGlob(pattern string) error { // doAddFile create a new blob from path and update the index, added is true if // the file added is different from the index. -func (w *Worktree) doAddFile(idx *index.Index, s Status, path string) (added bool, h plumbing.Hash, err error) { +func (w *Worktree) doAddFile(idx *index.Index, s Status, path string, ignorePattern []gitignore.Pattern) (added bool, h plumbing.Hash, err error) { if s.File(path).Worktree == Unmodified { return false, h, nil } + if len(ignorePattern) > 0 { + m := gitignore.NewMatcher(ignorePattern) + matchPath := strings.Split(path, string(os.PathSeparator)) + if m.Match(matchPath, true) { + // ignore + return false, h, nil + } + } h, err = w.copyFileToStorage(path) if err != nil { diff --git a/worktree_test.go b/worktree_test.go index 24a65eb..59c80af 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -265,6 +265,26 @@ func (s *RepositorySuite) TestPullAdd(c *C) { c.Assert(branch.Hash().String(), Not(Equals), "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") } +func (s *WorktreeSuite) TestPullAlreadyUptodate(c *C) { + path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() + + r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ + URL: filepath.Join(path, ".git"), + }) + + c.Assert(err, IsNil) + + w, err := r.Worktree() + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(path, "bar"), []byte("bar"), 0755) + c.Assert(err, IsNil) + _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()}) + c.Assert(err, IsNil) + + err = w.Pull(&PullOptions{}) + c.Assert(err, Equals, NoErrAlreadyUpToDate) +} + func (s *WorktreeSuite) TestCheckout(c *C) { fs := memfs.New() w := &Worktree{ @@ -1370,6 +1390,52 @@ func (s *WorktreeSuite) TestAddDirectoryErrorNotFound(c *C) { c.Assert(h.IsZero(), Equals, true) } +func (s *WorktreeSuite) TestAddAll(c *C) { + fs := memfs.New() + w := &Worktree{ + r: s.Repository, + Filesystem: fs, + } + + err := w.Checkout(&CheckoutOptions{Force: true}) + c.Assert(err, IsNil) + + idx, err := w.r.Storer.Index() + c.Assert(err, IsNil) + c.Assert(idx.Entries, HasLen, 9) + + err = util.WriteFile(w.Filesystem, "file1", []byte("file1"), 0644) + c.Assert(err, IsNil) + + err = util.WriteFile(w.Filesystem, "file2", []byte("file2"), 0644) + c.Assert(err, IsNil) + + err = util.WriteFile(w.Filesystem, "file3", []byte("ignore me"), 0644) + c.Assert(err, IsNil) + + w.Excludes = make([]gitignore.Pattern, 0) + w.Excludes = append(w.Excludes, gitignore.ParsePattern("file3", nil)) + + err = w.AddWithOptions(&AddOptions{All: true}) + c.Assert(err, IsNil) + + idx, err = w.r.Storer.Index() + c.Assert(err, IsNil) + c.Assert(idx.Entries, HasLen, 11) + + status, err := w.Status() + c.Assert(err, IsNil) + c.Assert(status, HasLen, 2) + + file1 := status.File("file1") + c.Assert(file1.Staging, Equals, Added) + file2 := status.File("file2") + c.Assert(file2.Staging, Equals, Added) + file3 := status.File("file3") + c.Assert(file3.Staging, Equals, Untracked) + c.Assert(file3.Worktree, Equals, Untracked) +} + func (s *WorktreeSuite) TestAddGlob(c *C) { fs := memfs.New() w := &Worktree{ @@ -1391,7 +1457,7 @@ func (s *WorktreeSuite) TestAddGlob(c *C) { err = util.WriteFile(w.Filesystem, "qux/bar/baz", []byte("BAZ"), 0755) c.Assert(err, IsNil) - err = w.AddGlob(w.Filesystem.Join("qux", "b*")) + err = w.AddWithOptions(&AddOptions{Glob: w.Filesystem.Join("qux", "b*")}) c.Assert(err, IsNil) idx, err = w.r.Storer.Index() @@ -2006,3 +2072,78 @@ func (s *WorktreeSuite) TestAddAndCommit(c *C) { }) c.Assert(err, IsNil) } + +func (s *WorktreeSuite) TestLinkedWorktree(c *C) { + fs := fixtures.ByTag("linked-worktree").One().Worktree() + + // Open main repo. + { + fs, err := fs.Chroot("main") + c.Assert(err, IsNil) + repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) + c.Assert(err, IsNil) + + wt, err := repo.Worktree() + c.Assert(err, IsNil) + + status, err := wt.Status() + c.Assert(err, IsNil) + c.Assert(len(status), Equals, 2) // 2 files + + head, err := repo.Head() + c.Assert(err, IsNil) + c.Assert(string(head.Name()), Equals, "refs/heads/master") + } + + // Open linked-worktree #1. + { + fs, err := fs.Chroot("linked-worktree-1") + c.Assert(err, IsNil) + repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) + c.Assert(err, IsNil) + + wt, err := repo.Worktree() + c.Assert(err, IsNil) + + status, err := wt.Status() + c.Assert(err, IsNil) + c.Assert(len(status), Equals, 3) // 3 files + + _, ok := status["linked-worktree-1-unique-file.txt"] + c.Assert(ok, Equals, true) + + head, err := repo.Head() + c.Assert(err, IsNil) + c.Assert(string(head.Name()), Equals, "refs/heads/linked-worktree-1") + } + + // Open linked-worktree #2. + { + fs, err := fs.Chroot("linked-worktree-2") + c.Assert(err, IsNil) + repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) + c.Assert(err, IsNil) + + wt, err := repo.Worktree() + c.Assert(err, IsNil) + + status, err := wt.Status() + c.Assert(err, IsNil) + c.Assert(len(status), Equals, 3) // 3 files + + _, ok := status["linked-worktree-2-unique-file.txt"] + c.Assert(ok, Equals, true) + + head, err := repo.Head() + c.Assert(err, IsNil) + c.Assert(string(head.Name()), Equals, "refs/heads/branch-with-different-name") + } + + // Open linked-worktree #2. + { + fs, err := fs.Chroot("linked-worktree-invalid-commondir") + c.Assert(err, IsNil) + _, err = PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) + c.Assert(err, Equals, ErrRepositoryIncomplete) + } +} |