@@ -2,78 +2,73 @@ package git
22
33import (
44 "bytes"
5+ "context"
56 "fmt"
67 "os"
7- "os/exec"
88 "strings"
9- )
109
11- func GetExecCommand (args []string ) (* exec.Cmd , error ) {
12- git , err := exec .LookPath ("git" )
13- if err != nil {
14- return nil , fmt .Errorf ("failed to find 'git' on the path: %w" , err )
15- }
10+ "github.com/github/git-bundle-server/internal/cmd"
11+ "github.com/github/git-bundle-server/internal/log"
12+ )
1613
17- cmd := exec .Command (git , args ... )
18- cmd .Env = append (cmd .Env , "LC_CTYPE=C" )
14+ type GitHelper interface {
15+ CreateBundle (ctx context.Context , repoDir string , filename string ) (bool , error )
16+ CreateBundleFromRefs (ctx context.Context , repoDir string , filename string , refs map [string ]string ) error
17+ CreateIncrementalBundle (ctx context.Context , repoDir string , filename string , prereqs []string ) (bool , error )
18+ CloneBareRepo (ctx context.Context , url string , destination string ) error
19+ }
1920
20- return cmd , nil
21+ type gitHelper struct {
22+ logger log.TraceLogger
23+ cmdExec cmd.CommandExecutor
2124}
2225
23- func GitCommand ( args ... string ) error {
24- cmd , err := GetExecCommand ( args )
25- if err != nil {
26- return err
26+ func NewGitHelper ( l log. TraceLogger , c cmd. CommandExecutor ) GitHelper {
27+ return & gitHelper {
28+ logger : l ,
29+ cmdExec : c ,
2730 }
31+ }
2832
29- cmd .Stderr = os .Stderr
30- cmd .Stdout = os .Stdout
31-
32- err = cmd .Start ()
33- if err != nil {
34- return fmt .Errorf ("git command failed to start: %w" , err )
35- }
33+ func (g * gitHelper ) gitCommand (ctx context.Context , args ... string ) error {
34+ exitCode , err := g .cmdExec .Run (ctx , "git" , args ,
35+ cmd .Stdout (os .Stdout ),
36+ cmd .Stderr (os .Stderr ),
37+ cmd .Env ([]string {"LC_CTYPE=C" }),
38+ )
3639
37- err = cmd .Wait ()
3840 if err != nil {
39- return fmt .Errorf ("git command returned a failure: %w" , err )
41+ return g .logger .Error (ctx , err )
42+ } else if exitCode != 0 {
43+ return g .logger .Errorf (ctx , "'git' exited with status %d" , exitCode )
4044 }
4145
42- return err
46+ return nil
4347}
4448
45- func GitCommandWithStdin (stdinLines []string , args ... string ) error {
46- cmd , err := GetExecCommand (args )
47- if err != nil {
48- return err
49- }
50-
49+ func (g * gitHelper ) gitCommandWithStdin (ctx context.Context , stdinLines []string , args ... string ) error {
5150 buffer := bytes.Buffer {}
5251 for line := range stdinLines {
5352 buffer .Write ([]byte (stdinLines [line ] + "\n " ))
5453 }
54+ exitCode , err := g .cmdExec .Run (ctx , "git" , args ,
55+ cmd .Stdin (& buffer ),
56+ cmd .Stdout (os .Stdout ),
57+ cmd .Stderr (os .Stderr ),
58+ cmd .Env ([]string {"LC_CTYPE=C" }),
59+ )
5560
56- cmd .Stdin = & buffer
57-
58- errorBuffer := bytes.Buffer {}
59- cmd .Stderr = & errorBuffer
60- cmd .Stdout = os .Stdout
61-
62- err = cmd .Start ()
63- if err != nil {
64- return fmt .Errorf ("git command failed to start: %w" , err )
65- }
66-
67- err = cmd .Wait ()
6861 if err != nil {
69- return fmt .Errorf ("git command returned a failure: %w\n stderr: %s" , err , errorBuffer .String ())
62+ return g .logger .Error (ctx , err )
63+ } else if exitCode != 0 {
64+ return g .logger .Errorf (ctx , "'git' exited with status %d" , exitCode )
7065 }
7166
72- return err
67+ return nil
7368}
7469
75- func CreateBundle (repoDir string , filename string ) (bool , error ) {
76- err := GitCommand (
70+ func ( g * gitHelper ) CreateBundle (ctx context. Context , repoDir string , filename string ) (bool , error ) {
71+ err := g . gitCommand ( ctx ,
7772 "-C" , repoDir , "bundle" , "create" ,
7873 filename , "--branches" )
7974 if err != nil {
@@ -86,19 +81,19 @@ func CreateBundle(repoDir string, filename string) (bool, error) {
8681 return true , nil
8782}
8883
89- func CreateBundleFromRefs (repoDir string , filename string , refs map [string ]string ) error {
84+ func ( g * gitHelper ) CreateBundleFromRefs (ctx context. Context , repoDir string , filename string , refs map [string ]string ) error {
9085 refNames := []string {}
9186
9287 for ref , oid := range refs {
93- err := GitCommand ( "-C" , repoDir , "branch" , "-f" , ref , oid )
88+ err := g . gitCommand ( ctx , "-C" , repoDir , "branch" , "-f" , ref , oid )
9489 if err != nil {
9590 return fmt .Errorf ("failed to create ref %s: %w" , ref , err )
9691 }
9792
9893 refNames = append (refNames , ref )
9994 }
10095
101- err := GitCommandWithStdin (
96+ err := g . gitCommandWithStdin ( ctx ,
10297 refNames ,
10398 "-C" , repoDir , "bundle" , "create" ,
10499 filename , "--stdin" )
@@ -109,8 +104,8 @@ func CreateBundleFromRefs(repoDir string, filename string, refs map[string]strin
109104 return nil
110105}
111106
112- func CreateIncrementalBundle (repoDir string , filename string , prereqs []string ) (bool , error ) {
113- err := GitCommandWithStdin (
107+ func ( g * gitHelper ) CreateIncrementalBundle (ctx context. Context , repoDir string , filename string , prereqs []string ) (bool , error ) {
108+ err := g . gitCommandWithStdin ( ctx ,
114109 prereqs , "-C" , repoDir , "bundle" , "create" ,
115110 filename , "--stdin" , "--branches" )
116111 if err != nil {
@@ -122,3 +117,23 @@ func CreateIncrementalBundle(repoDir string, filename string, prereqs []string)
122117
123118 return true , nil
124119}
120+
121+ func (g * gitHelper ) CloneBareRepo (ctx context.Context , url string , destination string ) error {
122+ gitErr := g .gitCommand (ctx , "clone" , "--bare" , url , destination )
123+
124+ if gitErr != nil {
125+ return g .logger .Errorf (ctx , "failed to clone repository: %w" , gitErr )
126+ }
127+
128+ gitErr = g .gitCommand (ctx , "-C" , destination , "config" , "remote.origin.fetch" , "+refs/heads/*:refs/heads/*" )
129+ if gitErr != nil {
130+ return g .logger .Errorf (ctx , "failed to configure refspec: %w" , gitErr )
131+ }
132+
133+ gitErr = g .gitCommand (ctx , "-C" , destination , "fetch" , "origin" )
134+ if gitErr != nil {
135+ return g .logger .Errorf (ctx , "failed to fetch latest refs: %w" , gitErr )
136+ }
137+
138+ return nil
139+ }
0 commit comments