github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/localconn.go (about) 1 package git 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "os/exec" 9 ) 10 11 // A localConn is like an ssh conn, but it communicates locally 12 // over a pipe rather than running git-upload-pack remotely over 13 // ssh. 14 type localConn struct { 15 // Add functionality shared amongst all types of remotes 16 *sharedRemoteConn 17 18 stdin io.ReadCloser 19 stdout io.WriteCloser 20 cmd *exec.Cmd 21 } 22 23 var _ RemoteConn = &localConn{} 24 25 func (s *localConn) OpenConn(srv GitService) error { 26 var cmd *exec.Cmd 27 log.Println("Connecting locally via", s.uri.Path) 28 if s.service == "" { 29 cmd = exec.Command(s.service, s.uri.Path) 30 } else { 31 switch srv { 32 case UploadPackService: 33 cmd = exec.Command("git-upload-pack", s.uri.Path) 34 case ReceivePackService: 35 cmd = exec.Command("git-receive-pack", s.uri.Path) 36 default: 37 return fmt.Errorf("Unhandled service") 38 } 39 } 40 cmd.Stderr = os.Stderr 41 cmdIn, err := cmd.StdinPipe() 42 if err != nil { 43 return err 44 } 45 s.stdout = cmdIn 46 cmdOut, err := cmd.StdoutPipe() 47 if err != nil { 48 return err 49 } 50 s.stdin = cmdOut 51 52 // We don't check error on setenv because if it failed we'll just fall 53 // back on protocol v1 54 os.Setenv("GIT_PROTOCOL", "version=2") 55 56 if err := cmd.Start(); err != nil { 57 return err 58 } 59 60 v, cap, refs, err := parseRemoteInitialConnection(s.stdin, false) 61 if err != nil { 62 s.stdin.Close() 63 s.stdout.Close() 64 return cmd.Wait() 65 } 66 s.cmd = cmd 67 s.packProtocolReader = &packProtocolReader{conn: s.stdin, state: PktLineMode} 68 69 s.protocolversion = v 70 s.capabilities = cap 71 s.refs = refs 72 return nil 73 } 74 75 func (s localConn) Close() error { 76 fmt.Fprintf(s.stdout, "0000") 77 s.stdout.Close() 78 s.stdin.Close() 79 return s.cmd.Wait() 80 } 81 82 func (s localConn) GetRefs(opts LsRemoteOptions, patterns []string) ([]Ref, error) { 83 switch s.protocolversion { 84 case 1: 85 return getRefsV1(s.refs, opts, patterns) 86 case 2: 87 s.SetWriteMode(PktLineMode) 88 cmd, err := buildLsRefsCmdV2(opts, patterns) 89 if err != nil { 90 return nil, err 91 } 92 fmt.Fprintf(s.stdout, cmd) 93 line := make([]byte, 65536) 94 var vals []Ref 95 for { 96 n, err := s.Read(line) 97 switch err { 98 case flushPkt: 99 return vals, nil 100 case nil: // Nothing 101 default: 102 return nil, err 103 } 104 refstr := string(line[0:n]) 105 ref, err := parseLsRef(refstr) 106 if err != nil { 107 return nil, err 108 } 109 vals = append(vals, ref) 110 } 111 default: 112 return nil, fmt.Errorf("Protocol version not supported") 113 } 114 } 115 116 func (s localConn) Flush() error { 117 fmt.Fprintf(s.stdout, "0000") 118 return nil 119 } 120 121 func (s localConn) Delim() error { 122 fmt.Fprintf(s.stdout, "0001") 123 return nil 124 } 125 126 func (s localConn) Write(data []byte) (int, error) { 127 switch s.writemode { 128 case PktLineMode: 129 l, err := PktLineEncodeNoNl(data) 130 if err != nil { 131 return 0, err 132 } 133 fmt.Fprintf(s.stdout, "%s", l) 134 return len(data), nil 135 case DirectMode: 136 return s.stdout.Write(data) 137 default: 138 return 0, fmt.Errorf("Invalid write mode") 139 } 140 }