···1010 objecttype "codeberg.org/lindenii/furgit/object/type"
1111)
12121313-// ensureLoaded completes one node's metadata load if it has not been loaded yet.
1414-func (query *query) ensureLoaded(idx nodeIndex) error {
1515- if query.nodes[idx].loaded {
1616- return nil
1717- }
1818-1919- if query.nodes[idx].hasGraphPos {
2020- return query.loadByGraphPos(idx)
2121- }
2222-2323- return query.loadByOID(idx)
2424-}
2525-2613// loadByOID populates one node from an object ID.
2714func (query *query) loadByOID(idx nodeIndex) error {
2815 id := query.nodes[idx].id
+17
commitquery/mark_bits.go
···11+package commitquery
22+33+// markBits stores one set of traversal marks on one node.
44+type markBits uint8
55+66+// markLeft, markRight, markStale, and markResult track traversal state.
77+const (
88+ markLeft markBits = 1 << iota
99+ markRight
1010+ markStale
1111+ markResult
1212+)
1313+1414+// allMarks is the union of all defined mark bits.
1515+const (
1616+ allMarks = markLeft | markRight | markStale | markResult
1717+)
-102
commitquery/marks.go
···11-package commitquery
22-33-// Marks returns the mark bits of one internal node.
44-func (query *query) marks(idx nodeIndex) markBits {
55- return query.nodes[idx].marks
66-}
77-88-// HasAnyMarks reports whether one internal node has any requested bit.
99-func (query *query) hasAnyMarks(idx nodeIndex, bits markBits) bool {
1010- return query.nodes[idx].marks&bits != 0
1111-}
1212-1313-// HasAllMarks reports whether one internal node already has all requested bits.
1414-func (query *query) hasAllMarks(idx nodeIndex, bits markBits) bool {
1515- return query.nodes[idx].marks&bits == bits
1616-}
1717-1818-// SetMarks ORs one set of mark bits into one internal node.
1919-func (query *query) setMarks(idx nodeIndex, bits markBits) {
2020- newBits := bits &^ query.nodes[idx].marks
2121- if newBits == 0 {
2222- return
2323- }
2424-2525- query.trackTouched(idx)
2626- query.nodes[idx].marks |= bits
2727-}
2828-2929-// ClearMarks removes one set of mark bits from one internal node.
3030-func (query *query) clearMarks(idx nodeIndex, bits markBits) {
3131- if query.nodes[idx].marks&bits == 0 {
3232- return
3333- }
3434-3535- query.trackTouched(idx)
3636- query.nodes[idx].marks &^= bits
3737-}
3838-3939-// BeginMarkPhase starts one tracked mark-mutation phase.
4040-func (query *query) beginMarkPhase() {
4141- for _, idx := range query.touched {
4242- query.nodes[idx].marks = 0
4343- }
4444-4545- query.markPhase++
4646- if query.markPhase == 0 {
4747- query.markPhase++
4848- for i := range query.nodes {
4949- query.nodes[i].touchedPhase = 0
5050- }
5151- }
5252-5353- query.touched = query.touched[:0]
5454-}
5555-5656-// ClearTouchedMarks clears the provided bits from all nodes touched in the
5757-// current mark phase.
5858-func (query *query) clearTouchedMarks(bits markBits) {
5959- for _, idx := range query.touched {
6060- query.nodes[idx].marks &^= bits
6161- }
6262-}
6363-6464-func (query *query) trackTouched(idx nodeIndex) {
6565- if query.nodes[idx].touchedPhase == query.markPhase {
6666- return
6767- }
6868-6969- query.nodes[idx].touchedPhase = query.markPhase
7070- query.touched = append(query.touched, idx)
7171-}
7272-7373-func (query *query) collectMarkedResults() []nodeIndex {
7474- out := make([]nodeIndex, 0, 4)
7575-7676- for _, idx := range query.touched {
7777- if !query.hasAnyMarks(idx, markResult) {
7878- continue
7979- }
8080-8181- if query.hasAnyMarks(idx, markStale) {
8282- continue
8383- }
8484-8585- out = append(out, idx)
8686- }
8787-8888- return out
8989-}
9090-9191-type markBits uint8
9292-9393-const (
9494- markLeft markBits = 1 << iota
9595- markRight
9696- markStale
9797- markResult
9898-)
9999-100100-const (
101101- allMarks = markLeft | markRight | markStale | markResult
102102-)
-89
commitquery/merge_bases.go
···11-package commitquery
22-33-import (
44- "slices"
55-66- objectid "codeberg.org/lindenii/furgit/object/id"
77-)
88-99-// MergeBases reports all merge bases in Git's merge-base --all order.
1010-//
1111-// Both inputs are peeled through annotated tags before commit traversal.
1212-func (query *query) MergeBases(left, right objectid.ObjectID) ([]objectid.ObjectID, error) {
1313- leftIdx, err := query.resolveCommitish(left)
1414- if err != nil {
1515- return nil, err
1616- }
1717-1818- rightIdx, err := query.resolveCommitish(right)
1919- if err != nil {
2020- return nil, err
2121- }
2222-2323- candidates, err := query.mergeBases(leftIdx, rightIdx)
2424- if err != nil {
2525- return nil, err
2626- }
2727-2828- slices.SortFunc(candidates, func(left, right nodeIndex) int {
2929- switch {
3030- case query.commitTime(left) > query.commitTime(right):
3131- return -1
3232- case query.commitTime(left) < query.commitTime(right):
3333- return 1
3434- default:
3535- return objectid.Compare(query.id(left), query.id(right))
3636- }
3737- })
3838-3939- out := make([]objectid.ObjectID, 0, len(candidates))
4040- for _, idx := range candidates {
4141- out = append(out, query.id(idx))
4242- }
4343-4444- return out, nil
4545-}
4646-4747-// MergeBase reports one merge base between left and right, if any.
4848-func (query *query) MergeBase(left, right objectid.ObjectID) (objectid.ObjectID, bool, error) {
4949- bases, err := query.MergeBases(left, right)
5050- if err != nil {
5151- return objectid.ObjectID{}, false, err
5252- }
5353-5454- if len(bases) == 0 {
5555- return objectid.ObjectID{}, false, nil
5656- }
5757-5858- return bases[0], true, nil
5959-}
6060-6161-func (query *query) mergeBases(left, right nodeIndex) ([]nodeIndex, error) {
6262- if left == right {
6363- return []nodeIndex{left}, nil
6464- }
6565-6666- err := query.paintDownToCommon(left, []nodeIndex{right}, 0)
6767- if err != nil {
6868- return nil, err
6969- }
7070-7171- candidates := query.collectMarkedResults()
7272-7373- if len(candidates) <= 1 {
7474- slices.SortFunc(candidates, query.compare)
7575-7676- return candidates, nil
7777- }
7878-7979- query.clearTouchedMarks(allMarks)
8080-8181- reduced, err := removeRedundant(query, candidates)
8282- if err != nil {
8383- return nil, err
8484- }
8585-8686- slices.SortFunc(reduced, query.compare)
8787-8888- return reduced, nil
8989-}
···2233import "codeberg.org/lindenii/furgit/internal/priorityqueue"
4455+// paintDownToCommon propagates left and right marks downward until common nodes.
56func (query *query) paintDownToCommon(left nodeIndex, rights []nodeIndex, minGeneration uint64) error {
67 query.beginMarkPhase()
78
-27
commitquery/node_parent.go
···11-package commitquery
22-33-import (
44- commitgraphread "codeberg.org/lindenii/furgit/format/commitgraph/read"
55- objectid "codeberg.org/lindenii/furgit/object/id"
66-)
77-88-// Parents returns resolved parent node indices for one internal node.
99-func (query *query) parents(idx nodeIndex) []nodeIndex {
1010- return query.nodes[idx].parents
1111-}
1212-1313-// parentRef references one commit parent.
1414-type parentRef struct {
1515- ID objectid.ObjectID
1616- GraphPos commitgraphread.Position
1717- HasGraphPos bool
1818-}
1919-2020-// resolveParent resolves one parent descriptor to one internal node.
2121-func (query *query) resolveParent(parent parentRef) (nodeIndex, error) {
2222- if parent.HasGraphPos {
2323- return query.resolveGraphPos(parent.GraphPos)
2424- }
2525-2626- return query.resolveOID(parent.ID)
2727-}
+6
commitquery/node_parents.go
···11+package commitquery
22+33+// parents returns resolved parent node indices for one internal node.
44+func (query *query) parents(idx nodeIndex) []nodeIndex {
55+ return query.nodes[idx].parents
66+}
···11package commitquery
2233+// acquire removes one worker from the idle pool or allocates one new worker.
34func (queries *Queries) acquire() *query {
45 queries.mu.Lock()
56 defer queries.mu.Unlock()
···11111212 return query.MergeBases(left, right)
1313}
1414-1515-// MergeBase reports one merge base between left and right, if any.
1616-func (queries *Queries) MergeBase(left, right objectid.ObjectID) (objectid.ObjectID, bool, error) {
1717- query := queries.acquire()
1818- defer queries.release(query)
1919-2020- return query.MergeBase(left, right)
2121-}
+1
commitquery/queries_release.go
···11package commitquery
2233+// release resets one worker and returns it to the idle pool if there is room.
34func (queries *Queries) release(q *query) {
45 q.resetForReuse()
56
···11+package commitquery
22+33+// beginMarkPhase starts one tracked mark-mutation phase.
44+func (query *query) beginMarkPhase() {
55+ for _, idx := range query.touched {
66+ query.nodes[idx].marks = 0
77+ }
88+99+ query.markPhase++
1010+ if query.markPhase == 0 {
1111+ query.markPhase++
1212+ for i := range query.nodes {
1313+ query.nodes[i].touchedPhase = 0
1414+ }
1515+ }
1616+1717+ query.touched = query.touched[:0]
1818+}
1919+2020+// clearTouchedMarks clears the provided bits from all nodes touched in the
2121+// current mark phase.
2222+func (query *query) clearTouchedMarks(bits markBits) {
2323+ for _, idx := range query.touched {
2424+ query.nodes[idx].marks &^= bits
2525+ }
2626+}
2727+2828+// trackTouched records one node in the current mark phase.
2929+func (query *query) trackTouched(idx nodeIndex) {
3030+ if query.nodes[idx].touchedPhase == query.markPhase {
3131+ return
3232+ }
3333+3434+ query.nodes[idx].touchedPhase = query.markPhase
3535+ query.touched = append(query.touched, idx)
3636+}
+6
commitquery/query_marks_get.go
···11+package commitquery
22+33+// marks returns the mark bits of one internal node.
44+func (query *query) marks(idx nodeIndex) markBits {
55+ return query.nodes[idx].marks
66+}
+17
commitquery/query_merge_base.go
···11+package commitquery
22+33+import objectid "codeberg.org/lindenii/furgit/object/id"
44+55+// MergeBase reports one merge base between left and right, if any.
66+func (query *query) MergeBase(left, right objectid.ObjectID) (objectid.ObjectID, bool, error) {
77+ bases, err := query.MergeBases(left, right)
88+ if err != nil {
99+ return objectid.ObjectID{}, false, err
1010+ }
1111+1212+ if len(bases) == 0 {
1313+ return objectid.ObjectID{}, false, nil
1414+ }
1515+1616+ return bases[0], true, nil
1717+}
+45
commitquery/query_merge_bases.go
···11+package commitquery
22+33+import (
44+ "slices"
55+66+ objectid "codeberg.org/lindenii/furgit/object/id"
77+)
88+99+// MergeBases reports all merge bases in Git's merge-base --all order.
1010+//
1111+// Both inputs are peeled through annotated tags before commit traversal.
1212+func (query *query) MergeBases(left, right objectid.ObjectID) ([]objectid.ObjectID, error) {
1313+ leftIdx, err := query.resolveCommitish(left)
1414+ if err != nil {
1515+ return nil, err
1616+ }
1717+1818+ rightIdx, err := query.resolveCommitish(right)
1919+ if err != nil {
2020+ return nil, err
2121+ }
2222+2323+ candidates, err := query.mergeBases(leftIdx, rightIdx)
2424+ if err != nil {
2525+ return nil, err
2626+ }
2727+2828+ slices.SortFunc(candidates, func(left, right nodeIndex) int {
2929+ switch {
3030+ case query.commitTime(left) > query.commitTime(right):
3131+ return -1
3232+ case query.commitTime(left) < query.commitTime(right):
3333+ return 1
3434+ default:
3535+ return objectid.Compare(query.id(left), query.id(right))
3636+ }
3737+ })
3838+3939+ out := make([]objectid.ObjectID, 0, len(candidates))
4040+ for _, idx := range candidates {
4141+ out = append(out, query.id(idx))
4242+ }
4343+4444+ return out, nil
4545+}
···11+package commitquery
22+33+// resetForReuse clears transient state before one worker returns to the pool.
44+func (query *query) resetForReuse() {
55+ for _, idx := range query.touched {
66+ query.nodes[idx].marks = 0
77+ }
88+99+ query.touched = query.touched[:0]
1010+}