opengist/internal/git/output_parser.go

146 lines
3 KiB
Go
Raw Normal View History

package git
import (
2023-03-18 22:18:20 +00:00
"bufio"
"bytes"
"io"
2023-03-18 22:18:20 +00:00
"regexp"
)
2023-03-18 22:18:20 +00:00
type File struct {
2023-03-18 22:23:23 +00:00
Filename string
OldFilename string
Content string
2023-03-18 22:18:20 +00:00
Truncated bool
IsCreated bool
IsDeleted bool
}
type Commit struct {
Hash string
Author string
Timestamp string
Changed string
Files []File
}
func truncateCommandOutput(out io.Reader, maxBytes int64) (string, bool, error) {
2023-03-18 17:22:27 +00:00
var buf []byte
var err error
if maxBytes < 0 {
buf, err = io.ReadAll(out)
2023-03-18 17:22:27 +00:00
} else {
buf, err = io.ReadAll(io.LimitReader(out, maxBytes))
}
2023-03-18 17:22:27 +00:00
if err != nil {
return "", false, err
}
2023-03-18 17:22:27 +00:00
truncated := len(buf) >= int(maxBytes)
// Remove the last line if it's truncated
if truncated {
// Find the index of the last newline character
lastNewline := bytes.LastIndexByte(buf, '\n')
2023-03-18 17:22:27 +00:00
if lastNewline > 0 {
// Trim the data buffer up to the last newline character
buf = buf[:lastNewline]
}
}
2023-03-18 17:22:27 +00:00
return string(buf), truncated, nil
}
2023-03-18 22:18:20 +00:00
func parseLog(out io.Reader) []*Commit {
scanner := bufio.NewScanner(out)
var commits []*Commit
var currentCommit *Commit
var currentFile *File
var isContent bool
for scanner.Scan() {
// new commit found
currentFile = nil
currentCommit = &Commit{Hash: string(scanner.Bytes()[2:]), Files: []File{}}
scanner.Scan()
currentCommit.Author = string(scanner.Bytes()[2:])
scanner.Scan()
currentCommit.Timestamp = string(scanner.Bytes()[2:])
scanner.Scan()
changed := scanner.Bytes()[1:]
changed = bytes.ReplaceAll(changed, []byte("(+)"), []byte(""))
changed = bytes.ReplaceAll(changed, []byte("(-)"), []byte(""))
currentCommit.Changed = string(changed)
// twice because --shortstat adds a new line
scanner.Scan()
scanner.Scan()
// commit header parsed
// files changes inside the commit
for {
line := scanner.Bytes()
// end of content of file
if len(line) == 0 {
isContent = false
if currentFile != nil {
currentCommit.Files = append(currentCommit.Files, *currentFile)
}
break
}
// new file found
if bytes.HasPrefix(line, []byte("diff --git")) {
// current file is finished, we can add it to the commit
if currentFile != nil {
currentCommit.Files = append(currentCommit.Files, *currentFile)
}
// create a new file
isContent = false
currentFile = &File{}
filenameRegex := regexp.MustCompile(`^diff --git a/(.+) b/(.+)$`)
matches := filenameRegex.FindStringSubmatch(string(line))
if len(matches) == 3 {
currentFile.Filename = matches[2]
if matches[1] != matches[2] {
currentFile.OldFilename = matches[1]
}
}
scanner.Scan()
continue
}
if bytes.HasPrefix(line, []byte("new")) {
currentFile.IsCreated = true
}
if bytes.HasPrefix(line, []byte("deleted")) {
currentFile.IsDeleted = true
}
// file content found
if line[0] == '@' {
isContent = true
}
if isContent {
currentFile.Content += string(line) + "\n"
}
scanner.Scan()
}
if currentCommit != nil {
commits = append(commits, currentCommit)
}
}
return commits
}