Skip to content

Commit

Permalink
git: Worktree.AddWithOptions, add skipStatus option. #993
Browse files Browse the repository at this point in the history
  • Loading branch information
moranCohen26 committed Jan 16, 2024
1 parent a6e934f commit a205f3a
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 11 deletions.
3 changes: 3 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,9 @@ type AddOptions struct {
// 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
// SkipStatus adds the path with no status check. This option is relevant only
// when the `Path` option is specified and does not apply when the `All` option is used.
SkipStatus bool
}

// Validate validates the fields and sets the default values.
Expand Down
113 changes: 112 additions & 1 deletion worktree_commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ func (s *WorktreeSuite) TestCommitAmend(c *C) {
_, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
c.Assert(err, IsNil)


amendedHash, err := w.Commit("bar\n", &CommitOptions{Amend: true})
c.Assert(err, IsNil)

Expand All @@ -144,6 +143,118 @@ func (s *WorktreeSuite) TestCommitAmend(c *C) {
assertStorageStatus(c, s.Repository, 13, 11, 11, amendedHash)
}

func (s *WorktreeSuite) TestAddAndCommitWithSkipStatus(c *C) {
expected := plumbing.NewHash("375a3808ffde7f129cdd3c8c252fd0fe37cfd13b")

fs := memfs.New()
w := &Worktree{
r: s.Repository,
Filesystem: fs,
}

err := w.Checkout(&CheckoutOptions{})
c.Assert(err, IsNil)

util.WriteFile(fs, "LICENSE", []byte("foo"), 0644)
util.WriteFile(fs, "foo", []byte("foo"), 0644)

err = w.AddWithOptions(&AddOptions{
Path: "foo",
SkipStatus: true,
})
c.Assert(err, IsNil)

hash, err := w.Commit("commit foo only\n", &CommitOptions{
Author: defaultSignature(),
})

c.Assert(hash, Equals, expected)
c.Assert(err, IsNil)

assertStorageStatus(c, s.Repository, 13, 11, 10, expected)
}

func (s *WorktreeSuite) TestAddAndCommitWithSkipStatusPathNotModified(c *C) {
expected := plumbing.NewHash("375a3808ffde7f129cdd3c8c252fd0fe37cfd13b")
expected2 := plumbing.NewHash("8691273baf8f6ee2cccfc05e910552c04d02d472")

fs := memfs.New()
w := &Worktree{
r: s.Repository,
Filesystem: fs,
}

err := w.Checkout(&CheckoutOptions{})
c.Assert(err, IsNil)

util.WriteFile(fs, "foo", []byte("foo"), 0644)

status, err := w.Status()
c.Assert(err, IsNil)
foo := status.File("foo")
c.Assert(foo.Staging, Equals, Untracked)
c.Assert(foo.Worktree, Equals, Untracked)

err = w.AddWithOptions(&AddOptions{
Path: "foo",
SkipStatus: true,
})
c.Assert(err, IsNil)

status, err = w.Status()
c.Assert(err, IsNil)
foo = status.File("foo")
c.Assert(foo.Staging, Equals, Added)
c.Assert(foo.Worktree, Equals, Unmodified)

hash, err := w.Commit("commit foo only\n", &CommitOptions{All: true,
Author: defaultSignature(),
})
c.Assert(hash, Equals, expected)
c.Assert(err, IsNil)
commit1, err := w.r.CommitObject(hash)

status, err = w.Status()
c.Assert(err, IsNil)
foo = status.File("foo")
c.Assert(foo.Staging, Equals, Untracked)
c.Assert(foo.Worktree, Equals, Untracked)

assertStorageStatus(c, s.Repository, 13, 11, 10, expected)

err = w.AddWithOptions(&AddOptions{
Path: "foo",
SkipStatus: true,
})
c.Assert(err, IsNil)

status, err = w.Status()
c.Assert(err, IsNil)
foo = status.File("foo")
c.Assert(foo.Staging, Equals, Untracked)
c.Assert(foo.Worktree, Equals, Untracked)

hash, err = w.Commit("commit with no changes\n", &CommitOptions{
Author: defaultSignature(),
})
c.Assert(hash, Equals, expected2)
c.Assert(err, IsNil)
commit2, err := w.r.CommitObject(hash)

status, err = w.Status()
c.Assert(err, IsNil)
foo = status.File("foo")
c.Assert(foo.Staging, Equals, Untracked)
c.Assert(foo.Worktree, Equals, Untracked)

patch, err := commit2.Patch(commit1)
c.Assert(err, IsNil)
files := patch.FilePatches()
c.Assert(files, IsNil)

assertStorageStatus(c, s.Repository, 13, 11, 11, expected2)
}

func (s *WorktreeSuite) TestCommitAll(c *C) {
expected := plumbing.NewHash("aede6f8c9c1c7ec9ca8d287c64b8ed151276fa28")

Expand Down
27 changes: 17 additions & 10 deletions worktree_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ func diffTreeIsEquals(a, b noder.Hasher) bool {
// 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): deprecate in favor of AddWithOption in v6.
return w.doAdd(path, make([]gitignore.Pattern, 0))
return w.doAdd(path, make([]gitignore.Pattern, 0), false)
}

func (w *Worktree) doAddDirectory(idx *index.Index, s Status, directory string, ignorePattern []gitignore.Pattern) (added bool, err error) {
Expand Down Expand Up @@ -321,24 +321,19 @@ func (w *Worktree) AddWithOptions(opts *AddOptions) error {
}

if opts.All {
_, err := w.doAdd(".", w.Excludes)
_, err := w.doAdd(".", w.Excludes, false)
return err
}

if opts.Glob != "" {
return w.AddGlob(opts.Glob)
}

_, err := w.Add(opts.Path)
_, err := w.doAdd(opts.Path, make([]gitignore.Pattern, 0), opts.SkipStatus)
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
}

func (w *Worktree) doAdd(path string, ignorePattern []gitignore.Pattern, skipStatus bool) (plumbing.Hash, error) {
idx, err := w.r.Storer.Index()
if err != nil {
return plumbing.ZeroHash, err
Expand All @@ -348,6 +343,17 @@ func (w *Worktree) doAdd(path string, ignorePattern []gitignore.Pattern) (plumbi
var added bool

fi, err := w.Filesystem.Lstat(path)

// status is required for doAddDirectory
var s Status
var err2 error
if !skipStatus || fi == nil || fi.IsDir() {
s, err2 = w.Status()
if err2 != nil {
return plumbing.ZeroHash, err2
}
}

if err != nil || !fi.IsDir() {
added, h, err = w.doAddFile(idx, s, path, ignorePattern)
} else {
Expand Down Expand Up @@ -421,8 +427,9 @@ 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.
// if s status is nil will skip the status check and update the index anyway
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 {
if s != nil && s.File(path).Worktree == Unmodified {
return false, h, nil
}
if len(ignorePattern) > 0 {
Expand Down
108 changes: 108 additions & 0 deletions worktree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1930,6 +1930,114 @@ func (s *WorktreeSuite) TestAddGlobErrorNoMatches(c *C) {
c.Assert(err, Equals, ErrGlobNoMatches)
}

func (s *WorktreeSuite) TestAddSkipStatusAddedPath(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 = w.AddWithOptions(&AddOptions{Path: "file1", SkipStatus: true})
c.Assert(err, IsNil)

idx, err = w.r.Storer.Index()
c.Assert(err, IsNil)
c.Assert(idx.Entries, HasLen, 10)

e, err := idx.Entry("file1")
c.Assert(err, IsNil)
c.Assert(e.Mode, Equals, filemode.Regular)

status, err := w.Status()
c.Assert(err, IsNil)
c.Assert(status, HasLen, 1)

file := status.File("file1")
c.Assert(file.Staging, Equals, Added)
c.Assert(file.Worktree, Equals, Unmodified)
}

func (s *WorktreeSuite) TestAddSkipStatusModifiedPath(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, "LICENSE", []byte("file1"), 0644)
c.Assert(err, IsNil)

err = w.AddWithOptions(&AddOptions{Path: "LICENSE", SkipStatus: true})
c.Assert(err, IsNil)

idx, err = w.r.Storer.Index()
c.Assert(err, IsNil)
c.Assert(idx.Entries, HasLen, 9)

e, err := idx.Entry("LICENSE")
c.Assert(err, IsNil)
c.Assert(e.Mode, Equals, filemode.Regular)

status, err := w.Status()
c.Assert(err, IsNil)
c.Assert(status, HasLen, 1)

file := status.File("LICENSE")
c.Assert(file.Staging, Equals, Modified)
c.Assert(file.Worktree, Equals, Unmodified)
}

func (s *WorktreeSuite) TestAddSkipStatusNonModifiedPath(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 = w.AddWithOptions(&AddOptions{Path: "LICENSE", SkipStatus: true})
c.Assert(err, IsNil)

idx, err = w.r.Storer.Index()
c.Assert(err, IsNil)
c.Assert(idx.Entries, HasLen, 9)

e, err := idx.Entry("LICENSE")
c.Assert(err, IsNil)
c.Assert(e.Mode, Equals, filemode.Regular)

status, err := w.Status()
c.Assert(err, IsNil)
c.Assert(status, HasLen, 0)

file := status.File("LICENSE")
c.Assert(file.Staging, Equals, Untracked)
c.Assert(file.Worktree, Equals, Untracked)
}

func (s *WorktreeSuite) TestRemove(c *C) {
fs := memfs.New()
w := &Worktree{
Expand Down

0 comments on commit a205f3a

Please sign in to comment.