github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/commit_reader.go (about)

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