Exclude vendor dirs from git CRLF normalization (#10911)

* Exclude vendor dirs from git CRLF normalization

Should get rid of a few warnings like at the end of `lint-backend` like
https://drone.gitea.io/go-gitea/gitea/23117/1/4

* make vendor

Co-authored-by: John Olheiser <john.olheiser@gmail.com>
Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
silverwind 2020-04-01 08:33:49 +02:00 committed by GitHub
parent 8d99ee2773
commit 848502d04c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 1003 additions and 1000 deletions

3
.gitattributes vendored
View file

@ -1,4 +1,7 @@
* text=auto eol=lf * text=auto eol=lf
/vendor/** -text -eol
/public/vendor/** -text -eol
conf/* linguist-vendored conf/* linguist-vendored
docker/* linguist-vendored docker/* linguist-vendored
options/* linguist-vendored options/* linguist-vendored

View file

@ -1,35 +1,35 @@
package commitgraph package commitgraph
import ( import (
"time" "time"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
) )
// CommitData is a reduced representation of Commit as presented in the commit graph // CommitData is a reduced representation of Commit as presented in the commit graph
// file. It is merely useful as an optimization for walking the commit graphs. // file. It is merely useful as an optimization for walking the commit graphs.
type CommitData struct { type CommitData struct {
// TreeHash is the hash of the root tree of the commit. // TreeHash is the hash of the root tree of the commit.
TreeHash plumbing.Hash TreeHash plumbing.Hash
// ParentIndexes are the indexes of the parent commits of the commit. // ParentIndexes are the indexes of the parent commits of the commit.
ParentIndexes []int ParentIndexes []int
// ParentHashes are the hashes of the parent commits of the commit. // ParentHashes are the hashes of the parent commits of the commit.
ParentHashes []plumbing.Hash ParentHashes []plumbing.Hash
// Generation number is the pre-computed generation in the commit graph // Generation number is the pre-computed generation in the commit graph
// or zero if not available // or zero if not available
Generation int Generation int
// When is the timestamp of the commit. // When is the timestamp of the commit.
When time.Time When time.Time
} }
// Index represents a representation of commit graph that allows indexed // Index represents a representation of commit graph that allows indexed
// access to the nodes using commit object hash // access to the nodes using commit object hash
type Index interface { type Index interface {
// GetIndexByHash gets the index in the commit graph from commit hash, if available // GetIndexByHash gets the index in the commit graph from commit hash, if available
GetIndexByHash(h plumbing.Hash) (int, error) GetIndexByHash(h plumbing.Hash) (int, error)
// GetNodeByIndex gets the commit node from the commit graph using index // GetNodeByIndex gets the commit node from the commit graph using index
// obtained from child node, if available // obtained from child node, if available
GetCommitDataByIndex(i int) (*CommitData, error) GetCommitDataByIndex(i int) (*CommitData, error)
// Hashes returns all the hashes that are available in the index // Hashes returns all the hashes that are available in the index
Hashes() []plumbing.Hash Hashes() []plumbing.Hash
} }

View file

@ -1,188 +1,188 @@
package commitgraph package commitgraph
import ( import (
"crypto/sha1" "crypto/sha1"
"hash" "hash"
"io" "io"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/utils/binary" "github.com/go-git/go-git/v5/utils/binary"
) )
// Encoder writes MemoryIndex structs to an output stream. // Encoder writes MemoryIndex structs to an output stream.
type Encoder struct { type Encoder struct {
io.Writer io.Writer
hash hash.Hash hash hash.Hash
} }
// NewEncoder returns a new stream encoder that writes to w. // NewEncoder returns a new stream encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder { func NewEncoder(w io.Writer) *Encoder {
h := sha1.New() h := sha1.New()
mw := io.MultiWriter(w, h) mw := io.MultiWriter(w, h)
return &Encoder{mw, h} return &Encoder{mw, h}
} }
// Encode writes an index into the commit-graph file // Encode writes an index into the commit-graph file
func (e *Encoder) Encode(idx Index) error { func (e *Encoder) Encode(idx Index) error {
// Get all the hashes in the input index // Get all the hashes in the input index
hashes := idx.Hashes() hashes := idx.Hashes()
// Sort the inout and prepare helper structures we'll need for encoding // Sort the inout and prepare helper structures we'll need for encoding
hashToIndex, fanout, extraEdgesCount := e.prepare(idx, hashes) hashToIndex, fanout, extraEdgesCount := e.prepare(idx, hashes)
chunkSignatures := [][]byte{oidFanoutSignature, oidLookupSignature, commitDataSignature} chunkSignatures := [][]byte{oidFanoutSignature, oidLookupSignature, commitDataSignature}
chunkSizes := []uint64{4 * 256, uint64(len(hashes)) * 20, uint64(len(hashes)) * 36} chunkSizes := []uint64{4 * 256, uint64(len(hashes)) * 20, uint64(len(hashes)) * 36}
if extraEdgesCount > 0 { if extraEdgesCount > 0 {
chunkSignatures = append(chunkSignatures, extraEdgeListSignature) chunkSignatures = append(chunkSignatures, extraEdgeListSignature)
chunkSizes = append(chunkSizes, uint64(extraEdgesCount)*4) chunkSizes = append(chunkSizes, uint64(extraEdgesCount)*4)
} }
if err := e.encodeFileHeader(len(chunkSignatures)); err != nil { if err := e.encodeFileHeader(len(chunkSignatures)); err != nil {
return err return err
} }
if err := e.encodeChunkHeaders(chunkSignatures, chunkSizes); err != nil { if err := e.encodeChunkHeaders(chunkSignatures, chunkSizes); err != nil {
return err return err
} }
if err := e.encodeFanout(fanout); err != nil { if err := e.encodeFanout(fanout); err != nil {
return err return err
} }
if err := e.encodeOidLookup(hashes); err != nil { if err := e.encodeOidLookup(hashes); err != nil {
return err return err
} }
if extraEdges, err := e.encodeCommitData(hashes, hashToIndex, idx); err == nil { if extraEdges, err := e.encodeCommitData(hashes, hashToIndex, idx); err == nil {
if err = e.encodeExtraEdges(extraEdges); err != nil { if err = e.encodeExtraEdges(extraEdges); err != nil {
return err return err
} }
} else { } else {
return err return err
} }
return e.encodeChecksum() return e.encodeChecksum()
} }
func (e *Encoder) prepare(idx Index, hashes []plumbing.Hash) (hashToIndex map[plumbing.Hash]uint32, fanout []uint32, extraEdgesCount uint32) { func (e *Encoder) prepare(idx Index, hashes []plumbing.Hash) (hashToIndex map[plumbing.Hash]uint32, fanout []uint32, extraEdgesCount uint32) {
// Sort the hashes and build our index // Sort the hashes and build our index
plumbing.HashesSort(hashes) plumbing.HashesSort(hashes)
hashToIndex = make(map[plumbing.Hash]uint32) hashToIndex = make(map[plumbing.Hash]uint32)
fanout = make([]uint32, 256) fanout = make([]uint32, 256)
for i, hash := range hashes { for i, hash := range hashes {
hashToIndex[hash] = uint32(i) hashToIndex[hash] = uint32(i)
fanout[hash[0]]++ fanout[hash[0]]++
} }
// Convert the fanout to cumulative values // Convert the fanout to cumulative values
for i := 1; i <= 0xff; i++ { for i := 1; i <= 0xff; i++ {
fanout[i] += fanout[i-1] fanout[i] += fanout[i-1]
} }
// Find out if we will need extra edge table // Find out if we will need extra edge table
for i := 0; i < len(hashes); i++ { for i := 0; i < len(hashes); i++ {
v, _ := idx.GetCommitDataByIndex(i) v, _ := idx.GetCommitDataByIndex(i)
if len(v.ParentHashes) > 2 { if len(v.ParentHashes) > 2 {
extraEdgesCount += uint32(len(v.ParentHashes) - 1) extraEdgesCount += uint32(len(v.ParentHashes) - 1)
break break
} }
} }
return return
} }
func (e *Encoder) encodeFileHeader(chunkCount int) (err error) { func (e *Encoder) encodeFileHeader(chunkCount int) (err error) {
if _, err = e.Write(commitFileSignature); err == nil { if _, err = e.Write(commitFileSignature); err == nil {
_, err = e.Write([]byte{1, 1, byte(chunkCount), 0}) _, err = e.Write([]byte{1, 1, byte(chunkCount), 0})
} }
return return
} }
func (e *Encoder) encodeChunkHeaders(chunkSignatures [][]byte, chunkSizes []uint64) (err error) { func (e *Encoder) encodeChunkHeaders(chunkSignatures [][]byte, chunkSizes []uint64) (err error) {
// 8 bytes of file header, 12 bytes for each chunk header and 12 byte for terminator // 8 bytes of file header, 12 bytes for each chunk header and 12 byte for terminator
offset := uint64(8 + len(chunkSignatures)*12 + 12) offset := uint64(8 + len(chunkSignatures)*12 + 12)
for i, signature := range chunkSignatures { for i, signature := range chunkSignatures {
if _, err = e.Write(signature); err == nil { if _, err = e.Write(signature); err == nil {
err = binary.WriteUint64(e, offset) err = binary.WriteUint64(e, offset)
} }
if err != nil { if err != nil {
return return
} }
offset += chunkSizes[i] offset += chunkSizes[i]
} }
if _, err = e.Write(lastSignature); err == nil { if _, err = e.Write(lastSignature); err == nil {
err = binary.WriteUint64(e, offset) err = binary.WriteUint64(e, offset)
} }
return return
} }
func (e *Encoder) encodeFanout(fanout []uint32) (err error) { func (e *Encoder) encodeFanout(fanout []uint32) (err error) {
for i := 0; i <= 0xff; i++ { for i := 0; i <= 0xff; i++ {
if err = binary.WriteUint32(e, fanout[i]); err != nil { if err = binary.WriteUint32(e, fanout[i]); err != nil {
return return
} }
} }
return return
} }
func (e *Encoder) encodeOidLookup(hashes []plumbing.Hash) (err error) { func (e *Encoder) encodeOidLookup(hashes []plumbing.Hash) (err error) {
for _, hash := range hashes { for _, hash := range hashes {
if _, err = e.Write(hash[:]); err != nil { if _, err = e.Write(hash[:]); err != nil {
return err return err
} }
} }
return return
} }
func (e *Encoder) encodeCommitData(hashes []plumbing.Hash, hashToIndex map[plumbing.Hash]uint32, idx Index) (extraEdges []uint32, err error) { func (e *Encoder) encodeCommitData(hashes []plumbing.Hash, hashToIndex map[plumbing.Hash]uint32, idx Index) (extraEdges []uint32, err error) {
for _, hash := range hashes { for _, hash := range hashes {
origIndex, _ := idx.GetIndexByHash(hash) origIndex, _ := idx.GetIndexByHash(hash)
commitData, _ := idx.GetCommitDataByIndex(origIndex) commitData, _ := idx.GetCommitDataByIndex(origIndex)
if _, err = e.Write(commitData.TreeHash[:]); err != nil { if _, err = e.Write(commitData.TreeHash[:]); err != nil {
return return
} }
var parent1, parent2 uint32 var parent1, parent2 uint32
if len(commitData.ParentHashes) == 0 { if len(commitData.ParentHashes) == 0 {
parent1 = parentNone parent1 = parentNone
parent2 = parentNone parent2 = parentNone
} else if len(commitData.ParentHashes) == 1 { } else if len(commitData.ParentHashes) == 1 {
parent1 = hashToIndex[commitData.ParentHashes[0]] parent1 = hashToIndex[commitData.ParentHashes[0]]
parent2 = parentNone parent2 = parentNone
} else if len(commitData.ParentHashes) == 2 { } else if len(commitData.ParentHashes) == 2 {
parent1 = hashToIndex[commitData.ParentHashes[0]] parent1 = hashToIndex[commitData.ParentHashes[0]]
parent2 = hashToIndex[commitData.ParentHashes[1]] parent2 = hashToIndex[commitData.ParentHashes[1]]
} else if len(commitData.ParentHashes) > 2 { } else if len(commitData.ParentHashes) > 2 {
parent1 = hashToIndex[commitData.ParentHashes[0]] parent1 = hashToIndex[commitData.ParentHashes[0]]
parent2 = uint32(len(extraEdges)) | parentOctopusUsed parent2 = uint32(len(extraEdges)) | parentOctopusUsed
for _, parentHash := range commitData.ParentHashes[1:] { for _, parentHash := range commitData.ParentHashes[1:] {
extraEdges = append(extraEdges, hashToIndex[parentHash]) extraEdges = append(extraEdges, hashToIndex[parentHash])
} }
extraEdges[len(extraEdges)-1] |= parentLast extraEdges[len(extraEdges)-1] |= parentLast
} }
if err = binary.WriteUint32(e, parent1); err == nil { if err = binary.WriteUint32(e, parent1); err == nil {
err = binary.WriteUint32(e, parent2) err = binary.WriteUint32(e, parent2)
} }
if err != nil { if err != nil {
return return
} }
unixTime := uint64(commitData.When.Unix()) unixTime := uint64(commitData.When.Unix())
unixTime |= uint64(commitData.Generation) << 34 unixTime |= uint64(commitData.Generation) << 34
if err = binary.WriteUint64(e, unixTime); err != nil { if err = binary.WriteUint64(e, unixTime); err != nil {
return return
} }
} }
return return
} }
func (e *Encoder) encodeExtraEdges(extraEdges []uint32) (err error) { func (e *Encoder) encodeExtraEdges(extraEdges []uint32) (err error) {
for _, parent := range extraEdges { for _, parent := range extraEdges {
if err = binary.WriteUint32(e, parent); err != nil { if err = binary.WriteUint32(e, parent); err != nil {
return return
} }
} }
return return
} }
func (e *Encoder) encodeChecksum() error { func (e *Encoder) encodeChecksum() error {
_, err := e.Write(e.hash.Sum(nil)[:20]) _, err := e.Write(e.hash.Sum(nil)[:20])
return err return err
} }

View file

@ -1,259 +1,259 @@
package commitgraph package commitgraph
import ( import (
"bytes" "bytes"
encbin "encoding/binary" encbin "encoding/binary"
"errors" "errors"
"io" "io"
"time" "time"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/utils/binary" "github.com/go-git/go-git/v5/utils/binary"
) )
var ( var (
// ErrUnsupportedVersion is returned by OpenFileIndex when the commit graph // ErrUnsupportedVersion is returned by OpenFileIndex when the commit graph
// file version is not supported. // file version is not supported.
ErrUnsupportedVersion = errors.New("Unsupported version") ErrUnsupportedVersion = errors.New("Unsupported version")
// ErrUnsupportedHash is returned by OpenFileIndex when the commit graph // ErrUnsupportedHash is returned by OpenFileIndex when the commit graph
// hash function is not supported. Currently only SHA-1 is defined and // hash function is not supported. Currently only SHA-1 is defined and
// supported // supported
ErrUnsupportedHash = errors.New("Unsupported hash algorithm") ErrUnsupportedHash = errors.New("Unsupported hash algorithm")
// ErrMalformedCommitGraphFile is returned by OpenFileIndex when the commit // ErrMalformedCommitGraphFile is returned by OpenFileIndex when the commit
// graph file is corrupted. // graph file is corrupted.
ErrMalformedCommitGraphFile = errors.New("Malformed commit graph file") ErrMalformedCommitGraphFile = errors.New("Malformed commit graph file")
commitFileSignature = []byte{'C', 'G', 'P', 'H'} commitFileSignature = []byte{'C', 'G', 'P', 'H'}
oidFanoutSignature = []byte{'O', 'I', 'D', 'F'} oidFanoutSignature = []byte{'O', 'I', 'D', 'F'}
oidLookupSignature = []byte{'O', 'I', 'D', 'L'} oidLookupSignature = []byte{'O', 'I', 'D', 'L'}
commitDataSignature = []byte{'C', 'D', 'A', 'T'} commitDataSignature = []byte{'C', 'D', 'A', 'T'}
extraEdgeListSignature = []byte{'E', 'D', 'G', 'E'} extraEdgeListSignature = []byte{'E', 'D', 'G', 'E'}
lastSignature = []byte{0, 0, 0, 0} lastSignature = []byte{0, 0, 0, 0}
parentNone = uint32(0x70000000) parentNone = uint32(0x70000000)
parentOctopusUsed = uint32(0x80000000) parentOctopusUsed = uint32(0x80000000)
parentOctopusMask = uint32(0x7fffffff) parentOctopusMask = uint32(0x7fffffff)
parentLast = uint32(0x80000000) parentLast = uint32(0x80000000)
) )
type fileIndex struct { type fileIndex struct {
reader io.ReaderAt reader io.ReaderAt
fanout [256]int fanout [256]int
oidFanoutOffset int64 oidFanoutOffset int64
oidLookupOffset int64 oidLookupOffset int64
commitDataOffset int64 commitDataOffset int64
extraEdgeListOffset int64 extraEdgeListOffset int64
} }
// OpenFileIndex opens a serialized commit graph file in the format described at // OpenFileIndex opens a serialized commit graph file in the format described at
// https://github.com/git/git/blob/master/Documentation/technical/commit-graph-format.txt // https://github.com/git/git/blob/master/Documentation/technical/commit-graph-format.txt
func OpenFileIndex(reader io.ReaderAt) (Index, error) { func OpenFileIndex(reader io.ReaderAt) (Index, error) {
fi := &fileIndex{reader: reader} fi := &fileIndex{reader: reader}
if err := fi.verifyFileHeader(); err != nil { if err := fi.verifyFileHeader(); err != nil {
return nil, err return nil, err
} }
if err := fi.readChunkHeaders(); err != nil { if err := fi.readChunkHeaders(); err != nil {
return nil, err return nil, err
} }
if err := fi.readFanout(); err != nil { if err := fi.readFanout(); err != nil {
return nil, err return nil, err
} }
return fi, nil return fi, nil
} }
func (fi *fileIndex) verifyFileHeader() error { func (fi *fileIndex) verifyFileHeader() error {
// Verify file signature // Verify file signature
var signature = make([]byte, 4) var signature = make([]byte, 4)
if _, err := fi.reader.ReadAt(signature, 0); err != nil { if _, err := fi.reader.ReadAt(signature, 0); err != nil {
return err return err
} }
if !bytes.Equal(signature, commitFileSignature) { if !bytes.Equal(signature, commitFileSignature) {
return ErrMalformedCommitGraphFile return ErrMalformedCommitGraphFile
} }
// Read and verify the file header // Read and verify the file header
var header = make([]byte, 4) var header = make([]byte, 4)
if _, err := fi.reader.ReadAt(header, 4); err != nil { if _, err := fi.reader.ReadAt(header, 4); err != nil {
return err return err
} }
if header[0] != 1 { if header[0] != 1 {
return ErrUnsupportedVersion return ErrUnsupportedVersion
} }
if header[1] != 1 { if header[1] != 1 {
return ErrUnsupportedHash return ErrUnsupportedHash
} }
return nil return nil
} }
func (fi *fileIndex) readChunkHeaders() error { func (fi *fileIndex) readChunkHeaders() error {
var chunkID = make([]byte, 4) var chunkID = make([]byte, 4)
for i := 0; ; i++ { for i := 0; ; i++ {
chunkHeader := io.NewSectionReader(fi.reader, 8+(int64(i)*12), 12) chunkHeader := io.NewSectionReader(fi.reader, 8+(int64(i)*12), 12)
if _, err := io.ReadAtLeast(chunkHeader, chunkID, 4); err != nil { if _, err := io.ReadAtLeast(chunkHeader, chunkID, 4); err != nil {
return err return err
} }
chunkOffset, err := binary.ReadUint64(chunkHeader) chunkOffset, err := binary.ReadUint64(chunkHeader)
if err != nil { if err != nil {
return err return err
} }
if bytes.Equal(chunkID, oidFanoutSignature) { if bytes.Equal(chunkID, oidFanoutSignature) {
fi.oidFanoutOffset = int64(chunkOffset) fi.oidFanoutOffset = int64(chunkOffset)
} else if bytes.Equal(chunkID, oidLookupSignature) { } else if bytes.Equal(chunkID, oidLookupSignature) {
fi.oidLookupOffset = int64(chunkOffset) fi.oidLookupOffset = int64(chunkOffset)
} else if bytes.Equal(chunkID, commitDataSignature) { } else if bytes.Equal(chunkID, commitDataSignature) {
fi.commitDataOffset = int64(chunkOffset) fi.commitDataOffset = int64(chunkOffset)
} else if bytes.Equal(chunkID, extraEdgeListSignature) { } else if bytes.Equal(chunkID, extraEdgeListSignature) {
fi.extraEdgeListOffset = int64(chunkOffset) fi.extraEdgeListOffset = int64(chunkOffset)
} else if bytes.Equal(chunkID, lastSignature) { } else if bytes.Equal(chunkID, lastSignature) {
break break
} }
} }
if fi.oidFanoutOffset <= 0 || fi.oidLookupOffset <= 0 || fi.commitDataOffset <= 0 { if fi.oidFanoutOffset <= 0 || fi.oidLookupOffset <= 0 || fi.commitDataOffset <= 0 {
return ErrMalformedCommitGraphFile return ErrMalformedCommitGraphFile
} }
return nil return nil
} }
func (fi *fileIndex) readFanout() error { func (fi *fileIndex) readFanout() error {
fanoutReader := io.NewSectionReader(fi.reader, fi.oidFanoutOffset, 256*4) fanoutReader := io.NewSectionReader(fi.reader, fi.oidFanoutOffset, 256*4)
for i := 0; i < 256; i++ { for i := 0; i < 256; i++ {
fanoutValue, err := binary.ReadUint32(fanoutReader) fanoutValue, err := binary.ReadUint32(fanoutReader)
if err != nil { if err != nil {
return err return err
} }
if fanoutValue > 0x7fffffff { if fanoutValue > 0x7fffffff {
return ErrMalformedCommitGraphFile return ErrMalformedCommitGraphFile
} }
fi.fanout[i] = int(fanoutValue) fi.fanout[i] = int(fanoutValue)
} }
return nil return nil
} }
func (fi *fileIndex) GetIndexByHash(h plumbing.Hash) (int, error) { func (fi *fileIndex) GetIndexByHash(h plumbing.Hash) (int, error) {
var oid plumbing.Hash var oid plumbing.Hash
// Find the hash in the oid lookup table // Find the hash in the oid lookup table
var low int var low int
if h[0] == 0 { if h[0] == 0 {
low = 0 low = 0
} else { } else {
low = fi.fanout[h[0]-1] low = fi.fanout[h[0]-1]
} }
high := fi.fanout[h[0]] high := fi.fanout[h[0]]
for low < high { for low < high {
mid := (low + high) >> 1 mid := (low + high) >> 1
offset := fi.oidLookupOffset + int64(mid)*20 offset := fi.oidLookupOffset + int64(mid)*20
if _, err := fi.reader.ReadAt(oid[:], offset); err != nil { if _, err := fi.reader.ReadAt(oid[:], offset); err != nil {
return 0, err return 0, err
} }
cmp := bytes.Compare(h[:], oid[:]) cmp := bytes.Compare(h[:], oid[:])
if cmp < 0 { if cmp < 0 {
high = mid high = mid
} else if cmp == 0 { } else if cmp == 0 {
return mid, nil return mid, nil
} else { } else {
low = mid + 1 low = mid + 1
} }
} }
return 0, plumbing.ErrObjectNotFound return 0, plumbing.ErrObjectNotFound
} }
func (fi *fileIndex) GetCommitDataByIndex(idx int) (*CommitData, error) { func (fi *fileIndex) GetCommitDataByIndex(idx int) (*CommitData, error) {
if idx >= fi.fanout[0xff] { if idx >= fi.fanout[0xff] {
return nil, plumbing.ErrObjectNotFound return nil, plumbing.ErrObjectNotFound
} }
offset := fi.commitDataOffset + int64(idx)*36 offset := fi.commitDataOffset + int64(idx)*36
commitDataReader := io.NewSectionReader(fi.reader, offset, 36) commitDataReader := io.NewSectionReader(fi.reader, offset, 36)
treeHash, err := binary.ReadHash(commitDataReader) treeHash, err := binary.ReadHash(commitDataReader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
parent1, err := binary.ReadUint32(commitDataReader) parent1, err := binary.ReadUint32(commitDataReader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
parent2, err := binary.ReadUint32(commitDataReader) parent2, err := binary.ReadUint32(commitDataReader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
genAndTime, err := binary.ReadUint64(commitDataReader) genAndTime, err := binary.ReadUint64(commitDataReader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var parentIndexes []int var parentIndexes []int
if parent2&parentOctopusUsed == parentOctopusUsed { if parent2&parentOctopusUsed == parentOctopusUsed {
// Octopus merge // Octopus merge
parentIndexes = []int{int(parent1 & parentOctopusMask)} parentIndexes = []int{int(parent1 & parentOctopusMask)}
offset := fi.extraEdgeListOffset + 4*int64(parent2&parentOctopusMask) offset := fi.extraEdgeListOffset + 4*int64(parent2&parentOctopusMask)
buf := make([]byte, 4) buf := make([]byte, 4)
for { for {
_, err := fi.reader.ReadAt(buf, offset) _, err := fi.reader.ReadAt(buf, offset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
parent := encbin.BigEndian.Uint32(buf) parent := encbin.BigEndian.Uint32(buf)
offset += 4 offset += 4
parentIndexes = append(parentIndexes, int(parent&parentOctopusMask)) parentIndexes = append(parentIndexes, int(parent&parentOctopusMask))
if parent&parentLast == parentLast { if parent&parentLast == parentLast {
break break
} }
} }
} else if parent2 != parentNone { } else if parent2 != parentNone {
parentIndexes = []int{int(parent1 & parentOctopusMask), int(parent2 & parentOctopusMask)} parentIndexes = []int{int(parent1 & parentOctopusMask), int(parent2 & parentOctopusMask)}
} else if parent1 != parentNone { } else if parent1 != parentNone {
parentIndexes = []int{int(parent1 & parentOctopusMask)} parentIndexes = []int{int(parent1 & parentOctopusMask)}
} }
parentHashes, err := fi.getHashesFromIndexes(parentIndexes) parentHashes, err := fi.getHashesFromIndexes(parentIndexes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &CommitData{ return &CommitData{
TreeHash: treeHash, TreeHash: treeHash,
ParentIndexes: parentIndexes, ParentIndexes: parentIndexes,
ParentHashes: parentHashes, ParentHashes: parentHashes,
Generation: int(genAndTime >> 34), Generation: int(genAndTime >> 34),
When: time.Unix(int64(genAndTime&0x3FFFFFFFF), 0), When: time.Unix(int64(genAndTime&0x3FFFFFFFF), 0),
}, nil }, nil
} }
func (fi *fileIndex) getHashesFromIndexes(indexes []int) ([]plumbing.Hash, error) { func (fi *fileIndex) getHashesFromIndexes(indexes []int) ([]plumbing.Hash, error) {
hashes := make([]plumbing.Hash, len(indexes)) hashes := make([]plumbing.Hash, len(indexes))
for i, idx := range indexes { for i, idx := range indexes {
if idx >= fi.fanout[0xff] { if idx >= fi.fanout[0xff] {
return nil, ErrMalformedCommitGraphFile return nil, ErrMalformedCommitGraphFile
} }
offset := fi.oidLookupOffset + int64(idx)*20 offset := fi.oidLookupOffset + int64(idx)*20
if _, err := fi.reader.ReadAt(hashes[i][:], offset); err != nil { if _, err := fi.reader.ReadAt(hashes[i][:], offset); err != nil {
return nil, err return nil, err
} }
} }
return hashes, nil return hashes, nil
} }
// Hashes returns all the hashes that are available in the index // Hashes returns all the hashes that are available in the index
func (fi *fileIndex) Hashes() []plumbing.Hash { func (fi *fileIndex) Hashes() []plumbing.Hash {
hashes := make([]plumbing.Hash, fi.fanout[0xff]) hashes := make([]plumbing.Hash, fi.fanout[0xff])
for i := 0; i < fi.fanout[0xff]; i++ { for i := 0; i < fi.fanout[0xff]; i++ {
offset := fi.oidLookupOffset + int64(i)*20 offset := fi.oidLookupOffset + int64(i)*20
if n, err := fi.reader.ReadAt(hashes[i][:], offset); err != nil || n < 20 { if n, err := fi.reader.ReadAt(hashes[i][:], offset); err != nil || n < 20 {
return nil return nil
} }
} }
return hashes return hashes
} }

View file

@ -1,72 +1,72 @@
package commitgraph package commitgraph
import ( import (
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
) )
// MemoryIndex provides a way to build the commit-graph in memory // MemoryIndex provides a way to build the commit-graph in memory
// for later encoding to file. // for later encoding to file.
type MemoryIndex struct { type MemoryIndex struct {
commitData []*CommitData commitData []*CommitData
indexMap map[plumbing.Hash]int indexMap map[plumbing.Hash]int
} }
// NewMemoryIndex creates in-memory commit graph representation // NewMemoryIndex creates in-memory commit graph representation
func NewMemoryIndex() *MemoryIndex { func NewMemoryIndex() *MemoryIndex {
return &MemoryIndex{ return &MemoryIndex{
indexMap: make(map[plumbing.Hash]int), indexMap: make(map[plumbing.Hash]int),
} }
} }
// GetIndexByHash gets the index in the commit graph from commit hash, if available // GetIndexByHash gets the index in the commit graph from commit hash, if available
func (mi *MemoryIndex) GetIndexByHash(h plumbing.Hash) (int, error) { func (mi *MemoryIndex) GetIndexByHash(h plumbing.Hash) (int, error) {
i, ok := mi.indexMap[h] i, ok := mi.indexMap[h]
if ok { if ok {
return i, nil return i, nil
} }
return 0, plumbing.ErrObjectNotFound return 0, plumbing.ErrObjectNotFound
} }
// GetCommitDataByIndex gets the commit node from the commit graph using index // GetCommitDataByIndex gets the commit node from the commit graph using index
// obtained from child node, if available // obtained from child node, if available
func (mi *MemoryIndex) GetCommitDataByIndex(i int) (*CommitData, error) { func (mi *MemoryIndex) GetCommitDataByIndex(i int) (*CommitData, error) {
if i >= len(mi.commitData) { if i >= len(mi.commitData) {
return nil, plumbing.ErrObjectNotFound return nil, plumbing.ErrObjectNotFound
} }
commitData := mi.commitData[i] commitData := mi.commitData[i]
// Map parent hashes to parent indexes // Map parent hashes to parent indexes
if commitData.ParentIndexes == nil { if commitData.ParentIndexes == nil {
parentIndexes := make([]int, len(commitData.ParentHashes)) parentIndexes := make([]int, len(commitData.ParentHashes))
for i, parentHash := range commitData.ParentHashes { for i, parentHash := range commitData.ParentHashes {
var err error var err error
if parentIndexes[i], err = mi.GetIndexByHash(parentHash); err != nil { if parentIndexes[i], err = mi.GetIndexByHash(parentHash); err != nil {
return nil, err return nil, err
} }
} }
commitData.ParentIndexes = parentIndexes commitData.ParentIndexes = parentIndexes
} }
return commitData, nil return commitData, nil
} }
// Hashes returns all the hashes that are available in the index // Hashes returns all the hashes that are available in the index
func (mi *MemoryIndex) Hashes() []plumbing.Hash { func (mi *MemoryIndex) Hashes() []plumbing.Hash {
hashes := make([]plumbing.Hash, 0, len(mi.indexMap)) hashes := make([]plumbing.Hash, 0, len(mi.indexMap))
for k := range mi.indexMap { for k := range mi.indexMap {
hashes = append(hashes, k) hashes = append(hashes, k)
} }
return hashes return hashes
} }
// Add adds new node to the memory index // Add adds new node to the memory index
func (mi *MemoryIndex) Add(hash plumbing.Hash, commitData *CommitData) { func (mi *MemoryIndex) Add(hash plumbing.Hash, commitData *CommitData) {
// The parent indexes are calculated lazily in GetNodeByIndex // The parent indexes are calculated lazily in GetNodeByIndex
// which allows adding nodes out of order as long as all parents // which allows adding nodes out of order as long as all parents
// are eventually resolved // are eventually resolved
commitData.ParentIndexes = nil commitData.ParentIndexes = nil
mi.indexMap[hash] = len(mi.commitData) mi.indexMap[hash] = len(mi.commitData)
mi.commitData = append(mi.commitData, commitData) mi.commitData = append(mi.commitData, commitData)
} }

View file

@ -1,98 +1,98 @@
package commitgraph package commitgraph
import ( import (
"io" "io"
"time" "time"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/plumbing/storer"
) )
// CommitNode is generic interface encapsulating a lightweight commit object retrieved // CommitNode is generic interface encapsulating a lightweight commit object retrieved
// from CommitNodeIndex // from CommitNodeIndex
type CommitNode interface { type CommitNode interface {
// ID returns the Commit object id referenced by the commit graph node. // ID returns the Commit object id referenced by the commit graph node.
ID() plumbing.Hash ID() plumbing.Hash
// Tree returns the Tree referenced by the commit graph node. // Tree returns the Tree referenced by the commit graph node.
Tree() (*object.Tree, error) Tree() (*object.Tree, error)
// CommitTime returns the Commiter.When time of the Commit referenced by the commit graph node. // CommitTime returns the Commiter.When time of the Commit referenced by the commit graph node.
CommitTime() time.Time CommitTime() time.Time
// NumParents returns the number of parents in a commit. // NumParents returns the number of parents in a commit.
NumParents() int NumParents() int
// ParentNodes return a CommitNodeIter for parents of specified node. // ParentNodes return a CommitNodeIter for parents of specified node.
ParentNodes() CommitNodeIter ParentNodes() CommitNodeIter
// ParentNode returns the ith parent of a commit. // ParentNode returns the ith parent of a commit.
ParentNode(i int) (CommitNode, error) ParentNode(i int) (CommitNode, error)
// ParentHashes returns hashes of the parent commits for a specified node // ParentHashes returns hashes of the parent commits for a specified node
ParentHashes() []plumbing.Hash ParentHashes() []plumbing.Hash
// Generation returns the generation of the commit for reachability analysis. // Generation returns the generation of the commit for reachability analysis.
// Objects with newer generation are not reachable from objects of older generation. // Objects with newer generation are not reachable from objects of older generation.
Generation() uint64 Generation() uint64
// Commit returns the full commit object from the node // Commit returns the full commit object from the node
Commit() (*object.Commit, error) Commit() (*object.Commit, error)
} }
// CommitNodeIndex is generic interface encapsulating an index of CommitNode objects // CommitNodeIndex is generic interface encapsulating an index of CommitNode objects
type CommitNodeIndex interface { type CommitNodeIndex interface {
// Get returns a commit node from a commit hash // Get returns a commit node from a commit hash
Get(hash plumbing.Hash) (CommitNode, error) Get(hash plumbing.Hash) (CommitNode, error)
} }
// CommitNodeIter is a generic closable interface for iterating over commit nodes. // CommitNodeIter is a generic closable interface for iterating over commit nodes.
type CommitNodeIter interface { type CommitNodeIter interface {
Next() (CommitNode, error) Next() (CommitNode, error)
ForEach(func(CommitNode) error) error ForEach(func(CommitNode) error) error
Close() Close()
} }
// parentCommitNodeIter provides an iterator for parent commits from associated CommitNodeIndex. // parentCommitNodeIter provides an iterator for parent commits from associated CommitNodeIndex.
type parentCommitNodeIter struct { type parentCommitNodeIter struct {
node CommitNode node CommitNode
i int i int
} }
func newParentgraphCommitNodeIter(node CommitNode) CommitNodeIter { func newParentgraphCommitNodeIter(node CommitNode) CommitNodeIter {
return &parentCommitNodeIter{node, 0} return &parentCommitNodeIter{node, 0}
} }
// Next moves the iterator to the next commit and returns a pointer to it. If // Next moves the iterator to the next commit and returns a pointer to it. If
// there are no more commits, it returns io.EOF. // there are no more commits, it returns io.EOF.
func (iter *parentCommitNodeIter) Next() (CommitNode, error) { func (iter *parentCommitNodeIter) Next() (CommitNode, error) {
obj, err := iter.node.ParentNode(iter.i) obj, err := iter.node.ParentNode(iter.i)
if err == object.ErrParentNotFound { if err == object.ErrParentNotFound {
return nil, io.EOF return nil, io.EOF
} }
if err == nil { if err == nil {
iter.i++ iter.i++
} }
return obj, err return obj, err
} }
// ForEach call the cb function for each commit contained on this iter until // ForEach call the cb function for each commit contained on this iter until
// an error appends or the end of the iter is reached. If ErrStop is sent // an error appends or the end of the iter is reached. If ErrStop is sent
// the iteration is stopped but no error is returned. The iterator is closed. // the iteration is stopped but no error is returned. The iterator is closed.
func (iter *parentCommitNodeIter) ForEach(cb func(CommitNode) error) error { func (iter *parentCommitNodeIter) ForEach(cb func(CommitNode) error) error {
for { for {
obj, err := iter.Next() obj, err := iter.Next()
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
return nil return nil
} }
return err return err
} }
if err := cb(obj); err != nil { if err := cb(obj); err != nil {
if err == storer.ErrStop { if err == storer.ErrStop {
return nil return nil
} }
return err return err
} }
} }
} }
func (iter *parentCommitNodeIter) Close() { func (iter *parentCommitNodeIter) Close() {
} }

View file

@ -1,131 +1,131 @@
package commitgraph package commitgraph
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/commitgraph" "github.com/go-git/go-git/v5/plumbing/format/commitgraph"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/plumbing/storer"
) )
// graphCommitNode is a reduced representation of Commit as presented in the commit // graphCommitNode is a reduced representation of Commit as presented in the commit
// graph file (commitgraph.Node). It is merely useful as an optimization for walking // graph file (commitgraph.Node). It is merely useful as an optimization for walking
// the commit graphs. // the commit graphs.
// //
// graphCommitNode implements the CommitNode interface. // graphCommitNode implements the CommitNode interface.
type graphCommitNode struct { type graphCommitNode struct {
// Hash for the Commit object // Hash for the Commit object
hash plumbing.Hash hash plumbing.Hash
// Index of the node in the commit graph file // Index of the node in the commit graph file
index int index int
commitData *commitgraph.CommitData commitData *commitgraph.CommitData
gci *graphCommitNodeIndex gci *graphCommitNodeIndex
} }
// graphCommitNodeIndex is an index that can load CommitNode objects from both the commit // graphCommitNodeIndex is an index that can load CommitNode objects from both the commit
// graph files and the object store. // graph files and the object store.
// //
// graphCommitNodeIndex implements the CommitNodeIndex interface // graphCommitNodeIndex implements the CommitNodeIndex interface
type graphCommitNodeIndex struct { type graphCommitNodeIndex struct {
commitGraph commitgraph.Index commitGraph commitgraph.Index
s storer.EncodedObjectStorer s storer.EncodedObjectStorer
} }
// NewGraphCommitNodeIndex returns CommitNodeIndex implementation that uses commit-graph // NewGraphCommitNodeIndex returns CommitNodeIndex implementation that uses commit-graph
// files as backing storage and falls back to object storage when necessary // files as backing storage and falls back to object storage when necessary
func NewGraphCommitNodeIndex(commitGraph commitgraph.Index, s storer.EncodedObjectStorer) CommitNodeIndex { func NewGraphCommitNodeIndex(commitGraph commitgraph.Index, s storer.EncodedObjectStorer) CommitNodeIndex {
return &graphCommitNodeIndex{commitGraph, s} return &graphCommitNodeIndex{commitGraph, s}
} }
func (gci *graphCommitNodeIndex) Get(hash plumbing.Hash) (CommitNode, error) { func (gci *graphCommitNodeIndex) Get(hash plumbing.Hash) (CommitNode, error) {
// Check the commit graph first // Check the commit graph first
parentIndex, err := gci.commitGraph.GetIndexByHash(hash) parentIndex, err := gci.commitGraph.GetIndexByHash(hash)
if err == nil { if err == nil {
parent, err := gci.commitGraph.GetCommitDataByIndex(parentIndex) parent, err := gci.commitGraph.GetCommitDataByIndex(parentIndex)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &graphCommitNode{ return &graphCommitNode{
hash: hash, hash: hash,
index: parentIndex, index: parentIndex,
commitData: parent, commitData: parent,
gci: gci, gci: gci,
}, nil }, nil
} }
// Fallback to loading full commit object // Fallback to loading full commit object
commit, err := object.GetCommit(gci.s, hash) commit, err := object.GetCommit(gci.s, hash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &objectCommitNode{ return &objectCommitNode{
nodeIndex: gci, nodeIndex: gci,
commit: commit, commit: commit,
}, nil }, nil
} }
func (c *graphCommitNode) ID() plumbing.Hash { func (c *graphCommitNode) ID() plumbing.Hash {
return c.hash return c.hash
} }
func (c *graphCommitNode) Tree() (*object.Tree, error) { func (c *graphCommitNode) Tree() (*object.Tree, error) {
return object.GetTree(c.gci.s, c.commitData.TreeHash) return object.GetTree(c.gci.s, c.commitData.TreeHash)
} }
func (c *graphCommitNode) CommitTime() time.Time { func (c *graphCommitNode) CommitTime() time.Time {
return c.commitData.When return c.commitData.When
} }
func (c *graphCommitNode) NumParents() int { func (c *graphCommitNode) NumParents() int {
return len(c.commitData.ParentIndexes) return len(c.commitData.ParentIndexes)
} }
func (c *graphCommitNode) ParentNodes() CommitNodeIter { func (c *graphCommitNode) ParentNodes() CommitNodeIter {
return newParentgraphCommitNodeIter(c) return newParentgraphCommitNodeIter(c)
} }
func (c *graphCommitNode) ParentNode(i int) (CommitNode, error) { func (c *graphCommitNode) ParentNode(i int) (CommitNode, error) {
if i < 0 || i >= len(c.commitData.ParentIndexes) { if i < 0 || i >= len(c.commitData.ParentIndexes) {
return nil, object.ErrParentNotFound return nil, object.ErrParentNotFound
} }
parent, err := c.gci.commitGraph.GetCommitDataByIndex(c.commitData.ParentIndexes[i]) parent, err := c.gci.commitGraph.GetCommitDataByIndex(c.commitData.ParentIndexes[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &graphCommitNode{ return &graphCommitNode{
hash: c.commitData.ParentHashes[i], hash: c.commitData.ParentHashes[i],
index: c.commitData.ParentIndexes[i], index: c.commitData.ParentIndexes[i],
commitData: parent, commitData: parent,
gci: c.gci, gci: c.gci,
}, nil }, nil
} }
func (c *graphCommitNode) ParentHashes() []plumbing.Hash { func (c *graphCommitNode) ParentHashes() []plumbing.Hash {
return c.commitData.ParentHashes return c.commitData.ParentHashes
} }
func (c *graphCommitNode) Generation() uint64 { func (c *graphCommitNode) Generation() uint64 {
// If the commit-graph file was generated with older Git version that // If the commit-graph file was generated with older Git version that
// set the generation to zero for every commit the generation assumption // set the generation to zero for every commit the generation assumption
// is still valid. It is just less useful. // is still valid. It is just less useful.
return uint64(c.commitData.Generation) return uint64(c.commitData.Generation)
} }
func (c *graphCommitNode) Commit() (*object.Commit, error) { func (c *graphCommitNode) Commit() (*object.Commit, error) {
return object.GetCommit(c.gci.s, c.hash) return object.GetCommit(c.gci.s, c.hash)
} }
func (c *graphCommitNode) String() string { func (c *graphCommitNode) String() string {
return fmt.Sprintf( return fmt.Sprintf(
"%s %s\nDate: %s", "%s %s\nDate: %s",
plumbing.CommitObject, c.ID(), plumbing.CommitObject, c.ID(),
c.CommitTime().Format(object.DateFormat), c.CommitTime().Format(object.DateFormat),
) )
} }

View file

@ -1,90 +1,90 @@
package commitgraph package commitgraph
import ( import (
"math" "math"
"time" "time"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/plumbing/storer"
) )
// objectCommitNode is a representation of Commit as presented in the GIT object format. // objectCommitNode is a representation of Commit as presented in the GIT object format.
// //
// objectCommitNode implements the CommitNode interface. // objectCommitNode implements the CommitNode interface.
type objectCommitNode struct { type objectCommitNode struct {
nodeIndex CommitNodeIndex nodeIndex CommitNodeIndex
commit *object.Commit commit *object.Commit
} }
// NewObjectCommitNodeIndex returns CommitNodeIndex implementation that uses // NewObjectCommitNodeIndex returns CommitNodeIndex implementation that uses
// only object storage to load the nodes // only object storage to load the nodes
func NewObjectCommitNodeIndex(s storer.EncodedObjectStorer) CommitNodeIndex { func NewObjectCommitNodeIndex(s storer.EncodedObjectStorer) CommitNodeIndex {
return &objectCommitNodeIndex{s} return &objectCommitNodeIndex{s}
} }
func (oci *objectCommitNodeIndex) Get(hash plumbing.Hash) (CommitNode, error) { func (oci *objectCommitNodeIndex) Get(hash plumbing.Hash) (CommitNode, error) {
commit, err := object.GetCommit(oci.s, hash) commit, err := object.GetCommit(oci.s, hash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &objectCommitNode{ return &objectCommitNode{
nodeIndex: oci, nodeIndex: oci,
commit: commit, commit: commit,
}, nil }, nil
} }
// objectCommitNodeIndex is an index that can load CommitNode objects only from the // objectCommitNodeIndex is an index that can load CommitNode objects only from the
// object store. // object store.
// //
// objectCommitNodeIndex implements the CommitNodeIndex interface // objectCommitNodeIndex implements the CommitNodeIndex interface
type objectCommitNodeIndex struct { type objectCommitNodeIndex struct {
s storer.EncodedObjectStorer s storer.EncodedObjectStorer
} }
func (c *objectCommitNode) CommitTime() time.Time { func (c *objectCommitNode) CommitTime() time.Time {
return c.commit.Committer.When return c.commit.Committer.When
} }
func (c *objectCommitNode) ID() plumbing.Hash { func (c *objectCommitNode) ID() plumbing.Hash {
return c.commit.ID() return c.commit.ID()
} }
func (c *objectCommitNode) Tree() (*object.Tree, error) { func (c *objectCommitNode) Tree() (*object.Tree, error) {
return c.commit.Tree() return c.commit.Tree()
} }
func (c *objectCommitNode) NumParents() int { func (c *objectCommitNode) NumParents() int {
return c.commit.NumParents() return c.commit.NumParents()
} }
func (c *objectCommitNode) ParentNodes() CommitNodeIter { func (c *objectCommitNode) ParentNodes() CommitNodeIter {
return newParentgraphCommitNodeIter(c) return newParentgraphCommitNodeIter(c)
} }
func (c *objectCommitNode) ParentNode(i int) (CommitNode, error) { func (c *objectCommitNode) ParentNode(i int) (CommitNode, error) {
if i < 0 || i >= len(c.commit.ParentHashes) { if i < 0 || i >= len(c.commit.ParentHashes) {
return nil, object.ErrParentNotFound return nil, object.ErrParentNotFound
} }
// Note: It's necessary to go through CommitNodeIndex here to ensure // Note: It's necessary to go through CommitNodeIndex here to ensure
// that if the commit-graph file covers only part of the history we // that if the commit-graph file covers only part of the history we
// start using it when that part is reached. // start using it when that part is reached.
return c.nodeIndex.Get(c.commit.ParentHashes[i]) return c.nodeIndex.Get(c.commit.ParentHashes[i])
} }
func (c *objectCommitNode) ParentHashes() []plumbing.Hash { func (c *objectCommitNode) ParentHashes() []plumbing.Hash {
return c.commit.ParentHashes return c.commit.ParentHashes
} }
func (c *objectCommitNode) Generation() uint64 { func (c *objectCommitNode) Generation() uint64 {
// Commit nodes representing objects outside of the commit graph can never // Commit nodes representing objects outside of the commit graph can never
// be reached by objects from the commit-graph thus we return the highest // be reached by objects from the commit-graph thus we return the highest
// possible value. // possible value.
return math.MaxUint64 return math.MaxUint64
} }
func (c *objectCommitNode) Commit() (*object.Commit, error) { func (c *objectCommitNode) Commit() (*object.Commit, error) {
return c.commit, nil return c.commit, nil
} }

View file

@ -1,105 +1,105 @@
package commitgraph package commitgraph
import ( import (
"io" "io"
"github.com/emirpasic/gods/trees/binaryheap" "github.com/emirpasic/gods/trees/binaryheap"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/plumbing/storer"
) )
type commitNodeIteratorByCTime struct { type commitNodeIteratorByCTime struct {
heap *binaryheap.Heap heap *binaryheap.Heap
seenExternal map[plumbing.Hash]bool seenExternal map[plumbing.Hash]bool
seen map[plumbing.Hash]bool seen map[plumbing.Hash]bool
} }
// NewCommitNodeIterCTime returns a CommitNodeIter that walks the commit history, // NewCommitNodeIterCTime returns a CommitNodeIter that walks the commit history,
// starting at the given commit and visiting its parents while preserving Committer Time order. // starting at the given commit and visiting its parents while preserving Committer Time order.
// this appears to be the closest order to `git log` // this appears to be the closest order to `git log`
// The given callback will be called for each visited commit. Each commit will // The given callback will be called for each visited commit. Each commit will
// be visited only once. If the callback returns an error, walking will stop // be visited only once. If the callback returns an error, walking will stop
// and will return the error. Other errors might be returned if the history // and will return the error. Other errors might be returned if the history
// cannot be traversed (e.g. missing objects). Ignore allows to skip some // cannot be traversed (e.g. missing objects). Ignore allows to skip some
// commits from being iterated. // commits from being iterated.
func NewCommitNodeIterCTime( func NewCommitNodeIterCTime(
c CommitNode, c CommitNode,
seenExternal map[plumbing.Hash]bool, seenExternal map[plumbing.Hash]bool,
ignore []plumbing.Hash, ignore []plumbing.Hash,
) CommitNodeIter { ) CommitNodeIter {
seen := make(map[plumbing.Hash]bool) seen := make(map[plumbing.Hash]bool)
for _, h := range ignore { for _, h := range ignore {
seen[h] = true seen[h] = true
} }
heap := binaryheap.NewWith(func(a, b interface{}) int { heap := binaryheap.NewWith(func(a, b interface{}) int {
if a.(CommitNode).CommitTime().Before(b.(CommitNode).CommitTime()) { if a.(CommitNode).CommitTime().Before(b.(CommitNode).CommitTime()) {
return 1 return 1
} }
return -1 return -1
}) })
heap.Push(c) heap.Push(c)
return &commitNodeIteratorByCTime{ return &commitNodeIteratorByCTime{
heap: heap, heap: heap,
seenExternal: seenExternal, seenExternal: seenExternal,
seen: seen, seen: seen,
} }
} }
func (w *commitNodeIteratorByCTime) Next() (CommitNode, error) { func (w *commitNodeIteratorByCTime) Next() (CommitNode, error) {
var c CommitNode var c CommitNode
for { for {
cIn, ok := w.heap.Pop() cIn, ok := w.heap.Pop()
if !ok { if !ok {
return nil, io.EOF return nil, io.EOF
} }
c = cIn.(CommitNode) c = cIn.(CommitNode)
cID := c.ID() cID := c.ID()
if w.seen[cID] || w.seenExternal[cID] { if w.seen[cID] || w.seenExternal[cID] {
continue continue
} }
w.seen[cID] = true w.seen[cID] = true
for i, h := range c.ParentHashes() { for i, h := range c.ParentHashes() {
if w.seen[h] || w.seenExternal[h] { if w.seen[h] || w.seenExternal[h] {
continue continue
} }
pc, err := c.ParentNode(i) pc, err := c.ParentNode(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
w.heap.Push(pc) w.heap.Push(pc)
} }
return c, nil return c, nil
} }
} }
func (w *commitNodeIteratorByCTime) ForEach(cb func(CommitNode) error) error { func (w *commitNodeIteratorByCTime) ForEach(cb func(CommitNode) error) error {
for { for {
c, err := w.Next() c, err := w.Next()
if err == io.EOF { if err == io.EOF {
break break
} }
if err != nil { if err != nil {
return err return err
} }
err = cb(c) err = cb(c)
if err == storer.ErrStop { if err == storer.ErrStop {
break break
} }
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }
func (w *commitNodeIteratorByCTime) Close() {} func (w *commitNodeIteratorByCTime) Close() {}

View file

@ -1,22 +1,22 @@
Copyright (c) 2013 Caleb Spare Copyright (c) 2013 Caleb Spare
MIT License MIT License
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software. included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.