forked from NYANDEV/forgejo
Use git log name-status in get last commit (#16059)
* 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>
This commit is contained in:
parent
8fa3bbc424
commit
23358bc55d
40 changed files with 2540 additions and 297 deletions
|
@ -11,6 +11,9 @@ import (
|
|||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/djherbis/buffer"
|
||||
"github.com/djherbis/nio/v3"
|
||||
)
|
||||
|
||||
// WriteCloserError wraps an io.WriteCloser with an additional CloseWithError function
|
||||
|
@ -42,7 +45,7 @@ func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()
|
|||
}
|
||||
}()
|
||||
|
||||
// For simplicities sake we'll us a buffered reader to read from the cat-file --batch
|
||||
// For simplicities sake we'll use a buffered reader to read from the cat-file --batch-check
|
||||
batchReader := bufio.NewReader(batchStdoutReader)
|
||||
|
||||
return batchStdinWriter, batchReader, cancel
|
||||
|
@ -53,7 +56,7 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
|
|||
// We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
|
||||
// so let's create a batch stdin and stdout
|
||||
batchStdinReader, batchStdinWriter := io.Pipe()
|
||||
batchStdoutReader, batchStdoutWriter := io.Pipe()
|
||||
batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024))
|
||||
cancel := func() {
|
||||
_ = batchStdinReader.Close()
|
||||
_ = batchStdinWriter.Close()
|
||||
|
@ -74,7 +77,7 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
|
|||
}()
|
||||
|
||||
// For simplicities sake we'll us a buffered reader to read from the cat-file --batch
|
||||
batchReader := bufio.NewReader(batchStdoutReader)
|
||||
batchReader := bufio.NewReaderSize(batchStdoutReader, 32*1024)
|
||||
|
||||
return batchStdinWriter, batchReader, cancel
|
||||
}
|
||||
|
@ -84,22 +87,31 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
|
|||
// <sha> SP <type> SP <size> LF
|
||||
// sha is a 40byte not 20byte here
|
||||
func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) {
|
||||
sha, err = rd.ReadBytes(' ')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sha = sha[:len(sha)-1]
|
||||
|
||||
typ, err = rd.ReadString('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(typ) == 1 {
|
||||
typ, err = rd.ReadString('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
idx := strings.IndexByte(typ, ' ')
|
||||
if idx < 0 {
|
||||
log("missing space typ: %s", typ)
|
||||
err = ErrNotExist{ID: string(sha)}
|
||||
return
|
||||
}
|
||||
sha = []byte(typ[:idx])
|
||||
typ = typ[idx+1:]
|
||||
|
||||
idx := strings.Index(typ, " ")
|
||||
idx = strings.IndexByte(typ, ' ')
|
||||
if idx < 0 {
|
||||
err = ErrNotExist{ID: string(sha)}
|
||||
return
|
||||
}
|
||||
|
||||
sizeStr := typ[idx+1 : len(typ)-1]
|
||||
typ = typ[:idx]
|
||||
|
||||
|
@ -130,7 +142,7 @@ headerLoop:
|
|||
}
|
||||
|
||||
// Discard the rest of the tag
|
||||
discard := size - n
|
||||
discard := size - n + 1
|
||||
for discard > math.MaxInt32 {
|
||||
_, err := rd.Discard(math.MaxInt32)
|
||||
if err != nil {
|
||||
|
@ -200,57 +212,6 @@ func To40ByteSHA(sha, out []byte) []byte {
|
|||
return out
|
||||
}
|
||||
|
||||
// ParseTreeLineSkipMode reads an entry from a tree in a cat-file --batch stream
|
||||
// This simply skips the mode - saving a substantial amount of time and carefully avoids allocations - except where fnameBuf is too small.
|
||||
// It is recommended therefore to pass in an fnameBuf large enough to avoid almost all allocations
|
||||
//
|
||||
// Each line is composed of:
|
||||
// <mode-in-ascii-dropping-initial-zeros> SP <fname> NUL <20-byte SHA>
|
||||
//
|
||||
// We don't attempt to convert the 20-byte SHA to 40-byte SHA to save a lot of time
|
||||
func ParseTreeLineSkipMode(rd *bufio.Reader, fnameBuf, shaBuf []byte) (fname, sha []byte, n int, err error) {
|
||||
var readBytes []byte
|
||||
// Skip the Mode
|
||||
readBytes, err = rd.ReadSlice(' ') // NB: DOES NOT ALLOCATE SIMPLY RETURNS SLICE WITHIN READER BUFFER
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += len(readBytes)
|
||||
|
||||
// Deal with the fname
|
||||
readBytes, err = rd.ReadSlice('\x00')
|
||||
copy(fnameBuf, readBytes)
|
||||
if len(fnameBuf) > len(readBytes) {
|
||||
fnameBuf = fnameBuf[:len(readBytes)] // cut the buf the correct size
|
||||
} else {
|
||||
fnameBuf = append(fnameBuf, readBytes[len(fnameBuf):]...) // extend the buf and copy in the missing bits
|
||||
}
|
||||
for err == bufio.ErrBufferFull { // Then we need to read more
|
||||
readBytes, err = rd.ReadSlice('\x00')
|
||||
fnameBuf = append(fnameBuf, readBytes...) // there is little point attempting to avoid allocations here so just extend
|
||||
}
|
||||
n += len(fnameBuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fnameBuf = fnameBuf[:len(fnameBuf)-1] // Drop the terminal NUL
|
||||
fname = fnameBuf // set the returnable fname to the slice
|
||||
|
||||
// Now deal with the 20-byte SHA
|
||||
idx := 0
|
||||
for idx < 20 {
|
||||
read := 0
|
||||
read, err = rd.Read(shaBuf[idx:20])
|
||||
n += read
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
idx += read
|
||||
}
|
||||
sha = shaBuf
|
||||
return
|
||||
}
|
||||
|
||||
// ParseTreeLine reads an entry from a tree in a cat-file --batch stream
|
||||
// This carefully avoids allocations - except where fnameBuf is too small.
|
||||
// It is recommended therefore to pass in an fnameBuf large enough to avoid almost all allocations
|
||||
|
@ -262,23 +223,31 @@ func ParseTreeLineSkipMode(rd *bufio.Reader, fnameBuf, shaBuf []byte) (fname, sh
|
|||
func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fname, sha []byte, n int, err error) {
|
||||
var readBytes []byte
|
||||
|
||||
// Read the Mode
|
||||
readBytes, err = rd.ReadSlice(' ')
|
||||
// Read the Mode & fname
|
||||
readBytes, err = rd.ReadSlice('\x00')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += len(readBytes)
|
||||
copy(modeBuf, readBytes)
|
||||
if len(modeBuf) > len(readBytes) {
|
||||
modeBuf = modeBuf[:len(readBytes)]
|
||||
} else {
|
||||
modeBuf = append(modeBuf, readBytes[len(modeBuf):]...)
|
||||
idx := bytes.IndexByte(readBytes, ' ')
|
||||
if idx < 0 {
|
||||
log("missing space in readBytes ParseTreeLine: %s", readBytes)
|
||||
|
||||
err = &ErrNotExist{}
|
||||
return
|
||||
}
|
||||
mode = modeBuf[:len(modeBuf)-1] // Drop the SP
|
||||
|
||||
n += idx + 1
|
||||
copy(modeBuf, readBytes[:idx])
|
||||
if len(modeBuf) >= idx {
|
||||
modeBuf = modeBuf[:idx]
|
||||
} else {
|
||||
modeBuf = append(modeBuf, readBytes[len(modeBuf):idx]...)
|
||||
}
|
||||
mode = modeBuf
|
||||
|
||||
readBytes = readBytes[idx+1:]
|
||||
|
||||
// Deal with the fname
|
||||
readBytes, err = rd.ReadSlice('\x00')
|
||||
copy(fnameBuf, readBytes)
|
||||
if len(fnameBuf) > len(readBytes) {
|
||||
fnameBuf = fnameBuf[:len(readBytes)]
|
||||
|
@ -297,7 +266,7 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
|
|||
fname = fnameBuf
|
||||
|
||||
// Deal with the 20-byte SHA
|
||||
idx := 0
|
||||
idx = 0
|
||||
for idx < 20 {
|
||||
read := 0
|
||||
read, err = rd.Read(shaBuf[idx:20])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue