1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
// 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
}
|