code.gitea.io/gitea@v1.19.3/modules/git/commit_reader.go (about)

     1  // Copyright 2020 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package git
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"io"
    10  	"strings"
    11  )
    12  
    13  // CommitFromReader will generate a Commit from a provided reader
    14  // We need this to interpret commits from cat-file or cat-file --batch
    15  //
    16  // If used as part of a cat-file --batch stream you need to limit the reader to the correct size
    17  func CommitFromReader(gitRepo *Repository, sha SHA1, reader io.Reader) (*Commit, error) {
    18  	commit := &Commit{
    19  		ID:        sha,
    20  		Author:    &Signature{},
    21  		Committer: &Signature{},
    22  	}
    23  
    24  	payloadSB := new(strings.Builder)
    25  	signatureSB := new(strings.Builder)
    26  	messageSB := new(strings.Builder)
    27  	message := false
    28  	pgpsig := false
    29  
    30  	bufReader, ok := reader.(*bufio.Reader)
    31  	if !ok {
    32  		bufReader = bufio.NewReader(reader)
    33  	}
    34  
    35  readLoop:
    36  	for {
    37  		line, err := bufReader.ReadBytes('\n')
    38  		if err != nil {
    39  			if err == io.EOF {
    40  				if message {
    41  					_, _ = messageSB.Write(line)
    42  				}
    43  				_, _ = payloadSB.Write(line)
    44  				break readLoop
    45  			}
    46  			return nil, err
    47  		}
    48  		if pgpsig {
    49  			if len(line) > 0 && line[0] == ' ' {
    50  				_, _ = signatureSB.Write(line[1:])
    51  				continue
    52  			} else {
    53  				pgpsig = false
    54  			}
    55  		}
    56  
    57  		if !message {
    58  			// This is probably not correct but is copied from go-gits interpretation...
    59  			trimmed := bytes.TrimSpace(line)
    60  			if len(trimmed) == 0 {
    61  				message = true
    62  				_, _ = payloadSB.Write(line)
    63  				continue
    64  			}
    65  
    66  			split := bytes.SplitN(trimmed, []byte{' '}, 2)
    67  			var data []byte
    68  			if len(split) > 1 {
    69  				data = split[1]
    70  			}
    71  
    72  			switch string(split[0]) {
    73  			case "tree":
    74  				commit.Tree = *NewTree(gitRepo, MustIDFromString(string(data)))
    75  				_, _ = payloadSB.Write(line)
    76  			case "parent":
    77  				commit.Parents = append(commit.Parents, MustIDFromString(string(data)))
    78  				_, _ = payloadSB.Write(line)
    79  			case "author":
    80  				commit.Author = &Signature{}
    81  				commit.Author.Decode(data)
    82  				_, _ = payloadSB.Write(line)
    83  			case "committer":
    84  				commit.Committer = &Signature{}
    85  				commit.Committer.Decode(data)
    86  				_, _ = payloadSB.Write(line)
    87  			case "gpgsig":
    88  				_, _ = signatureSB.Write(data)
    89  				_ = signatureSB.WriteByte('\n')
    90  				pgpsig = true
    91  			}
    92  		} else {
    93  			_, _ = messageSB.Write(line)
    94  			_, _ = payloadSB.Write(line)
    95  		}
    96  	}
    97  	commit.CommitMessage = messageSB.String()
    98  	commit.Signature = &CommitGPGSignature{
    99  		Signature: signatureSB.String(),
   100  		Payload:   payloadSB.String(),
   101  	}
   102  	if len(commit.Signature.Signature) == 0 {
   103  		commit.Signature = nil
   104  	}
   105  
   106  	return commit, nil
   107  }