+69
status.go
+69
status.go
···
4
"bytes"
5
"fmt"
6
"path/filepath"
7
+
8
+
mindex "github.com/go-git/go-git/v5/utils/merkletrie/index"
9
+
"github.com/go-git/go-git/v5/utils/merkletrie/noder"
10
)
11
12
// Status represents the current status of a Worktree.
···
80
Copied StatusCode = 'C'
81
UpdatedButUnmerged StatusCode = 'U'
82
)
83
+
84
+
// StatusStrategy defines the different types of strategies when processing
85
+
// the worktree status.
86
+
type StatusStrategy int
87
+
88
+
const (
89
+
// TODO: (V6) Review the default status strategy.
90
+
// TODO: (V6) Review the type used to represent Status, to enable lazy
91
+
// processing of statuses going direct to the backing filesystem.
92
+
defaultStatusStrategy = Empty
93
+
94
+
// Empty starts its status map from empty. Missing entries for a given
95
+
// path means that the file is untracked. This causes a known issue (#119)
96
+
// whereby unmodified files can be incorrectly reported as untracked.
97
+
//
98
+
// This can be used when returning the changed state within a modified Worktree.
99
+
// For example, to check whether the current worktree is clean.
100
+
Empty StatusStrategy = 0
101
+
// Preload goes through all existing nodes from the index and add them to the
102
+
// status map as unmodified. This is currently the most reliable strategy
103
+
// although it comes at a performance cost in large repositories.
104
+
//
105
+
// This method is recommended when fetching the status of unmodified files.
106
+
// For example, to confirm the status of a specific file that is either
107
+
// untracked or unmodified.
108
+
Preload StatusStrategy = 1
109
+
)
110
+
111
+
func (s StatusStrategy) new(w *Worktree) (Status, error) {
112
+
switch s {
113
+
case Preload:
114
+
return preloadStatus(w)
115
+
case Empty:
116
+
return make(Status), nil
117
+
}
118
+
return nil, fmt.Errorf("%w: %+v", ErrUnsupportedStatusStrategy, s)
119
+
}
120
+
121
+
func preloadStatus(w *Worktree) (Status, error) {
122
+
idx, err := w.r.Storer.Index()
123
+
if err != nil {
124
+
return nil, err
125
+
}
126
+
127
+
idxRoot := mindex.NewRootNode(idx)
128
+
nodes := []noder.Noder{idxRoot}
129
+
130
+
status := make(Status)
131
+
for len(nodes) > 0 {
132
+
var node noder.Noder
133
+
node, nodes = nodes[0], nodes[1:]
134
+
if node.IsDir() {
135
+
children, err := node.Children()
136
+
if err != nil {
137
+
return nil, err
138
+
}
139
+
nodes = append(nodes, children...)
140
+
continue
141
+
}
142
+
fs := status.File(node.Name())
143
+
fs.Worktree = Unmodified
144
+
fs.Staging = Unmodified
145
+
}
146
+
147
+
return status, nil
148
+
}
+19
-3
worktree_status.go
+19
-3
worktree_status.go
···
29
// ErrGlobNoMatches in an AddGlob if the glob pattern does not match any
30
// files in the worktree.
31
ErrGlobNoMatches = errors.New("glob pattern did not match any files")
32
)
33
34
// Status returns the working tree status.
35
func (w *Worktree) Status() (Status, error) {
36
var hash plumbing.Hash
37
38
ref, err := w.r.Head()
···
44
hash = ref.Hash()
45
}
46
47
-
return w.status(hash)
48
}
49
50
-
func (w *Worktree) status(commit plumbing.Hash) (Status, error) {
51
-
s := make(Status)
52
53
left, err := w.diffCommitWithStaging(commit, false)
54
if err != nil {
···
29
// ErrGlobNoMatches in an AddGlob if the glob pattern does not match any
30
// files in the worktree.
31
ErrGlobNoMatches = errors.New("glob pattern did not match any files")
32
+
// ErrUnsupportedStatusStrategy occurs when an invalid StatusStrategy is used
33
+
// when processing the Worktree status.
34
+
ErrUnsupportedStatusStrategy = errors.New("unsupported status strategy")
35
)
36
37
// Status returns the working tree status.
38
func (w *Worktree) Status() (Status, error) {
39
+
return w.StatusWithOptions(StatusOptions{Strategy: defaultStatusStrategy})
40
+
}
41
+
42
+
// StatusOptions defines the options for Worktree.StatusWithOptions().
43
+
type StatusOptions struct {
44
+
Strategy StatusStrategy
45
+
}
46
+
47
+
// StatusWithOptions returns the working tree status.
48
+
func (w *Worktree) StatusWithOptions(o StatusOptions) (Status, error) {
49
var hash plumbing.Hash
50
51
ref, err := w.r.Head()
···
57
hash = ref.Hash()
58
}
59
60
+
return w.status(o.Strategy, hash)
61
}
62
63
+
func (w *Worktree) status(ss StatusStrategy, commit plumbing.Hash) (Status, error) {
64
+
s, err := ss.new(w)
65
+
if err != nil {
66
+
return nil, err
67
+
}
68
69
left, err := w.diffCommitWithStaging(commit, false)
70
if err != nil {
+27
worktree_test.go
+27
worktree_test.go
···
1058
c.Assert(status, HasLen, 1)
1059
}
1060
1061
+
func (s *WorktreeSuite) TestStatusUnmodified(c *C) {
1062
+
fs := memfs.New()
1063
+
w := &Worktree{
1064
+
r: s.Repository,
1065
+
Filesystem: fs,
1066
+
}
1067
+
1068
+
err := w.Checkout(&CheckoutOptions{Force: true})
1069
+
c.Assert(err, IsNil)
1070
+
1071
+
status, err := w.StatusWithOptions(StatusOptions{Strategy: Preload})
1072
+
c.Assert(err, IsNil)
1073
+
c.Assert(status.IsClean(), Equals, true)
1074
+
c.Assert(status.IsUntracked("LICENSE"), Equals, false)
1075
+
1076
+
c.Assert(status.File("LICENSE").Staging, Equals, Unmodified)
1077
+
c.Assert(status.File("LICENSE").Worktree, Equals, Unmodified)
1078
+
1079
+
status, err = w.StatusWithOptions(StatusOptions{Strategy: Empty})
1080
+
c.Assert(err, IsNil)
1081
+
c.Assert(status.IsClean(), Equals, true)
1082
+
c.Assert(status.IsUntracked("LICENSE"), Equals, false)
1083
+
1084
+
c.Assert(status.File("LICENSE").Staging, Equals, Untracked)
1085
+
c.Assert(status.File("LICENSE").Worktree, Equals, Untracked)
1086
+
}
1087
+
1088
func (s *WorktreeSuite) TestReset(c *C) {
1089
fs := memfs.New()
1090
w := &Worktree{