Bump reva

This commit is contained in:
André Duffeck
2026-02-11 10:05:53 +01:00
committed by Ralf Haferkamp
parent 99aba9cfa1
commit ae3582afa3
36 changed files with 619 additions and 228 deletions
+46 -10
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
@@ -72,9 +73,16 @@ type CloneOptions struct {
// Tags describe how the tags will be fetched from the remote repository,
// by default is AllTags.
Tags TagMode
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// ProxyOptions provides info required for connecting to a proxy.
ProxyOptions transport.ProxyOptions
@@ -153,9 +161,16 @@ type PullOptions struct {
// Force allows the pull to update a local branch even when the remote
// branch does not descend from it.
Force bool
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// ProxyOptions provides info required for connecting to a proxy.
ProxyOptions transport.ProxyOptions
@@ -211,9 +226,16 @@ type FetchOptions struct {
// Force allows the fetch to update a local branch even when the remote
// branch does not descend from it.
Force bool
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// ProxyOptions provides info required for connecting to a proxy.
ProxyOptions transport.ProxyOptions
@@ -267,9 +289,16 @@ type PushOptions struct {
// Force allows the push to update a remote branch even when the local
// branch does not descend from it.
Force bool
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// RequireRemoteRefs only allows a remote ref to be updated if its current
// value is the one specified here.
@@ -693,9 +722,16 @@ func (o *CreateTagOptions) loadConfigTagger(r *Repository) error {
type ListOptions struct {
// Auth credentials, if required, to use with the remote repository.
Auth transport.AuthMethod
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// PeelingOption defines how peeled objects are handled during a
// remote list.
+25 -6
View File
@@ -1,9 +1,11 @@
package idxfile
import (
"bufio"
"bytes"
"crypto"
"encoding/hex"
"errors"
"fmt"
"io"
"github.com/go-git/go-git/v5/plumbing/hash"
@@ -25,12 +27,15 @@ const (
// Decoder reads and decodes idx files from an input stream.
type Decoder struct {
*bufio.Reader
io.Reader
h hash.Hash
}
// NewDecoder builds a new idx stream decoder, that reads from r.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{bufio.NewReader(r)}
h := hash.New(crypto.SHA1)
tr := io.TeeReader(r, h)
return &Decoder{tr, h}
}
// Decode reads from the stream and decode the content into the MemoryIndex struct.
@@ -45,7 +50,7 @@ func (d *Decoder) Decode(idx *MemoryIndex) error {
readObjectNames,
readCRC32,
readOffsets,
readChecksums,
readPackChecksum,
}
for _, f := range flow {
@@ -54,11 +59,21 @@ func (d *Decoder) Decode(idx *MemoryIndex) error {
}
}
actual := d.h.Sum(nil)
if err := readIdxChecksum(idx, d); err != nil {
return err
}
if !bytes.Equal(actual, idx.IdxChecksum[:]) {
return fmt.Errorf("%w: checksum mismatch: %q instead of %q",
ErrMalformedIdxFile, hex.EncodeToString(idx.IdxChecksum[:]), hex.EncodeToString(actual))
}
return nil
}
func validateHeader(r io.Reader) error {
var h = make([]byte, 4)
h := make([]byte, 4)
if _, err := io.ReadFull(r, h); err != nil {
return err
}
@@ -165,11 +180,15 @@ func readOffsets(idx *MemoryIndex, r io.Reader) error {
return nil
}
func readChecksums(idx *MemoryIndex, r io.Reader) error {
func readPackChecksum(idx *MemoryIndex, r io.Reader) error {
if _, err := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil {
return err
}
return nil
}
func readIdxChecksum(idx *MemoryIndex, r io.Reader) error {
if _, err := io.ReadFull(r, idx.IdxChecksum[:]); err != nil {
return err
}
+25 -23
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"io"
"sort"
"sync"
encbin "encoding/binary"
@@ -57,8 +58,9 @@ type MemoryIndex struct {
PackfileChecksum [hash.Size]byte
IdxChecksum [hash.Size]byte
offsetHash map[int64]plumbing.Hash
offsetHashIsFull bool
offsetHash map[int64]plumbing.Hash
offsetBuildOnce sync.Once
mu sync.RWMutex
}
var _ Index = (*MemoryIndex)(nil)
@@ -126,13 +128,13 @@ func (idx *MemoryIndex) FindOffset(h plumbing.Hash) (int64, error) {
offset := idx.getOffset(k, i)
if !idx.offsetHashIsFull {
// Save the offset for reverse lookup
if idx.offsetHash == nil {
idx.offsetHash = make(map[int64]plumbing.Hash)
}
idx.offsetHash[int64(offset)] = h
// Save the offset for reverse lookup
idx.mu.Lock()
if idx.offsetHash == nil {
idx.offsetHash = make(map[int64]plumbing.Hash)
}
idx.offsetHash[int64(offset)] = h
idx.mu.Unlock()
return int64(offset), nil
}
@@ -173,20 +175,17 @@ func (idx *MemoryIndex) FindHash(o int64) (plumbing.Hash, error) {
var hash plumbing.Hash
var ok bool
if idx.offsetHash != nil {
if hash, ok = idx.offsetHash[o]; ok {
return hash, nil
}
var genErr error
idx.offsetBuildOnce.Do(func() {
genErr = idx.genOffsetHash()
})
if genErr != nil {
return plumbing.ZeroHash, genErr
}
// Lazily generate the reverse offset/hash map if required.
if !idx.offsetHashIsFull || idx.offsetHash == nil {
if err := idx.genOffsetHash(); err != nil {
return plumbing.ZeroHash, err
}
hash, ok = idx.offsetHash[o]
}
idx.mu.RLock()
hash, ok = idx.offsetHash[o]
idx.mu.RUnlock()
if !ok {
return plumbing.ZeroHash, plumbing.ErrObjectNotFound
@@ -202,8 +201,7 @@ func (idx *MemoryIndex) genOffsetHash() error {
return err
}
idx.offsetHash = make(map[int64]plumbing.Hash, count)
idx.offsetHashIsFull = true
offsetHash := make(map[int64]plumbing.Hash, count)
var hash plumbing.Hash
i := uint32(0)
@@ -212,11 +210,15 @@ func (idx *MemoryIndex) genOffsetHash() error {
for secondLevel := uint32(0); i < fanoutValue; i++ {
copy(hash[:], idx.Names[mappedFirstLevel][secondLevel*objectIDLength:])
offset := int64(idx.getOffset(mappedFirstLevel, int(secondLevel)))
idx.offsetHash[offset] = hash
offsetHash[offset] = hash
secondLevel++
}
}
idx.mu.Lock()
idx.offsetHash = offsetHash
idx.mu.Unlock()
return nil
}
+1 -1
View File
@@ -30,7 +30,7 @@ type Reader struct {
func NewReader(r io.Reader) (*Reader, error) {
zlib, err := sync.GetZlibReader(r)
if err != nil {
return nil, packfile.ErrZLib.AddDetails(err.Error())
return nil, packfile.ErrZLib.AddDetails("%s", err.Error())
}
return &Reader{
+16 -20
View File
@@ -47,7 +47,6 @@ type Parser struct {
oi []*objectInfo
oiByHash map[plumbing.Hash]*objectInfo
oiByOffset map[int64]*objectInfo
checksum plumbing.Hash
cache *cache.BufferLRU
// delta content by offset, only used if source is not seekable
@@ -133,28 +132,27 @@ func (p *Parser) onFooter(h plumbing.Hash) error {
// Parse start decoding phase of the packfile.
func (p *Parser) Parse() (plumbing.Hash, error) {
if err := p.init(); err != nil {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
if err := p.indexObjects(); err != nil {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
var err error
p.checksum, err = p.scanner.Checksum()
checksum, err := p.scanner.Checksum()
if err != nil && err != io.EOF {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
if err := p.resolveDeltas(); err != nil {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
if err := p.onFooter(p.checksum); err != nil {
return plumbing.ZeroHash, err
if err := p.onFooter(checksum); err != nil {
return plumbing.ZeroHash, wrapEOF(err)
}
return p.checksum, nil
return checksum, nil
}
func (p *Parser) init() error {
@@ -218,7 +216,7 @@ func (p *Parser) indexObjects() error {
if !ok {
// can't find referenced object in this pack file
// this must be a "thin" pack.
parent = &objectInfo{ //Placeholder parent
parent = &objectInfo{ // Placeholder parent
SHA1: oh.Reference,
ExternalRef: true, // mark as an external reference that must be resolved
Type: plumbing.AnyObject,
@@ -531,6 +529,13 @@ func (p *Parser) readData(w io.Writer, o *objectInfo) error {
return nil
}
func wrapEOF(err error) error {
if err == io.ErrUnexpectedEOF || err == io.EOF {
return fmt.Errorf("%w: %w", ErrMalformedPackFile, err)
}
return err
}
// applyPatchBase applies the patch to target.
//
// Note that ota will be updated based on the description in resolveObject.
@@ -558,15 +563,6 @@ func applyPatchBase(ota *objectInfo, base io.ReaderAt, delta io.Reader, target i
return nil
}
func getSHA1(t plumbing.ObjectType, data []byte) (plumbing.Hash, error) {
hasher := plumbing.NewHasher(t, int64(len(data)))
if _, err := hasher.Write(data); err != nil {
return plumbing.ZeroHash, err
}
return hasher.Sum(), nil
}
type objectInfo struct {
Offset int64
Length int64
+32 -13
View File
@@ -3,12 +3,15 @@ package packfile
import (
"bufio"
"bytes"
"crypto"
"errors"
"fmt"
"hash"
gohash "hash"
"hash/crc32"
"io"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/utils/binary"
"github.com/go-git/go-git/v5/utils/ioutil"
"github.com/go-git/go-git/v5/utils/sync"
@@ -24,6 +27,8 @@ var (
ErrUnsupportedVersion = NewError("unsupported packfile version")
// ErrSeekNotSupported returned if seek is not support
ErrSeekNotSupported = NewError("not seek support")
// ErrMalformedPackFile is returned by the parser when the pack file is corrupted.
ErrMalformedPackFile = errors.New("malformed PACK file")
)
// ObjectHeader contains the information related to the object, this information
@@ -37,8 +42,9 @@ type ObjectHeader struct {
}
type Scanner struct {
r *scannerReader
crc hash.Hash32
r *scannerReader
crc gohash.Hash32
packHasher hash.Hash
// pendingObject is used to detect if an object has been read, or still
// is waiting to be read
@@ -56,10 +62,12 @@ func NewScanner(r io.Reader) *Scanner {
_, ok := r.(io.ReadSeeker)
crc := crc32.NewIEEE()
hasher := hash.New(crypto.SHA1)
return &Scanner{
r: newScannerReader(r, crc),
r: newScannerReader(r, io.MultiWriter(crc, hasher)),
crc: crc,
IsSeekable: ok,
packHasher: hasher,
}
}
@@ -68,6 +76,7 @@ func (s *Scanner) Reset(r io.Reader) {
s.r.Reset(r)
s.crc.Reset()
s.packHasher.Reset()
s.IsSeekable = ok
s.pendingObject = nil
s.version = 0
@@ -114,7 +123,7 @@ func (s *Scanner) Header() (version, objects uint32, err error) {
// readSignature reads a returns the signature field in the packfile.
func (s *Scanner) readSignature() ([]byte, error) {
var sig = make([]byte, 4)
sig := make([]byte, 4)
if _, err := io.ReadFull(s.r, sig); err != nil {
return []byte{}, err
}
@@ -322,7 +331,6 @@ func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err erro
func (s *Scanner) ReadObject() (io.ReadCloser, error) {
s.pendingObject = nil
zr, err := sync.GetZlibReader(s.r)
if err != nil {
return nil, fmt.Errorf("zlib reset error: %s", err)
}
@@ -374,7 +382,18 @@ func (s *Scanner) Checksum() (plumbing.Hash, error) {
return plumbing.ZeroHash, err
}
return binary.ReadHash(s.r)
s.r.Flush()
actual := plumbing.Hash(s.packHasher.Sum(nil))
packChecksum, err := binary.ReadHash(s.r)
if err != nil {
return plumbing.ZeroHash, err
}
if actual != packChecksum {
return plumbing.ZeroHash, fmt.Errorf("%w: checksum mismatch: %q instead of %q", ErrMalformedPackFile, packChecksum, actual)
}
return packChecksum, nil
}
// Close reads the reader until io.EOF
@@ -401,17 +420,17 @@ func (s *Scanner) Flush() error {
// to the crc32 hash writer.
type scannerReader struct {
reader io.Reader
crc io.Writer
writer io.Writer
rbuf *bufio.Reader
wbuf *bufio.Writer
offset int64
}
func newScannerReader(r io.Reader, h io.Writer) *scannerReader {
func newScannerReader(r io.Reader, w io.Writer) *scannerReader {
sr := &scannerReader{
rbuf: bufio.NewReader(nil),
wbuf: bufio.NewWriterSize(nil, 64),
crc: h,
rbuf: bufio.NewReader(nil),
wbuf: bufio.NewWriterSize(nil, 64),
writer: w,
}
sr.Reset(r)
@@ -421,7 +440,7 @@ func newScannerReader(r io.Reader, h io.Writer) *scannerReader {
func (r *scannerReader) Reset(reader io.Reader) {
r.reader = reader
r.rbuf.Reset(r.reader)
r.wbuf.Reset(r.crc)
r.wbuf.Reset(r.writer)
r.offset = 0
if seeker, ok := r.reader.(io.ReadSeeker); ok {
+72
View File
@@ -62,10 +62,55 @@ type Commit struct {
ParentHashes []plumbing.Hash
// Encoding is the encoding of the commit.
Encoding MessageEncoding
// List of extra headers of the commit
ExtraHeaders []ExtraHeader
s storer.EncodedObjectStorer
}
// ExtraHeader holds any non-standard header
type ExtraHeader struct {
// Header name
Key string
// Value of the header
Value string
}
// Implement fmt.Formatter for ExtraHeader
func (h ExtraHeader) Format(f fmt.State, verb rune) {
switch verb {
case 'v':
fmt.Fprintf(f, "ExtraHeader{Key: %v, Value: %v}", h.Key, h.Value)
default:
fmt.Fprintf(f, "%s", h.Key)
if len(h.Value) > 0 {
fmt.Fprint(f, " ")
// Content may be spread on multiple lines, if so we need to
// prepend each of them with a space for "continuation".
value := strings.TrimSuffix(h.Value, "\n")
lines := strings.Split(value, "\n")
fmt.Fprint(f, strings.Join(lines, "\n "))
}
}
}
// Parse an extra header and indicate whether it may be continue on the next line
func parseExtraHeader(line []byte) (ExtraHeader, bool) {
split := bytes.SplitN(line, []byte{' '}, 2)
out := ExtraHeader {
Key: string(bytes.TrimRight(split[0], "\n")),
Value: "",
}
if len(split) == 2 {
out.Value += string(split[1])
return out, true
} else {
return out, false
}
}
// GetCommit gets a commit from an object storer and decodes it.
func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) {
o, err := s.EncodedObject(plumbing.CommitObject, h)
@@ -204,6 +249,7 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
var mergetag bool
var pgpsig bool
var msgbuf bytes.Buffer
var extraheader *ExtraHeader = nil
for {
line, err := r.ReadBytes('\n')
if err != nil && err != io.EOF {
@@ -230,7 +276,19 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
}
}
if extraheader != nil {
if len(line) > 0 && line[0] == ' ' {
extraheader.Value += string(line[1:])
continue
} else {
extraheader.Value = strings.TrimRight(extraheader.Value, "\n")
c.ExtraHeaders = append(c.ExtraHeaders, *extraheader)
extraheader = nil
}
}
if !message {
original_line := line
line = bytes.TrimSpace(line)
if len(line) == 0 {
message = true
@@ -261,6 +319,13 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
case headerpgp:
c.PGPSignature += string(data) + "\n"
pgpsig = true
default:
h, maybecontinued := parseExtraHeader(original_line)
if maybecontinued {
extraheader = &h
} else {
c.ExtraHeaders = append(c.ExtraHeaders, h)
}
}
} else {
msgbuf.Write(line)
@@ -341,6 +406,13 @@ func (c *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) {
}
}
for _, header := range c.ExtraHeaders {
if _, err = fmt.Fprintf(w, "\n%s", header); err != nil {
return err
}
}
if c.PGPSignature != "" && includeSig {
if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil {
return err
@@ -262,9 +262,8 @@ func decodeShallow(p *advRefsDecoder) decoderStateFn {
p.line = bytes.TrimPrefix(p.line, shallow)
if len(p.line) != hashSize {
p.error(fmt.Sprintf(
"malformed shallow hash: wrong length, expected 40 bytes, read %d bytes",
len(p.line)))
p.error("malformed shallow hash: wrong length, expected 40 bytes, read %d bytes",
len(p.line))
return nil
}
@@ -62,7 +62,7 @@ func (req *ReferenceUpdateRequest) encodeCommands(e *pktline.Encoder,
}
for _, cmd := range cmds[1:] {
if err := e.Encodef(formatCommand(cmd)); err != nil {
if err := e.Encodef("%s", formatCommand(cmd)); err != nil {
return err
}
}
+10 -2
View File
@@ -113,9 +113,17 @@ type Endpoint struct {
Port int
// Path is the repository path.
Path string
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if Protocol is HTTPS.
InsecureSkipTLS bool
// CaBundle specify additional ca bundle with system cert pool
// ClientCert specifies an optional client certificate to use for mutual
// TLS authentication if Protocol is HTTPS.
ClientCert []byte
// ClientKey specifies an optional client key to use for mutual TLS
// authentication if Protocol is HTTPS.
ClientKey []byte
// CaBundle specifies an optional CA bundle to use for SSL verification
// if Protocol is HTTPS. The bundle is added in addition to the system
// CA bundle.
CaBundle []byte
// Proxy provides info required for connecting to a proxy.
Proxy ProxyOptions
+28 -4
View File
@@ -15,12 +15,13 @@ import (
"strings"
"sync"
"github.com/golang/groupcache/lru"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/utils/ioutil"
"github.com/golang/groupcache/lru"
)
// it requires a bytes.Buffer, because we need to know the length
@@ -185,6 +186,18 @@ func transportWithInsecureTLS(transport *http.Transport) {
transport.TLSClientConfig.InsecureSkipVerify = true
}
func transportWithClientCert(transport *http.Transport, cert, key []byte) error {
keyPair, err := tls.X509KeyPair(cert, key)
if err != nil {
return err
}
if transport.TLSClientConfig == nil {
transport.TLSClientConfig = &tls.Config{}
}
transport.TLSClientConfig.Certificates = []tls.Certificate{keyPair}
return nil
}
func transportWithCABundle(transport *http.Transport, caBundle []byte) error {
rootCAs, err := x509.SystemCertPool()
if err != nil {
@@ -206,6 +219,11 @@ func transportWithProxy(transport *http.Transport, proxyURL *url.URL) {
}
func configureTransport(transport *http.Transport, ep *transport.Endpoint) error {
if len(ep.ClientCert) > 0 && len(ep.ClientKey) > 0 {
if err := transportWithClientCert(transport, ep.ClientCert, ep.ClientKey); err != nil {
return err
}
}
if len(ep.CaBundle) > 0 {
if err := transportWithCABundle(transport, ep.CaBundle); err != nil {
return err
@@ -230,7 +248,7 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
// We need to configure the http transport if there are transport specific
// options present in the endpoint.
if len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
if len(ep.ClientKey) > 0 || len(ep.ClientCert) > 0 || len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
var transport *http.Transport
// if the client wasn't configured to have a cache for transports then just configure
// the transport and use it directly, otherwise try to use the cache.
@@ -242,9 +260,13 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
}
transport = tr.Clone()
configureTransport(transport, ep)
if err := configureTransport(transport, ep); err != nil {
return nil, err
}
} else {
transportOpts := transportOptions{
clientCert: string(ep.ClientCert),
clientKey: string(ep.ClientKey),
caBundle: string(ep.CaBundle),
insecureSkipTLS: ep.InsecureSkipTLS,
}
@@ -260,7 +282,9 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
if !found {
transport = c.client.Transport.(*http.Transport).Clone()
configureTransport(transport, ep)
if err := configureTransport(transport, ep); err != nil {
return nil, err
}
c.addTransport(transportOpts, transport)
}
}
+4 -2
View File
@@ -9,8 +9,10 @@ import (
type transportOptions struct {
insecureSkipTLS bool
// []byte is not comparable.
caBundle string
proxyURL url.URL
clientCert string
clientKey string
caBundle string
proxyURL url.URL
}
func (c *client) addTransport(opts transportOptions, transport *http.Transport) {
+58 -18
View File
@@ -54,7 +54,7 @@ func (a *KeyboardInteractive) String() string {
}
func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{
a.Challenge,
@@ -78,7 +78,7 @@ func (a *Password) String() string {
}
func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
})
@@ -101,7 +101,7 @@ func (a *PasswordCallback) String() string {
}
func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
})
@@ -150,7 +150,7 @@ func (a *PublicKeys) String() string {
}
func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
})
@@ -211,7 +211,7 @@ func (a *PublicKeysCallback) String() string {
}
func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
})
@@ -230,11 +230,26 @@ func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
// ~/.ssh/known_hosts
// /etc/ssh/ssh_known_hosts
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
kh, err := newKnownHosts(files...)
return ssh.HostKeyCallback(kh), err
kh, err := NewKnownHostsDb(files...)
if err != nil {
return nil, err
}
return kh.HostKeyCallback(), nil
}
func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
// NewKnownHostsDb returns knownhosts.HostKeyDB based on a file based on a
// known_hosts file. http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
//
// If list of files is empty, then it will be read from the SSH_KNOWN_HOSTS
// environment variable, example:
//
// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
//
// If SSH_KNOWN_HOSTS is not set the following file locations will be used:
//
// ~/.ssh/known_hosts
// /etc/ssh/ssh_known_hosts
func NewKnownHostsDb(files ...string) (*knownhosts.HostKeyDB, error) {
var err error
if len(files) == 0 {
@@ -247,7 +262,7 @@ func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
return nil, err
}
return knownhosts.New(files...)
return knownhosts.NewDB(files...)
}
func getDefaultKnownHostsFiles() ([]string, error) {
@@ -289,25 +304,50 @@ func filterKnownHostsFiles(files ...string) ([]string, error) {
}
// HostKeyCallbackHelper is a helper that provides common functionality to
// configure HostKeyCallback into a ssh.ClientConfig.
// configure HostKeyCallback and HostKeyAlgorithms into a ssh.ClientConfig.
type HostKeyCallbackHelper struct {
// HostKeyCallback is the function type used for verifying server keys.
// If nil default callback will be create using NewKnownHostsCallback
// If nil, a default callback will be created using NewKnownHostsDb
// without argument.
HostKeyCallback ssh.HostKeyCallback
// HostKeyAlgorithms is a list of supported host key algorithms that will
// be used for host key verification.
HostKeyAlgorithms []string
// fallback allows for injecting the fallback call, which is called
// when a HostKeyCallback is not set.
fallback func(files ...string) (ssh.HostKeyCallback, error)
}
// SetHostKeyCallback sets the field HostKeyCallback in the given cfg. If
// HostKeyCallback is empty a default callback is created using
// NewKnownHostsCallback.
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
var err error
// SetHostKeyCallbackAndAlgorithms sets the field HostKeyCallback and HostKeyAlgorithms in the given cfg.
// If the host key callback or algorithms is empty it is left empty. It will be handled by the dial method,
// falling back to knownhosts.
func (m *HostKeyCallbackHelper) SetHostKeyCallbackAndAlgorithms(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
if cfg == nil {
cfg = &ssh.ClientConfig{}
}
if m.HostKeyCallback == nil {
if m.HostKeyCallback, err = NewKnownHostsCallback(); err != nil {
return cfg, err
if m.fallback == nil {
m.fallback = NewKnownHostsCallback
}
hkcb, err := m.fallback()
if err != nil {
return nil, fmt.Errorf("cannot create known hosts callback: %w", err)
}
cfg.HostKeyCallback = hkcb
cfg.HostKeyAlgorithms = m.HostKeyAlgorithms
return cfg, err
}
cfg.HostKeyCallback = m.HostKeyCallback
cfg.HostKeyAlgorithms = m.HostKeyAlgorithms
return cfg, nil
}
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
return m.SetHostKeyCallbackAndAlgorithms(cfg)
}
+8 -9
View File
@@ -11,7 +11,6 @@ import (
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/internal/common"
"github.com/skeema/knownhosts"
"github.com/kevinburke/ssh_config"
"golang.org/x/crypto/ssh"
@@ -127,17 +126,17 @@ func (c *command) connect() error {
}
hostWithPort := c.getHostWithPort()
if config.HostKeyCallback == nil {
kh, err := newKnownHosts()
db, err := NewKnownHostsDb()
if err != nil {
return err
}
config.HostKeyCallback = kh.HostKeyCallback()
config.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
} else if len(config.HostKeyAlgorithms) == 0 {
// Set the HostKeyAlgorithms based on HostKeyCallback.
// For background see https://github.com/go-git/go-git/issues/411 as well as
// https://github.com/golang/go/issues/29286 for root cause.
config.HostKeyAlgorithms = knownhosts.HostKeyAlgorithms(config.HostKeyCallback, hostWithPort)
config.HostKeyCallback = db.HostKeyCallback()
config.HostKeyAlgorithms = db.HostKeyAlgorithms(hostWithPort)
} else {
// If the user gave a custom HostKeyCallback, we do not try to detect host key algorithms
// based on knownhosts functionality, as the user may be requesting a FixedKey or using a
// different key approval strategy. In that case, the user is responsible for populating
// HostKeyAlgorithms appropriately
}
overrideConfig(c.config, config)
+11 -9
View File
@@ -114,7 +114,7 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
o.RemoteURL = r.c.URLs[len(r.c.URLs)-1]
}
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
if err != nil {
return err
}
@@ -416,7 +416,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
o.RemoteURL = r.c.URLs[0]
}
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
if err != nil {
return nil, err
}
@@ -532,8 +532,8 @@ func depthChanged(before []plumbing.Hash, s storage.Storer) (bool, error) {
return false, nil
}
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
if err != nil {
return nil, err
}
@@ -541,8 +541,8 @@ func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool,
return c.NewUploadPackSession(ep, auth)
}
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
if err != nil {
return nil, err
}
@@ -550,13 +550,15 @@ func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, ca
return c.NewReceivePackSession(ep, auth)
}
func newClient(url string, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
func newClient(url string, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
ep, err := transport.NewEndpoint(url)
if err != nil {
return nil, nil, err
}
ep.InsecureSkipTLS = insecure
ep.CaBundle = cabundle
ep.ClientCert = clientCert
ep.ClientKey = clientKey
ep.CaBundle = caBundle
ep.Proxy = proxyOpts
c, err := client.NewClient(ep)
@@ -1356,7 +1358,7 @@ func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Refe
return nil, ErrEmptyUrls
}
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
if err != nil {
return nil, err
}
+3
View File
@@ -19,6 +19,7 @@ import (
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-billy/v5/util"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/internal/path_util"
"github.com/go-git/go-git/v5/internal/revision"
@@ -930,6 +931,8 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
Tags: o.Tags,
RemoteName: o.RemoteName,
InsecureSkipTLS: o.InsecureSkipTLS,
ClientCert: o.ClientCert,
ClientKey: o.ClientKey,
CABundle: o.CABundle,
ProxyOptions: o.ProxyOptions,
}, o.ReferenceName)
+18 -9
View File
@@ -2,6 +2,8 @@ package filesystem
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"sync"
@@ -87,6 +89,11 @@ func (s *ObjectStorage) loadIdxFile(h plumbing.Hash) (err error) {
return err
}
if !bytes.Equal(idxf.PackfileChecksum[:], h[:]) {
return fmt.Errorf("%w: packfile mismatch: target is %q not %q",
idxfile.ErrMalformedIdxFile, hex.EncodeToString(idxf.PackfileChecksum[:]), h.String())
}
s.index[h] = idxf
return err
}
@@ -186,7 +193,8 @@ func (s *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) {
}
func (s *ObjectStorage) encodedObjectSizeFromUnpacked(h plumbing.Hash) (
size int64, err error) {
size int64, err error,
) {
f, err := s.dir.Object(h)
if err != nil {
if os.IsNotExist(err) {
@@ -274,7 +282,8 @@ func (s *ObjectStorage) storePackfileInCache(hash plumbing.Hash, p *packfile.Pac
}
func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
size int64, err error) {
size int64, err error,
) {
if err := s.requireIndex(); err != nil {
return 0, err
}
@@ -310,7 +319,8 @@ func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
// EncodedObjectSize returns the plaintext size of the given object,
// without actually reading the full object data from storage.
func (s *ObjectStorage) EncodedObjectSize(h plumbing.Hash) (
size int64, err error) {
size int64, err error,
) {
size, err = s.encodedObjectSizeFromUnpacked(h)
if err != nil && err != plumbing.ErrObjectNotFound {
return 0, err
@@ -371,7 +381,8 @@ func (s *ObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (p
// DeltaObject returns the object with the given hash, by searching for
// it in the packfile and the git object directories.
func (s *ObjectStorage) DeltaObject(t plumbing.ObjectType,
h plumbing.Hash) (plumbing.EncodedObject, error) {
h plumbing.Hash,
) (plumbing.EncodedObject, error) {
obj, err := s.getFromUnpacked(h)
if err == plumbing.ErrObjectNotFound {
obj, err = s.getFromPackfile(h, true)
@@ -451,8 +462,8 @@ var copyBufferPool = sync.Pool{
// Get returns the object with the given hash, by searching for it in
// the packfile.
func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) (
plumbing.EncodedObject, error) {
plumbing.EncodedObject, error,
) {
if err := s.requireIndex(); err != nil {
return nil, err
}
@@ -509,9 +520,7 @@ func (s *ObjectStorage) decodeDeltaObjectAt(
return nil, err
}
var (
base plumbing.Hash
)
var base plumbing.Hash
switch header.Type {
case plumbing.REFDeltaObject:
+4 -2
View File
@@ -131,7 +131,9 @@ func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
}
if !root.IsDir() {
l.Add(ctor(root))
if !root.Skip() {
l.Add(ctor(root))
}
return nil
}
@@ -148,7 +150,7 @@ func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
}
return err
}
if current.IsDir() {
if current.IsDir() || current.Skip() {
continue
}
l.Add(ctor(current))
+20 -23
View File
@@ -297,18 +297,16 @@ func DiffTreeContext(ctx context.Context, fromTree, toTree noder.Noder,
case noMoreNoders:
return ret, nil
case onlyFromRemains:
if err = ret.AddRecursiveDelete(from); err != nil {
return nil, err
if !from.Skip() {
if err = ret.AddRecursiveDelete(from); err != nil {
return nil, err
}
}
if err = ii.nextFrom(); err != nil {
return nil, err
}
case onlyToRemains:
if to.Skip() {
if err = ret.AddRecursiveDelete(to); err != nil {
return nil, err
}
} else {
if !to.Skip() {
if err = ret.AddRecursiveInsert(to); err != nil {
return nil, err
}
@@ -317,26 +315,25 @@ func DiffTreeContext(ctx context.Context, fromTree, toTree noder.Noder,
return nil, err
}
case bothHaveNodes:
if from.Skip() {
if err = ret.AddRecursiveDelete(from); err != nil {
return nil, err
var err error
switch {
case from.Skip():
if from.Name() == to.Name() {
err = ii.nextBoth()
} else {
err = ii.nextFrom()
}
if err := ii.nextBoth(); err != nil {
return nil, err
case to.Skip():
if from.Name() == to.Name() {
err = ii.nextBoth()
} else {
err = ii.nextTo()
}
break
}
if to.Skip() {
if err = ret.AddRecursiveDelete(to); err != nil {
return nil, err
}
if err := ii.nextBoth(); err != nil {
return nil, err
}
break
default:
err = diffNodes(&ret, ii)
}
if err = diffNodes(&ret, ii); err != nil {
if err != nil {
return nil, err
}
default:
+9 -1
View File
@@ -36,7 +36,15 @@ func NewRootNode(idx *index.Index) noder.Noder {
parent := fullpath
fullpath = path.Join(fullpath, part)
if _, ok := m[fullpath]; ok {
// It's possible that the first occurrence of subdirectory is skipped.
// The parent node can be created with SkipWorktree set to true, but
// if any future children do not skip their subtree, the entire lineage
// of the tree needs to have this value set to false so that subdirectories
// are not ignored.
if parentNode, ok := m[fullpath]; ok {
if e.SkipWorktree == false {
parentNode.skip = false
}
continue
}
+20 -8
View File
@@ -12,6 +12,7 @@ import (
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/util"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
@@ -79,6 +80,8 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
Progress: o.Progress,
Force: o.Force,
InsecureSkipTLS: o.InsecureSkipTLS,
ClientCert: o.ClientCert,
ClientKey: o.ClientKey,
CABundle: o.CABundle,
ProxyOptions: o.ProxyOptions,
})
@@ -307,13 +310,20 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error {
return err
}
var removedFiles []string
if opts.Mode == MixedReset || opts.Mode == MergeReset || opts.Mode == HardReset {
if err := w.resetIndex(t, dirs, opts.Files); err != nil {
if removedFiles, err = w.resetIndex(t, dirs, opts.Files); err != nil {
return err
}
}
if opts.Mode == MergeReset || opts.Mode == HardReset {
if opts.Mode == MergeReset && len(removedFiles) > 0 {
if err := w.resetWorktree(t, removedFiles); err != nil {
return err
}
}
if opts.Mode == HardReset {
if err := w.resetWorktree(t, opts.Files); err != nil {
return err
}
@@ -362,23 +372,24 @@ func (w *Worktree) Reset(opts *ResetOptions) error {
return w.ResetSparsely(opts, nil)
}
func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) error {
func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) ([]string, error) {
idx, err := w.r.Storer.Index()
if err != nil {
return err
return nil, err
}
b := newIndexBuilder(idx)
changes, err := w.diffTreeWithStaging(t, true)
if err != nil {
return err
return nil, err
}
var removedFiles []string
for _, ch := range changes {
a, err := ch.Action()
if err != nil {
return err
return nil, err
}
var name string
@@ -389,7 +400,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
name = ch.To.String()
e, err = t.FindEntry(name)
if err != nil {
return err
return nil, err
}
case merkletrie.Delete:
name = ch.From.String()
@@ -403,6 +414,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
}
b.Remove(name)
removedFiles = append(removedFiles, name)
if e == nil {
continue
}
@@ -421,7 +433,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
idx.SkipUnless(dirs)
}
return w.r.Storer.SetIndex(idx)
return removedFiles, w.r.Storer.SetIndex(idx)
}
func inFiles(files []string, v string) bool {