• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gittuf / gittuf / 13142961148

04 Feb 2025 07:06PM UTC coverage: 63.248% (-0.1%) from 63.375%
13142961148

push

github

web-flow
Merge pull request #784 from gittuf/gitinterface-status

gitinterface: Add support for file statuses

40 of 81 new or added lines in 1 file covered. (49.38%)

5662 of 8952 relevant lines covered (63.25%)

51.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.38
/internal/gitinterface/status.go
1
// Copyright The gittuf Authors
2
// SPDX-License-Identifier: Apache-2.0
3

4
package gitinterface
5

6
import (
7
        "errors"
8
        "fmt"
9
        "strings"
10
)
11

12
// See https://git-scm.com/docs/git-status#_porcelain_format_version_1.
13

14
var (
15
        ErrInvalidStatusCodeLength = errors.New("status code string must be of length 1")
16
        ErrInvalidStatusCode       = errors.New("status code string is unrecognized")
17
)
18

19
type StatusCode uint
20

21
const (
22
        StatusCodeUnmodified StatusCode = iota + 1 // we use 0 as error code
23
        StatusCodeModified
24
        StatusCodeTypeChanged
25
        StatusCodeAdded
26
        StatusCodeDeleted
27
        StatusCodeRenamed
28
        StatusCodeCopied
29
        StatusCodeUpdatedUnmerged
30
        StatusCodeUntracked
31
        StatusCodeIgnored
32
)
33

NEW
34
func (s StatusCode) String() string {
×
NEW
35
        switch s {
×
NEW
36
        case StatusCodeUnmodified:
×
NEW
37
                return " " // is this actually a space or empty string?
×
NEW
38
        case StatusCodeModified:
×
NEW
39
                return "M"
×
NEW
40
        case StatusCodeTypeChanged:
×
NEW
41
                return "T"
×
NEW
42
        case StatusCodeAdded:
×
NEW
43
                return "A"
×
NEW
44
        case StatusCodeDeleted:
×
NEW
45
                return "D"
×
NEW
46
        case StatusCodeRenamed:
×
NEW
47
                return "R"
×
NEW
48
        case StatusCodeCopied:
×
NEW
49
                return "C"
×
NEW
50
        case StatusCodeUpdatedUnmerged:
×
NEW
51
                return "U"
×
NEW
52
        case StatusCodeUntracked:
×
NEW
53
                return "?"
×
NEW
54
        case StatusCodeIgnored:
×
NEW
55
                return "!"
×
NEW
56
        default:
×
NEW
57
                return "invalid-code"
×
58
        }
59
}
60

61
func NewStatusCodeFromByte(s byte) (StatusCode, error) {
12✔
62
        switch s {
12✔
63
        case ' ':
4✔
64
                return StatusCodeUnmodified, nil
4✔
65
        case 'M':
2✔
66
                return StatusCodeModified, nil
2✔
67
        case 'T':
1✔
68
                return StatusCodeTypeChanged, nil
1✔
69
        case 'A':
2✔
70
                return StatusCodeAdded, nil
2✔
71
        case 'D':
1✔
72
                return StatusCodeDeleted, nil
1✔
NEW
73
        case 'C':
×
NEW
74
                return StatusCodeCopied, nil
×
NEW
75
        case 'U':
×
NEW
76
                return StatusCodeUpdatedUnmerged, nil
×
77
        case '?':
2✔
78
                return StatusCodeUntracked, nil
2✔
NEW
79
        case '!':
×
NEW
80
                return StatusCodeIgnored, nil
×
NEW
81
        default:
×
NEW
82
                return 0, ErrInvalidStatusCode
×
83
        }
84
}
85

86
type FileStatus struct {
87
        X StatusCode
88
        Y StatusCode
89
}
90

NEW
91
func (f *FileStatus) Untracked() bool {
×
NEW
92
        return f.X == StatusCodeUntracked || f.Y == StatusCodeUntracked
×
NEW
93
}
×
94

95
func (r *Repository) Status() (map[string]FileStatus, error) {
10✔
96
        output, err := r.executor("status", "--porcelain=1", "-z", "--untracked-files=all", "--ignored").executeString()
10✔
97
        if err != nil {
10✔
NEW
98
                return nil, fmt.Errorf("unable to check status of repository: %w", err)
×
NEW
99
        }
×
100

101
        statuses := map[string]FileStatus{}
10✔
102

10✔
103
        lines := strings.Split(output, string('\000'))
10✔
104
        for _, line := range lines {
26✔
105
                if len(line) == 0 {
26✔
106
                        continue
10✔
107
                }
108

109
                // first two characters are status codes, find the corresponding
110
                // statuses
111
                xb := line[0]
6✔
112
                yb := line[1]
6✔
113
                // Note: we identify the status after inspecting the path so we can
6✔
114
                // provide better error messages
6✔
115

6✔
116
                // then, we have a single space followed by the path, ignore space and
6✔
117
                // read in the rest as the filepath
6✔
118
                filePath := strings.TrimSpace(line[2:])
6✔
119

6✔
120
                xStatus, err := NewStatusCodeFromByte(xb)
6✔
121
                if err != nil {
6✔
NEW
122
                        return nil, fmt.Errorf("unable to parse status code '%c' for path '%s': %w", xb, filePath, err)
×
NEW
123
                }
×
124

125
                yStatus, err := NewStatusCodeFromByte(yb)
6✔
126
                if err != nil {
6✔
NEW
127
                        return nil, fmt.Errorf("unable to parse status code '%c' for path '%s': %w", yb, filePath, err)
×
NEW
128
                }
×
129

130
                status := FileStatus{X: xStatus, Y: yStatus}
6✔
131

6✔
132
                statuses[filePath] = status
6✔
133
        }
134

135
        return statuses, nil
10✔
136
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc