// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package git import ( "bufio" "bytes" "io" "strings" "gopkg.in/src-d/go-git.v4/plumbing" ) // CommitFromReader will generate a Commit from a provided reader // We will need this to interpret commits from cat-file func CommitFromReader(gitRepo *Repository, sha plumbing.Hash, reader io.Reader) (*Commit, error) { commit := &Commit{ ID: sha, } payloadSB := new(strings.Builder) signatureSB := new(strings.Builder) messageSB := new(strings.Builder) message := false pgpsig := false scanner := bufio.NewScanner(reader) // Split by '\n' but include the '\n' scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { return 0, nil, nil } if i := bytes.IndexByte(data, '\n'); i >= 0 { // We have a full newline-terminated line. return i + 1, data[0 : i+1], nil } // If we're at EOF, we have a final, non-terminated line. Return it. if atEOF { return len(data), data, nil } // Request more data. return 0, nil, nil }) for scanner.Scan() { line := scanner.Bytes() if pgpsig { if len(line) > 0 && line[0] == ' ' { _, _ = signatureSB.Write(line[1:]) continue } else { pgpsig = false } } if !message { // This is probably not correct but is copied from go-gits interpretation... trimmed := bytes.TrimSpace(line) if len(trimmed) == 0 { message = true _, _ = payloadSB.Write(line) continue } split := bytes.SplitN(trimmed, []byte{' '}, 2) var data []byte if len(split) > 1 { data = split[1] } switch string(split[0]) { case "tree": commit.Tree = *NewTree(gitRepo, plumbing.NewHash(string(data))) _, _ = payloadSB.Write(line) case "parent": commit.Parents = append(commit.Parents, plumbing.NewHash(string(data))) _, _ = payloadSB.Write(line) case "author": commit.Author = &Signature{} commit.Author.Decode(data) _, _ = payloadSB.Write(line) case "committer": commit.Committer = &Signature{} commit.Committer.Decode(data) _, _ = payloadSB.Write(line) case "gpgsig": _, _ = signatureSB.Write(data) _ = signatureSB.WriteByte('\n') pgpsig = true } } else { _, _ = messageSB.Write(line) } } commit.CommitMessage = messageSB.String() _, _ = payloadSB.WriteString(commit.CommitMessage) commit.Signature = &CommitGPGSignature{ Signature: signatureSB.String(), Payload: payloadSB.String(), } if len(commit.Signature.Signature) == 0 { commit.Signature = nil } return commit, scanner.Err() }