| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- // Copyright 2020 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package git
-
- import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- )
-
- const (
- commitHeaderGpgsig = "gpgsig"
- commitHeaderGpgsigSha256 = "gpgsig-sha256"
- )
-
- func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, headerValue []byte) error {
- if len(headerValue) > 0 && headerValue[len(headerValue)-1] == '\n' {
- headerValue = headerValue[:len(headerValue)-1] // remove trailing newline
- }
- switch headerKey {
- case "tree":
- objID, err := NewIDFromString(string(headerValue))
- if err != nil {
- return fmt.Errorf("invalid tree ID %q: %w", string(headerValue), err)
- }
- commit.Tree = *NewTree(gitRepo, objID)
- case "parent":
- objID, err := NewIDFromString(string(headerValue))
- if err != nil {
- return fmt.Errorf("invalid parent ID %q: %w", string(headerValue), err)
- }
- commit.Parents = append(commit.Parents, objID)
- case "author":
- commit.Author.Decode(headerValue)
- case "committer":
- commit.Committer.Decode(headerValue)
- case commitHeaderGpgsig, commitHeaderGpgsigSha256:
- // if there are duplicate "gpgsig" and "gpgsig-sha256" headers, then the signature must have already been invalid
- // so we don't need to handle duplicate headers here
- commit.Signature = &CommitSignature{Signature: string(headerValue)}
- }
- return nil
- }
-
- // CommitFromReader will generate a Commit from a provided reader
- // We need this to interpret commits from cat-file or cat-file --batch
- //
- // If used as part of a cat-file --batch stream you need to limit the reader to the correct size
- func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader) (*Commit, error) {
- commit := &Commit{
- ID: objectID,
- Author: &Signature{},
- Committer: &Signature{},
- }
-
- bufReader := bufio.NewReader(reader)
- inHeader := true
- var payloadSB, messageSB bytes.Buffer
- var headerKey string
- var headerValue []byte
- for {
- line, err := bufReader.ReadBytes('\n')
- if err != nil && err != io.EOF {
- return nil, fmt.Errorf("unable to read commit %q: %w", objectID.String(), err)
- }
- if len(line) == 0 {
- break
- }
-
- if inHeader {
- inHeader = !(len(line) == 1 && line[0] == '\n') // still in header if line is not just a newline
- k, v, _ := bytes.Cut(line, []byte{' '})
- if len(k) != 0 || !inHeader {
- if headerKey != "" {
- if err = assignCommitFields(gitRepo, commit, headerKey, headerValue); err != nil {
- return nil, fmt.Errorf("unable to parse commit %q: %w", objectID.String(), err)
- }
- }
- headerKey = string(k) // it also resets the headerValue to empty string if not inHeader
- headerValue = v
- } else {
- headerValue = append(headerValue, v...)
- }
- if headerKey != commitHeaderGpgsig && headerKey != commitHeaderGpgsigSha256 {
- _, _ = payloadSB.Write(line)
- }
- } else {
- _, _ = messageSB.Write(line)
- _, _ = payloadSB.Write(line)
- }
- }
-
- commit.CommitMessage = messageSB.String()
- if commit.Signature != nil {
- commit.Signature.Payload = payloadSB.String()
- }
- return commit, nil
- }
|