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