@@ -2,88 +2,14 @@ package git
22
33import (
44 "bytes"
5- "errors"
65 "fmt"
7- "io"
8- "net/url"
9- "os"
10- "path"
11- "regexp"
126 "runtime"
137 "strings"
148
159 "github.com/carapace-sh/carapace-bin/completers/common/gh_completer/cmd/action/run"
1610 "github.com/carapace-sh/carapace/pkg/execlog"
17- "github.com/carapace-sh/carapace/pkg/util"
1811)
1912
20- // ErrNotOnAnyBranch indicates that the user is in detached HEAD state
21- var ErrNotOnAnyBranch = errors .New ("git: not on any branch" )
22-
23- // Ref represents a git commit reference
24- type Ref struct {
25- Hash string
26- Name string
27- }
28-
29- // TrackingRef represents a ref for a remote tracking branch
30- type TrackingRef struct {
31- RemoteName string
32- BranchName string
33- }
34-
35- func (r TrackingRef ) String () string {
36- return "refs/remotes/" + r .RemoteName + "/" + r .BranchName
37- }
38-
39- // ShowRefs resolves fully-qualified refs to commit hashes
40- func ShowRefs (ref ... string ) ([]Ref , error ) {
41- args := append ([]string {"show-ref" , "--verify" , "--" }, ref ... )
42- showRef , err := GitCommand (args ... )
43- if err != nil {
44- return nil , err
45- }
46- output , err := run .PrepareCmd (showRef ).Output ()
47-
48- var refs []Ref
49- for _ , line := range outputLines (output ) {
50- parts := strings .SplitN (line , " " , 2 )
51- if len (parts ) < 2 {
52- continue
53- }
54- refs = append (refs , Ref {
55- Hash : parts [0 ],
56- Name : parts [1 ],
57- })
58- }
59-
60- return refs , err
61- }
62-
63- // CurrentBranch reads the checked-out branch for the git repository
64- func CurrentBranch () (string , error ) {
65- refCmd , err := GitCommand ("symbolic-ref" , "--quiet" , "HEAD" )
66- if err != nil {
67- return "" , err
68- }
69-
70- stderr := bytes.Buffer {}
71- refCmd .Stderr = & stderr
72-
73- output , err := run .PrepareCmd (refCmd ).Output ()
74- if err == nil {
75- // Found the branch name
76- return getBranchShortName (output ), nil
77- }
78-
79- if stderr .Len () == 0 {
80- // Detached head
81- return "" , ErrNotOnAnyBranch
82- }
83-
84- return "" , fmt .Errorf ("%sgit: %s" , stderr .String (), err )
85- }
86-
8713func listRemotes () ([]string , error ) {
8814 remoteCmd , err := GitCommand ("remote" , "-v" )
8915 if err != nil {
@@ -93,20 +19,6 @@ func listRemotes() ([]string, error) {
9319 return outputLines (output ), err
9420}
9521
96- func Config (name string ) (string , error ) {
97- configCmd , err := GitCommand ("config" , name )
98- if err != nil {
99- return "" , err
100- }
101- output , err := run .PrepareCmd (configCmd ).Output ()
102- if err != nil {
103- return "" , fmt .Errorf ("unknown config key: %s" , name )
104- }
105-
106- return firstLine (output ), nil
107-
108- }
109-
11022var GitCommand = func (args ... string ) (* execlog.Cmd , error ) {
11123 gitExe , err := execlog .LookPath ("git" )
11224 if err != nil {
@@ -119,67 +31,11 @@ var GitCommand = func(args ...string) (*execlog.Cmd, error) {
11931 return execlog .Command (gitExe , args ... ), nil
12032}
12133
122- func UncommittedChangeCount () (int , error ) {
123- statusCmd , err := GitCommand ("status" , "--porcelain" )
124- if err != nil {
125- return 0 , err
126- }
127- output , err := run .PrepareCmd (statusCmd ).Output ()
128- if err != nil {
129- return 0 , err
130- }
131- lines := strings .Split (string (output ), "\n " )
132-
133- count := 0
134-
135- for _ , l := range lines {
136- if l != "" {
137- count ++
138- }
139- }
140-
141- return count , nil
142- }
143-
14434type Commit struct {
14535 Sha string
14636 Title string
14737}
14838
149- func Commits (baseRef , headRef string ) ([]* Commit , error ) {
150- logCmd , err := GitCommand (
151- "-c" , "log.ShowSignature=false" ,
152- "log" , "--pretty=format:%H,%s" ,
153- "--cherry" , fmt .Sprintf ("%s...%s" , baseRef , headRef ))
154- if err != nil {
155- return nil , err
156- }
157- output , err := run .PrepareCmd (logCmd ).Output ()
158- if err != nil {
159- return []* Commit {}, err
160- }
161-
162- commits := []* Commit {}
163- sha := 0
164- title := 1
165- for _ , line := range outputLines (output ) {
166- split := strings .SplitN (line , "," , 2 )
167- if len (split ) != 2 {
168- continue
169- }
170- commits = append (commits , & Commit {
171- Sha : split [sha ],
172- Title : split [title ],
173- })
174- }
175-
176- if len (commits ) == 0 {
177- return commits , fmt .Errorf ("could not find any commits between %s and %s" , baseRef , headRef )
178- }
179-
180- return commits , nil
181- }
182-
18339func lookupCommit (sha , format string ) ([]byte , error ) {
18440 logCmd , err := GitCommand ("-c" , "log.ShowSignature=false" , "show" , "-s" , "--pretty=format:" + format , sha )
18541 if err != nil {
@@ -201,166 +57,8 @@ func LastCommit() (*Commit, error) {
20157 }, nil
20258}
20359
204- func CommitBody (sha string ) (string , error ) {
205- output , err := lookupCommit (sha , "%b" )
206- return string (output ), err
207- }
208-
209- // Push publishes a git ref to a remote and sets up upstream configuration
210- func Push (remote string , ref string , cmdOut , cmdErr io.Writer ) error {
211- pushCmd , err := GitCommand ("push" , "--set-upstream" , remote , ref )
212- if err != nil {
213- return err
214- }
215- pushCmd .Stdout = cmdOut
216- pushCmd .Stderr = cmdErr
217- return run .PrepareCmd (pushCmd ).Run ()
218- }
219-
220- type BranchConfig struct {
221- RemoteName string
222- RemoteURL * url.URL
223- MergeRef string
224- }
225-
226- // ReadBranchConfig parses the `branch.BRANCH.(remote|merge)` part of git config
227- func ReadBranchConfig (branch string ) (cfg BranchConfig ) {
228- prefix := regexp .QuoteMeta (fmt .Sprintf ("branch.%s." , branch ))
229- configCmd , err := GitCommand ("config" , "--get-regexp" , fmt .Sprintf ("^%s(remote|merge)$" , prefix ))
230- if err != nil {
231- return
232- }
233- output , err := run .PrepareCmd (configCmd ).Output ()
234- if err != nil {
235- return
236- }
237- for _ , line := range outputLines (output ) {
238- parts := strings .SplitN (line , " " , 2 )
239- if len (parts ) < 2 {
240- continue
241- }
242- keys := strings .Split (parts [0 ], "." )
243- switch keys [len (keys )- 1 ] {
244- case "remote" :
245- if strings .Contains (parts [1 ], ":" ) {
246- u , err := ParseURL (parts [1 ])
247- if err != nil {
248- continue
249- }
250- cfg .RemoteURL = u
251- } else if ! util .HasPathPrefix (parts [1 ]) {
252- cfg .RemoteName = parts [1 ]
253- }
254- case "merge" :
255- cfg .MergeRef = parts [1 ]
256- }
257- }
258- return
259- }
260-
261- func DeleteLocalBranch (branch string ) error {
262- branchCmd , err := GitCommand ("branch" , "-D" , branch )
263- if err != nil {
264- return err
265- }
266- return run .PrepareCmd (branchCmd ).Run ()
267- }
268-
269- func HasLocalBranch (branch string ) bool {
270- configCmd , err := GitCommand ("rev-parse" , "--verify" , "refs/heads/" + branch )
271- if err != nil {
272- return false
273- }
274- _ , err = run .PrepareCmd (configCmd ).Output ()
275- return err == nil
276- }
277-
278- func CheckoutBranch (branch string ) error {
279- configCmd , err := GitCommand ("checkout" , branch )
280- if err != nil {
281- return err
282- }
283- return run .PrepareCmd (configCmd ).Run ()
284- }
285-
286- func parseCloneArgs (extraArgs []string ) (args []string , target string ) {
287- args = extraArgs
288-
289- if len (args ) > 0 {
290- if ! strings .HasPrefix (args [0 ], "-" ) {
291- target , args = args [0 ], args [1 :]
292- }
293- }
294- return
295- }
296-
297- func RunClone (cloneURL string , args []string ) (target string , err error ) {
298- cloneArgs , target := parseCloneArgs (args )
299-
300- cloneArgs = append (cloneArgs , cloneURL )
301-
302- // If the args contain an explicit target, pass it to clone
303- // otherwise, parse the URL to determine where git cloned it to so we can return it
304- if target != "" {
305- cloneArgs = append (cloneArgs , target )
306- } else {
307- target = path .Base (strings .TrimSuffix (cloneURL , ".git" ))
308- }
309-
310- cloneArgs = append ([]string {"clone" }, cloneArgs ... )
311-
312- cloneCmd , err := GitCommand (cloneArgs ... )
313- if err != nil {
314- return "" , err
315- }
316- cloneCmd .Stdin = os .Stdin
317- cloneCmd .Stdout = os .Stdout
318- cloneCmd .Stderr = os .Stderr
319-
320- err = run .PrepareCmd (cloneCmd ).Run ()
321- return
322- }
323-
324- func AddUpstreamRemote (upstreamURL , cloneDir string , branches []string ) error {
325- args := []string {"-C" , cloneDir , "remote" , "add" }
326- for _ , branch := range branches {
327- args = append (args , "-t" , branch )
328- }
329- args = append (args , "-f" , "upstream" , upstreamURL )
330- cloneCmd , err := GitCommand (args ... )
331- if err != nil {
332- return err
333- }
334- cloneCmd .Stdout = os .Stdout
335- cloneCmd .Stderr = os .Stderr
336- return run .PrepareCmd (cloneCmd ).Run ()
337- }
338-
339- // ToplevelDir returns the top-level directory path of the current repository
340- func ToplevelDir () (string , error ) {
341- showCmd , err := GitCommand ("rev-parse" , "--show-toplevel" )
342- if err != nil {
343- return "" , err
344- }
345- output , err := run .PrepareCmd (showCmd ).Output ()
346- return firstLine (output ), err
347-
348- }
349-
35060func outputLines (output []byte ) []string {
35161 lines := strings .TrimSuffix (string (output ), "\n " )
35262 return strings .Split (lines , "\n " )
35363
35464}
355-
356- func firstLine (output []byte ) string {
357- if i := bytes .IndexAny (output , "\n " ); i >= 0 {
358- return string (output )[0 :i ]
359- }
360- return string (output )
361- }
362-
363- func getBranchShortName (output []byte ) string {
364- branch := firstLine (output )
365- return strings .TrimPrefix (branch , "refs/heads/" )
366- }
0 commit comments