1package commitgraph
2
3import (
4 "fmt"
5 "time"
6
7 "github.com/go-git/go-git/v5/plumbing"
8 commitgraph "github.com/go-git/go-git/v5/plumbing/format/commitgraph/v2"
9 "github.com/go-git/go-git/v5/plumbing/object"
10 "github.com/go-git/go-git/v5/plumbing/storer"
11)
12
13// graphCommitNode is a reduced representation of Commit as presented in the commit
14// graph file (commitgraph.Node). It is merely useful as an optimization for walking
15// the commit graphs.
16//
17// graphCommitNode implements the CommitNode interface.
18type graphCommitNode struct {
19 // Hash for the Commit object
20 hash plumbing.Hash
21 // Index of the node in the commit graph file
22 index uint32
23
24 commitData *commitgraph.CommitData
25 gci *graphCommitNodeIndex
26}
27
28// graphCommitNodeIndex is an index that can load CommitNode objects from both the commit
29// graph files and the object store.
30//
31// graphCommitNodeIndex implements the CommitNodeIndex interface
32type graphCommitNodeIndex struct {
33 commitGraph commitgraph.Index
34 s storer.EncodedObjectStorer
35}
36
37// NewGraphCommitNodeIndex returns CommitNodeIndex implementation that uses commit-graph
38// files as backing storage and falls back to object storage when necessary
39func NewGraphCommitNodeIndex(commitGraph commitgraph.Index, s storer.EncodedObjectStorer) CommitNodeIndex {
40 return &graphCommitNodeIndex{commitGraph, s}
41}
42
43func (gci *graphCommitNodeIndex) Get(hash plumbing.Hash) (CommitNode, error) {
44 if gci.commitGraph != nil {
45 // Check the commit graph first
46 parentIndex, err := gci.commitGraph.GetIndexByHash(hash)
47 if err == nil {
48 parent, err := gci.commitGraph.GetCommitDataByIndex(parentIndex)
49 if err != nil {
50 return nil, err
51 }
52
53 return &graphCommitNode{
54 hash: hash,
55 index: parentIndex,
56 commitData: parent,
57 gci: gci,
58 }, nil
59 }
60 }
61
62 // Fallback to loading full commit object
63 commit, err := object.GetCommit(gci.s, hash)
64 if err != nil {
65 return nil, err
66 }
67
68 return &objectCommitNode{
69 nodeIndex: gci,
70 commit: commit,
71 }, nil
72}
73
74func (c *graphCommitNode) ID() plumbing.Hash {
75 return c.hash
76}
77
78func (c *graphCommitNode) Tree() (*object.Tree, error) {
79 return object.GetTree(c.gci.s, c.commitData.TreeHash)
80}
81
82func (c *graphCommitNode) CommitTime() time.Time {
83 return c.commitData.When
84}
85
86func (c *graphCommitNode) NumParents() int {
87 return len(c.commitData.ParentIndexes)
88}
89
90func (c *graphCommitNode) ParentNodes() CommitNodeIter {
91 return newParentgraphCommitNodeIter(c)
92}
93
94func (c *graphCommitNode) ParentNode(i int) (CommitNode, error) {
95 if i < 0 || i >= len(c.commitData.ParentIndexes) {
96 return nil, object.ErrParentNotFound
97 }
98
99 parent, err := c.gci.commitGraph.GetCommitDataByIndex(c.commitData.ParentIndexes[i])
100 if err != nil {
101 return nil, err
102 }
103
104 return &graphCommitNode{
105 hash: c.commitData.ParentHashes[i],
106 index: c.commitData.ParentIndexes[i],
107 commitData: parent,
108 gci: c.gci,
109 }, nil
110}
111
112func (c *graphCommitNode) ParentHashes() []plumbing.Hash {
113 return c.commitData.ParentHashes
114}
115
116func (c *graphCommitNode) Generation() uint64 {
117 // If the commit-graph file was generated with older Git version that
118 // set the generation to zero for every commit the generation assumption
119 // is still valid. It is just less useful.
120 return c.commitData.Generation
121}
122
123func (c *graphCommitNode) GenerationV2() uint64 {
124 // If the commit-graph file was generated with older Git version that
125 // set the generation to zero for every commit the generation assumption
126 // is still valid. It is just less useful.
127 return c.commitData.GenerationV2
128}
129
130func (c *graphCommitNode) Commit() (*object.Commit, error) {
131 return object.GetCommit(c.gci.s, c.hash)
132}
133
134func (c *graphCommitNode) String() string {
135 return fmt.Sprintf(
136 "%s %s\nDate: %s",
137 plumbing.CommitObject, c.ID(),
138 c.CommitTime().Format(object.DateFormat),
139 )
140}