* Improve get last commit using git log --name-status git log --name-status -c provides information about the diff between a commit and its parents. Using this and adjusting the algorithm to use the first change to a path allows for a much faster generation of commit info. There is a subtle change in the results generated but this will cause the results to more closely match those from elsewhere. Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Lauris BH <lauris@nix.lv>
		
			
				
	
	
		
			186 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			186 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| package buffer
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/gob"
 | |
| 	"io"
 | |
| 	"math"
 | |
| )
 | |
| 
 | |
| type chain struct {
 | |
| 	Buf  BufferAt
 | |
| 	Next BufferAt
 | |
| }
 | |
| 
 | |
| type nopBufferAt struct {
 | |
| 	Buffer
 | |
| }
 | |
| 
 | |
| func (buf *nopBufferAt) ReadAt(p []byte, off int64) (int, error) {
 | |
| 	panic("ReadAt not implemented")
 | |
| }
 | |
| 
 | |
| func (buf *nopBufferAt) WriteAt(p []byte, off int64) (int, error) {
 | |
| 	panic("WriteAt not implemented")
 | |
| }
 | |
| 
 | |
| // toBufferAt converts a Buffer to a BufferAt with nop ReadAt and WriteAt funcs
 | |
| func toBufferAt(buf Buffer) BufferAt {
 | |
| 	return &nopBufferAt{Buffer: buf}
 | |
| }
 | |
| 
 | |
| // NewMultiAt returns a BufferAt which is the logical concatenation of the passed BufferAts.
 | |
| // The data in the buffers is shifted such that there is no non-empty buffer following
 | |
| // a non-full buffer, this process is also run after every Read.
 | |
| // If no buffers are passed, the returned Buffer is nil.
 | |
| func NewMultiAt(buffers ...BufferAt) BufferAt {
 | |
| 	if len(buffers) == 0 {
 | |
| 		return nil
 | |
| 	} else if len(buffers) == 1 {
 | |
| 		return buffers[0]
 | |
| 	}
 | |
| 
 | |
| 	buf := &chain{
 | |
| 		Buf:  buffers[0],
 | |
| 		Next: NewMultiAt(buffers[1:]...),
 | |
| 	}
 | |
| 
 | |
| 	buf.Defrag()
 | |
| 
 | |
| 	return buf
 | |
| }
 | |
| 
 | |
| // NewMulti returns a Buffer which is the logical concatenation of the passed buffers.
 | |
| // The data in the buffers is shifted such that there is no non-empty buffer following
 | |
| // a non-full buffer, this process is also run after every Read.
 | |
| // If no buffers are passed, the returned Buffer is nil.
 | |
| func NewMulti(buffers ...Buffer) Buffer {
 | |
| 	bufAt := make([]BufferAt, len(buffers))
 | |
| 	for i, buf := range buffers {
 | |
| 		bufAt[i] = toBufferAt(buf)
 | |
| 	}
 | |
| 	return NewMultiAt(bufAt...)
 | |
| }
 | |
| 
 | |
| func (buf *chain) Reset() {
 | |
| 	buf.Next.Reset()
 | |
| 	buf.Buf.Reset()
 | |
| }
 | |
| 
 | |
| func (buf *chain) Cap() (n int64) {
 | |
| 	Next := buf.Next.Cap()
 | |
| 	if buf.Buf.Cap() > math.MaxInt64-Next {
 | |
| 		return math.MaxInt64
 | |
| 	}
 | |
| 	return buf.Buf.Cap() + Next
 | |
| }
 | |
| 
 | |
| func (buf *chain) Len() (n int64) {
 | |
| 	Next := buf.Next.Len()
 | |
| 	if buf.Buf.Len() > math.MaxInt64-Next {
 | |
| 		return math.MaxInt64
 | |
| 	}
 | |
| 	return buf.Buf.Len() + Next
 | |
| }
 | |
| 
 | |
| func (buf *chain) Defrag() {
 | |
| 	for !Full(buf.Buf) && !Empty(buf.Next) {
 | |
| 		r := io.LimitReader(buf.Next, Gap(buf.Buf))
 | |
| 		if _, err := io.Copy(buf.Buf, r); err != nil && err != io.EOF {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (buf *chain) Read(p []byte) (n int, err error) {
 | |
| 	n, err = buf.Buf.Read(p)
 | |
| 	if len(p[n:]) > 0 && (err == nil || err == io.EOF) {
 | |
| 		m, err := buf.Next.Read(p[n:])
 | |
| 		n += m
 | |
| 		if err != nil {
 | |
| 			return n, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	buf.Defrag()
 | |
| 
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| func (buf *chain) ReadAt(p []byte, off int64) (n int, err error) {
 | |
| 	if buf.Buf.Len() < off {
 | |
| 		return buf.Next.ReadAt(p, off-buf.Buf.Len())
 | |
| 	}
 | |
| 
 | |
| 	n, err = buf.Buf.ReadAt(p, off)
 | |
| 	if len(p[n:]) > 0 && (err == nil || err == io.EOF) {
 | |
| 		var m int
 | |
| 		m, err = buf.Next.ReadAt(p[n:], 0)
 | |
| 		n += m
 | |
| 	}
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| func (buf *chain) Write(p []byte) (n int, err error) {
 | |
| 	if n, err = buf.Buf.Write(p); err == io.ErrShortWrite {
 | |
| 		err = nil
 | |
| 	}
 | |
| 	p = p[n:]
 | |
| 	if len(p) > 0 && err == nil {
 | |
| 		m, err := buf.Next.Write(p)
 | |
| 		n += m
 | |
| 		if err != nil {
 | |
| 			return n, err
 | |
| 		}
 | |
| 	}
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| func (buf *chain) WriteAt(p []byte, off int64) (n int, err error) {
 | |
| 	switch {
 | |
| 	case buf.Buf.Cap() <= off: // past the end
 | |
| 		return buf.Next.WriteAt(p, off-buf.Buf.Cap())
 | |
| 
 | |
| 	case buf.Buf.Cap() >= off+int64(len(p)): // fits in
 | |
| 		return buf.Buf.WriteAt(p, off)
 | |
| 
 | |
| 	default: // partial fit
 | |
| 		n, err = buf.Buf.WriteAt(p, off)
 | |
| 		if len(p[n:]) > 0 && (err == nil || err == io.ErrShortWrite) {
 | |
| 			var m int
 | |
| 			m, err = buf.Next.WriteAt(p[n:], 0)
 | |
| 			n += m
 | |
| 		}
 | |
| 		return n, err
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	gob.Register(&chain{})
 | |
| 	gob.Register(&nopBufferAt{})
 | |
| }
 | |
| 
 | |
| func (buf *chain) MarshalBinary() ([]byte, error) {
 | |
| 	b := bytes.NewBuffer(nil)
 | |
| 	enc := gob.NewEncoder(b)
 | |
| 	if err := enc.Encode(&buf.Buf); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if err := enc.Encode(&buf.Next); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return b.Bytes(), nil
 | |
| }
 | |
| 
 | |
| func (buf *chain) UnmarshalBinary(data []byte) error {
 | |
| 	b := bytes.NewBuffer(data)
 | |
| 	dec := gob.NewDecoder(b)
 | |
| 	if err := dec.Decode(&buf.Buf); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := dec.Decode(&buf.Next); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |