github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/cmd/noms/commit_iterator.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package main 23 24 import ( 25 "context" 26 "fmt" 27 28 "github.com/dolthub/dolt/go/store/d" 29 30 "github.com/dolthub/dolt/go/store/datas" 31 "github.com/dolthub/dolt/go/store/hash" 32 "github.com/dolthub/dolt/go/store/types" 33 ) 34 35 type CommitIterator struct { 36 vr types.ValueReader 37 branches branchList 38 } 39 40 // NewCommitIterator initializes a new CommitIterator with the first commit to be printed. 41 func NewCommitIterator(vr types.ValueReader, commit types.Value) *CommitIterator { 42 cr, err := types.NewRef(commit, vr.Format()) 43 d.PanicIfError(err) 44 return &CommitIterator{vr: vr, branches: branchList{branch{addr: cr.TargetHash(), height: cr.Height(), commit: commit}}} 45 } 46 47 // Next returns information about the next commit to be printed. LogNode contains enough contextual 48 // info that the commit and associated graph can be correctly printed. 49 // This works by traversing the "commit" di-graph in a breadth-first manner. Each time it is called, 50 // the commit in the branchlist with the greatest height is returned. If that commit has multiple 51 // parents, new branches are added to the branchlist so that they can be traversed in order. When 52 // more than one branch contains the same node, that indicates that the branches are converging and so 53 // the branchlist will have branches removed to reflect that. 54 func (iter *CommitIterator) Next(ctx context.Context) (LogNode, bool) { 55 if iter.branches.IsEmpty() { 56 return LogNode{}, false 57 } 58 59 // Float of branches present when printing this commit 60 startingColCount := len(iter.branches) 61 62 branchIndexes := iter.branches.HighestBranchIndexes() 63 col := branchIndexes[0] 64 br := iter.branches[col] 65 66 // Any additional indexes, represent other branches with the same ancestor. So they are merging 67 // into a common ancestor and are no longer graphed. 68 iter.branches = iter.branches.RemoveBranches(branchIndexes[1:]) 69 70 // If this commit has parents, then a branch is splitting. Create a branch for each of the parents 71 // and splice that into the iterators list of branches. 72 branches := branchList{} 73 74 parents, err := datas.GetCommitParents(ctx, iter.vr, br.commit) 75 d.PanicIfError(err) 76 for _, p := range parents { 77 v := p.NomsValue() 78 b := branch{height: p.Height(), addr: p.Addr(), commit: v} 79 branches = append(branches, b) 80 } 81 iter.branches = iter.branches.Splice(col, 1, branches...) 82 83 // Collect the indexes for any newly created branches. 84 newCols := []int{} 85 for cnt := 1; cnt < len(parents); cnt++ { 86 newCols = append(newCols, col+cnt) 87 } 88 89 // Now that the branchlist has been adusted, check to see if there are branches with common 90 // ancestors that will be folded together on this commit's graph. 91 foldedCols := iter.branches.HighestBranchIndexes() 92 node := LogNode{ 93 height: br.height, 94 addr: br.addr, 95 commit: br.commit, 96 startingColCount: startingColCount, 97 endingColCount: len(iter.branches), 98 col: col, 99 newCols: newCols, 100 foldedCols: foldedCols, 101 lastCommit: iter.branches.IsEmpty(), 102 } 103 return node, true 104 } 105 106 type LogNode struct { 107 addr hash.Hash 108 height uint64 109 commit types.Value // commit that needs to be printed 110 startingColCount int // how many branches are being tracked when this commit is printed 111 endingColCount int // home many branches will be tracked when next commit is printed 112 col int // col to put the '*' character in graph 113 newCols []int // col to start using '\' in graph 114 foldedCols []int // cols with common ancestors, that will get folded together 115 lastCommit bool // this is the last commit that will be returned by iterator 116 } 117 118 func (n LogNode) String() string { 119 return fmt.Sprintf("cr: %s(%d), startingColCount: %d, endingColCount: %d, col: %d, newCols: %v, foldedCols: %v, expanding: %t, shrunk: %t, shrinking: %t", n.addr.String()[0:9], n.height, n.startingColCount, n.endingColCount, n.col, n.newCols, n.foldedCols, n.Expanding(), n.Shrunk(), n.Shrinking()) 120 } 121 122 // Expanding reports whether this commit's graph will expand to show an additional branch 123 func (n LogNode) Expanding() bool { 124 return n.startingColCount < n.endingColCount 125 } 126 127 // Shrinking reports whether this commit's graph will show a branch being folded into another branch 128 func (n LogNode) Shrinking() bool { 129 return len(n.foldedCols) > 1 130 } 131 132 // Shrunk reports whether the previous commit showed a branch being folded into another branch. 133 func (n LogNode) Shrunk() bool { 134 return n.startingColCount > n.endingColCount 135 } 136 137 type branch struct { 138 addr hash.Hash 139 height uint64 140 commit types.Value 141 } 142 143 func (b branch) String() string { 144 return fmt.Sprintf("%s(%d)", b.addr.String()[0:9], b.height) 145 } 146 147 type branchList []branch 148 149 func (bl branchList) IsEmpty() bool { 150 return len(bl) == 0 151 } 152 153 // look through this list of branches and return the one(s) with the max height. 154 // If there are multiple nodes with max height, the result will contain a list of all nodes with 155 // maxHeight that are duplicates of the first one found. 156 // This indicates that two or more branches or converging. 157 func (bl branchList) HighestBranchIndexes() []int { 158 maxHeight := uint64(0) 159 var br branch 160 cols := []int{} 161 for i, b := range bl { 162 if b.height > maxHeight { 163 maxHeight = b.height 164 br = b 165 cols = []int{i} 166 } else if b.height == maxHeight && b.addr == br.addr { 167 cols = append(cols, i) 168 } 169 } 170 return cols 171 } 172 173 func (bl branchList) Splice(start int, deleteCount int, branches ...branch) branchList { 174 res := append(branchList{}, bl[:start]...) 175 res = append(res, branches...) 176 return append(res, bl[start+deleteCount:]...) 177 } 178 179 func (bl branchList) RemoveBranches(indexes []int) branchList { 180 for i := len(indexes) - 1; i >= 0; i-- { 181 bl = bl.Splice(indexes[i], 1) 182 } 183 return bl 184 } 185 186 func commitRefsFromSet(ctx context.Context, set types.Set) []types.Ref { 187 res := []types.Ref{} 188 _ = set.IterAll(ctx, func(v types.Value) error { 189 res = append(res, v.(types.Ref)) 190 return nil 191 }) 192 193 return res 194 }