aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--status.go69
-rw-r--r--worktree_status.go53
-rw-r--r--worktree_test.go10
3 files changed, 94 insertions, 38 deletions
diff --git a/status.go b/status.go
index 7f18e02..d14f7e6 100644
--- a/status.go
+++ b/status.go
@@ -4,6 +4,9 @@ import (
"bytes"
"fmt"
"path/filepath"
+
+ mindex "github.com/go-git/go-git/v5/utils/merkletrie/index"
+ "github.com/go-git/go-git/v5/utils/merkletrie/noder"
)
// Status represents the current status of a Worktree.
@@ -77,3 +80,69 @@ const (
Copied StatusCode = 'C'
UpdatedButUnmerged StatusCode = 'U'
)
+
+// StatusStrategy defines the different types of strategies when processing
+// the worktree status.
+type StatusStrategy int
+
+const (
+ // TODO: (V6) Review the default status strategy.
+ // TODO: (V6) Review the type used to represent Status, to enable lazy
+ // processing of statuses going direct to the backing filesystem.
+ defaultStatusStrategy = Empty
+
+ // Empty starts its status map from empty. Missing entries for a given
+ // path means that the file is untracked. This causes a known issue (#119)
+ // whereby unmodified files can be incorrectly reported as untracked.
+ //
+ // This can be used when returning the changed state within a modified Worktree.
+ // For example, to check whether the current worktree is clean.
+ Empty StatusStrategy = 0
+ // Preload goes through all existing nodes from the index and add them to the
+ // status map as unmodified. This is currently the most reliable strategy
+ // although it comes at a performance cost in large repositories.
+ //
+ // This method is recommended when fetching the status of unmodified files.
+ // For example, to confirm the status of a specific file that is either
+ // untracked or unmodified.
+ Preload StatusStrategy = 1
+)
+
+func (s StatusStrategy) new(w *Worktree) (Status, error) {
+ switch s {
+ case Preload:
+ return preloadStatus(w)
+ case Empty:
+ return make(Status), nil
+ }
+ return nil, fmt.Errorf("%w: %+v", ErrUnsupportedStatusStrategy, s)
+}
+
+func preloadStatus(w *Worktree) (Status, error) {
+ idx, err := w.r.Storer.Index()
+ if err != nil {
+ return nil, err
+ }
+
+ idxRoot := mindex.NewRootNode(idx)
+ nodes := []noder.Noder{idxRoot}
+
+ status := make(Status)
+ for len(nodes) > 0 {
+ var node noder.Noder
+ node, nodes = nodes[0], nodes[1:]
+ if node.IsDir() {
+ children, err := node.Children()
+ if err != nil {
+ return nil, err
+ }
+ nodes = append(nodes, children...)
+ continue
+ }
+ fs := status.File(node.Name())
+ fs.Worktree = Unmodified
+ fs.Staging = Unmodified
+ }
+
+ return status, nil
+}
diff --git a/worktree_status.go b/worktree_status.go
index 2f865ce..6a0049c 100644
--- a/worktree_status.go
+++ b/worktree_status.go
@@ -29,10 +29,23 @@ var (
// ErrGlobNoMatches in an AddGlob if the glob pattern does not match any
// files in the worktree.
ErrGlobNoMatches = errors.New("glob pattern did not match any files")
+ // ErrUnsupportedStatusStrategy occurs when an invalid StatusStrategy is used
+ // when processing the Worktree status.
+ ErrUnsupportedStatusStrategy = errors.New("unsupported status strategy")
)
// Status returns the working tree status.
func (w *Worktree) Status() (Status, error) {
+ return w.StatusWithOptions(StatusOptions{Strategy: defaultStatusStrategy})
+}
+
+// StatusOptions defines the options for Worktree.StatusWithOptions().
+type StatusOptions struct {
+ Strategy StatusStrategy
+}
+
+// StatusWithOptions returns the working tree status.
+func (w *Worktree) StatusWithOptions(o StatusOptions) (Status, error) {
var hash plumbing.Hash
ref, err := w.r.Head()
@@ -44,45 +57,11 @@ func (w *Worktree) Status() (Status, error) {
hash = ref.Hash()
}
- return w.status(hash)
-}
-
-func (w *Worktree) newStatusFromIndex() (Status, error) {
- idx, err := w.r.Storer.Index()
- if err != nil {
- return nil, err
- }
-
- idxRoot := mindex.NewRootNode(idx)
- nodes := []noder.Noder{idxRoot}
-
- if err != nil {
- return nil, err
- }
-
- status := make(Status)
-
- for len(nodes) > 0 {
- var node noder.Noder
- node, nodes = nodes[0], nodes[1:]
- if node.IsDir() {
- children, err := node.Children()
- if err != nil {
- return nil, err
- }
- nodes = append(nodes, children...)
- continue
- }
- fs := status.File(node.Name())
- fs.Worktree = Unmodified
- fs.Staging = Unmodified
- }
-
- return status, nil
+ return w.status(o.Strategy, hash)
}
-func (w *Worktree) status(commit plumbing.Hash) (Status, error) {
- s, err := w.newStatusFromIndex()
+func (w *Worktree) status(ss StatusStrategy, commit plumbing.Hash) (Status, error) {
+ s, err := ss.new(w)
if err != nil {
return nil, err
}
diff --git a/worktree_test.go b/worktree_test.go
index deaf5e5..7ecd818 100644
--- a/worktree_test.go
+++ b/worktree_test.go
@@ -1062,13 +1062,21 @@ func (s *WorktreeSuite) TestStatusUnmodified(c *C) {
err := w.Checkout(&CheckoutOptions{Force: true})
c.Assert(err, IsNil)
- status, err := w.Status()
+ status, err := w.StatusWithOptions(StatusOptions{Strategy: Preload})
c.Assert(err, IsNil)
c.Assert(status.IsClean(), Equals, true)
c.Assert(status.IsUntracked("LICENSE"), Equals, false)
c.Assert(status.File("LICENSE").Staging, Equals, Unmodified)
c.Assert(status.File("LICENSE").Worktree, Equals, Unmodified)
+
+ status, err = w.StatusWithOptions(StatusOptions{Strategy: Empty})
+ c.Assert(err, IsNil)
+ c.Assert(status.IsClean(), Equals, true)
+ c.Assert(status.IsUntracked("LICENSE"), Equals, false)
+
+ c.Assert(status.File("LICENSE").Staging, Equals, Untracked)
+ c.Assert(status.File("LICENSE").Worktree, Equals, Untracked)
}
func (s *WorktreeSuite) TestReset(c *C) {