aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/config_test.go37
-rw-r--r--options.go42
-rw-r--r--options_test.go84
-rw-r--r--repository_test.go10
-rw-r--r--worktree_status.go101
-rw-r--r--worktree_test.go46
6 files changed, 272 insertions, 48 deletions
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) {
diff --git a/options.go b/options.go
index 5367031..d222267 100644
--- a/options.go
+++ b/options.go
@@ -373,6 +373,12 @@ var (
ErrMissingAuthor = errors.New("author field is required")
)
+// AddOptions describes how a add operation should be performed
+type AddOptions struct {
+ All bool
+ Path string
+}
+
// 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 +470,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 +485,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 +500,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.
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/repository_test.go b/repository_test.go
index 37cd914..d1af7b6 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -2103,15 +2103,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/worktree_status.go b/worktree_status.go
index 1542f5e..658ed94 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,22 @@ 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)
+ 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 +291,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,6 +308,47 @@ func (w *Worktree) doAddDirectory(idx *index.Index, s Status, directory string)
return
}
+// add changes from all tracked and untracked files
+func (w *Worktree) AddWithOptions(opts *AddOptions) (plumbing.Hash, error) {
+ if opts.All {
+ return w.doAdd(".", w.Excludes)
+ }
+ return w.Add(opts.Path)
+}
+
+func (w *Worktree) doAdd(path string, ignorePattern []gitignore.Pattern) (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, 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.
@@ -360,9 +381,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 +404,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..2ee830a 100644
--- a/worktree_test.go
+++ b/worktree_test.go
@@ -1370,6 +1370,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{