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

gittuf / gittuf / 13113299957

03 Feb 2025 12:18PM UTC coverage: 63.338% (+0.01%) from 63.326%
13113299957

push

github

web-flow
Merge pull request #775 from Horiodino/add_functionality_to_discard_the_changes

Add functionality to discard the changes

15 of 22 new or added lines in 3 files covered. (68.18%)

5613 of 8862 relevant lines covered (63.34%)

51.83 hits per line

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

59.85
/internal/gitinterface/references.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
        "os"
10
        "path"
11
        "strings"
12
)
13

14
const (
15
        RefPrefix       = "refs/"
16
        BranchRefPrefix = "refs/heads/"
17
        TagRefPrefix    = "refs/tags/"
18
        RemoteRefPrefix = "refs/remotes/"
19
)
20

21
var (
22
        ErrReferenceNotFound = errors.New("requested Git reference not found")
23
)
24

25
// GetReference returns the tip of the specified Git reference.
26
func (r *Repository) GetReference(refName string) (Hash, error) {
151✔
27
        refTipID, err := r.executor("rev-parse", refName).executeString()
151✔
28
        if err != nil {
212✔
29
                if strings.Contains(err.Error(), "unknown revision or path not in the working tree") {
122✔
30
                        return ZeroHash, ErrReferenceNotFound
61✔
31
                }
61✔
32
                return ZeroHash, fmt.Errorf("unable to read reference '%s': %w", refName, err)
×
33
        }
34

35
        hash, err := NewHash(refTipID)
90✔
36
        if err != nil {
90✔
37
                return ZeroHash, fmt.Errorf("invalid Git ID for reference '%s': %w", refName, err)
×
38
        }
×
39

40
        return hash, nil
90✔
41
}
42

43
// SetReference sets the specified reference to the provided Git ID.
44
func (r *Repository) SetReference(refName string, gitID Hash) error {
15✔
45
        _, err := r.executor("update-ref", "--create-reflog", refName, gitID.String()).executeString()
15✔
46
        if err != nil {
15✔
47
                return fmt.Errorf("unable to set Git reference '%s' to '%s': %w", refName, gitID.String(), err)
×
48
        }
×
49

50
        return nil
15✔
51
}
52

53
// DeleteReference deletes the specified Git reference.
54
func (r *Repository) DeleteReference(refName string) error {
1✔
55
        _, err := r.executor("update-ref", "-d", refName).executeString()
1✔
56
        if err != nil {
1✔
NEW
57
                return fmt.Errorf("unable to delete Git reference '%s': %w", refName, err)
×
NEW
58
        }
×
59
        return nil
1✔
60
}
61

62
// CheckAndSetReference sets the specified reference to the provided Git ID if
63
// the reference is currently set to `oldGitID`.
64
func (r *Repository) CheckAndSetReference(refName string, newGitID, oldGitID Hash) error {
103✔
65
        _, err := r.executor("update-ref", "--create-reflog", refName, newGitID.String(), oldGitID.String()).executeString()
103✔
66
        if err != nil {
103✔
67
                return fmt.Errorf("unable to set Git reference '%s' to '%s': %w", refName, newGitID.String(), err)
×
68
        }
×
69

70
        return nil
103✔
71
}
72

73
// GetSymbolicReferenceTarget returns the name of the Git reference the provided
74
// symbolic Git reference is pointing to.
75
func (r *Repository) GetSymbolicReferenceTarget(refName string) (string, error) {
1✔
76
        symTarget, err := r.executor("symbolic-ref", refName).executeString()
1✔
77
        if err != nil {
1✔
78
                return "", fmt.Errorf("unable to resolve %s: %w", refName, err)
×
79
        }
×
80

81
        return symTarget, nil
1✔
82
}
83

84
// AbsoluteReference returns the fully qualified reference path for the provided
85
// Git ref.
86
// Source: https://git-scm.com/docs/gitrevisions#Documentation/gitrevisions.txt-emltrefnamegtemegemmasterememheadsmasterememrefsheadsmasterem
87
func (r *Repository) AbsoluteReference(target string) (string, error) {
4✔
88
        _, err := os.Stat(path.Join(r.gitDirPath, target))
4✔
89
        if err == nil {
4✔
90
                if strings.HasPrefix(target, RefPrefix) {
×
91
                        // not symbolic ref
×
92
                        return target, nil
×
93
                }
×
94
                // symbolic ref such as .git/HEAD
95
                return r.GetSymbolicReferenceTarget(target)
×
96
        }
97

98
        // We may have a ref that isn't available locally but is still ref-prefixed.
99
        if strings.HasPrefix(target, RefPrefix) {
4✔
100
                return target, nil
×
101
        }
×
102

103
        // If target is a full ref already and it's stored in the GIT_DIR/refs
104
        // directory, we don't reach this point. Below, we handle cases where the
105
        // ref may be packed.
106

107
        // Check if custom reference
108
        customName := CustomReferenceName(target)
4✔
109
        _, err = r.GetReference(customName)
4✔
110
        if err == nil {
4✔
111
                return customName, nil
×
112
        }
×
113
        if !errors.Is(err, ErrReferenceNotFound) {
4✔
114
                return "", err
×
115
        }
×
116

117
        // Check if tag
118
        tagName := TagReferenceName(target)
4✔
119
        _, err = r.GetReference(tagName)
4✔
120
        if err == nil {
4✔
121
                return tagName, nil
×
122
        }
×
123
        if !errors.Is(err, ErrReferenceNotFound) {
4✔
124
                return "", err
×
125
        }
×
126

127
        // Check if branch
128
        branchName := BranchReferenceName(target)
4✔
129
        _, err = r.GetReference(branchName)
4✔
130
        if err == nil {
8✔
131
                return branchName, nil
4✔
132
        }
4✔
133
        if !errors.Is(err, ErrReferenceNotFound) {
×
134
                return "", err
×
135
        }
×
136

137
        // Check if remote tracker ref
138
        remoteRefName := RemoteReferenceName(target)
×
139
        _, err = r.GetReference(remoteRefName)
×
140
        if err == nil {
×
141
                return branchName, nil
×
142
        }
×
143
        if !errors.Is(err, ErrReferenceNotFound) {
×
144
                return "", err
×
145
        }
×
146

147
        remoteRefHEAD := path.Join(remoteRefName, "HEAD")
×
148
        _, err = r.GetReference(remoteRefHEAD)
×
149
        if err == nil {
×
150
                return branchName, nil
×
151
        }
×
152
        if !errors.Is(err, ErrReferenceNotFound) {
×
153
                return "", err
×
154
        }
×
155

156
        return "", ErrReferenceNotFound
×
157
}
158

159
// RefSpec creates a Git refspec for the specified ref.  For more information on
160
// the Git refspec, please consult:
161
// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec.
162
func (r *Repository) RefSpec(refName, remoteName string, fastForwardOnly bool) (string, error) {
30✔
163
        var (
30✔
164
                refPath string
30✔
165
                err     error
30✔
166
        )
30✔
167

30✔
168
        refPath = refName
30✔
169
        if !strings.HasPrefix(refPath, RefPrefix) {
34✔
170
                refPath, err = r.AbsoluteReference(refName)
4✔
171
                if err != nil {
4✔
172
                        return "", err
×
173
                }
×
174
        }
175

176
        if strings.HasPrefix(refPath, TagRefPrefix) {
34✔
177
                // TODO: check if this is correct, AFAICT tags aren't tracked in the
4✔
178
                // remotes namespace.
4✔
179
                fastForwardOnly = true
4✔
180
        }
4✔
181

182
        // local is always refPath, destination depends on remoteName
183
        localPath := refPath
30✔
184
        var remotePath string
30✔
185
        if len(remoteName) > 0 {
40✔
186
                remotePath = RemoteRef(refPath, remoteName)
10✔
187
        } else {
30✔
188
                remotePath = refPath
20✔
189
        }
20✔
190

191
        refSpecString := fmt.Sprintf("%s:%s", localPath, remotePath)
30✔
192
        if !fastForwardOnly {
38✔
193
                refSpecString = fmt.Sprintf("+%s", refSpecString)
8✔
194
        }
8✔
195

196
        return refSpecString, nil
30✔
197
}
198

199
// CustomReferenceName returns the full reference name in the form
200
// `refs/<customName>`.
201
func CustomReferenceName(customName string) string {
4✔
202
        if strings.HasPrefix(customName, RefPrefix) {
4✔
203
                return customName
×
204
        }
×
205

206
        return fmt.Sprintf("%s%s", RefPrefix, customName)
4✔
207
}
208

209
// TagReferenceName returns the full reference name for the specified tag in the
210
// form `refs/tags/<tagName>`.
211
func TagReferenceName(tagName string) string {
9✔
212
        if strings.HasPrefix(tagName, TagRefPrefix) {
10✔
213
                return tagName
1✔
214
        }
1✔
215

216
        return fmt.Sprintf("%s%s", TagRefPrefix, tagName)
8✔
217
}
218

219
// BranchReferenceName returns the full reference name for the specified branch
220
// in the form `refs/heads/<branchName>`.
221
func BranchReferenceName(branchName string) string {
27✔
222
        if strings.HasPrefix(branchName, BranchRefPrefix) {
28✔
223
                return branchName
1✔
224
        }
1✔
225

226
        return fmt.Sprintf("%s%s", BranchRefPrefix, branchName)
26✔
227
}
228

229
// RemoteReferenceName returns the full reference name in the form
230
// `refs/remotes/<name>`.
231
func RemoteReferenceName(name string) string {
×
232
        if strings.HasPrefix(name, RemoteRefPrefix) {
×
233
                return name
×
234
        }
×
235

236
        return fmt.Sprintf("%s%s", RemoteRefPrefix, name)
×
237
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc