github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/gitconn.go (about)

     1  package git
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  )
     8  
     9  // gitConn represents a remote which uses the git protocol
    10  // for transport (ie. a remote that starts with git://)
    11  type gitConn struct {
    12  	*sharedRemoteConn
    13  	conn io.ReadWriteCloser
    14  }
    15  
    16  var _ RemoteConn = &gitConn{}
    17  
    18  func (g *gitConn) OpenConn(srv GitService) error {
    19  	host := g.uri.Hostname()
    20  	port := g.uri.Port()
    21  	if port == "" {
    22  		port = "9418"
    23  	}
    24  
    25  	conn, err := net.Dial("tcp", host+":"+port)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	g.conn = conn
    30  	g.packProtocolReader = &packProtocolReader{g.conn, PktLineMode, nil, nil}
    31  
    32  	// Advertise the connection and try to negotiate protocol version 2
    33  	fmt.Fprintf(
    34  		g,
    35  		g.service+" %s\x00host=%s\x00\x00version=2\x00",
    36  		g.uri.Path,
    37  		host,
    38  	)
    39  
    40  	v, cap, refs, err := parseRemoteInitialConnection(conn, false)
    41  	if err != nil {
    42  		conn.Close()
    43  		return err
    44  	}
    45  	g.protocolversion = v
    46  	g.capabilities = cap
    47  	g.refs = refs
    48  	return nil
    49  }
    50  
    51  func (g *gitConn) Close() error {
    52  	g.Flush()
    53  	return g.conn.Close()
    54  }
    55  
    56  func (g *gitConn) GetRefs(opts LsRemoteOptions, patterns []string) ([]Ref, error) {
    57  	switch g.protocolversion {
    58  	case 1:
    59  		return getRefsV1(g.refs, opts, patterns)
    60  	case 2:
    61  		g.SetWriteMode(PktLineMode)
    62  		cmd, err := buildLsRefsCmdV2(opts, patterns)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		fmt.Fprintf(g.conn, cmd)
    67  		line := make([]byte, 65536)
    68  		var vals []Ref
    69  		for {
    70  			n, err := g.Read(line)
    71  			switch err {
    72  			case flushPkt:
    73  				return vals, nil
    74  			case nil: // Nothing
    75  			default:
    76  				return nil, err
    77  			}
    78  			refstr := string(line[0:n])
    79  			ref, err := parseLsRef(refstr)
    80  			if err != nil {
    81  				return nil, err
    82  			}
    83  			vals = append(vals, ref)
    84  		}
    85  	default:
    86  		return nil, fmt.Errorf("Protocol version not supported")
    87  	}
    88  }
    89  
    90  func (g *gitConn) Write(data []byte) (int, error) {
    91  	switch g.writemode {
    92  	case PktLineMode:
    93  		l, err := PktLineEncodeNoNl(data)
    94  		if err != nil {
    95  			return 0, err
    96  		}
    97  		fmt.Fprintf(g.conn, "%s", l)
    98  		// We lie about how much data was written since
    99  		// we wrote more than asked.
   100  		return len(data), nil
   101  	case DirectMode:
   102  		return g.conn.Write(data)
   103  	default:
   104  		return 0, fmt.Errorf("Invalid write mode")
   105  	}
   106  }
   107  
   108  func (g *gitConn) Flush() error {
   109  	fmt.Fprintf(g.conn, "0000")
   110  	return nil
   111  }
   112  
   113  func (g *gitConn) Delim() error {
   114  	fmt.Fprintf(g.conn, "0001")
   115  	return nil
   116  }