parent.go

 1package main
 2
 3import (
 4	"fmt"
 5
 6	"github.com/git-bug/git-bug/cache"
 7	"github.com/git-bug/git-bug/entities/bug"
 8	"github.com/git-bug/git-bug/entity"
 9	"github.com/git-bug/git-bug/repository"
10)
11
12// parentMetadataKey is the CreateOp metadata key holding the full ID
13// of the parent bug. Absent means "no parent".
14//
15// git-bug metadata keys are immutable once set: a later SetMetadata
16// cannot overwrite or delete the key. Parent links can therefore only
17// be added to bugs that have none; they cannot be changed or removed.
18const parentMetadataKey = "parent"
19
20// currentParentID returns the bug's current parent full ID, or "".
21func currentParentID(snap *bug.Snapshot) string {
22	if parent, ok := snap.GetCreateMetadata(parentMetadataKey); ok && parent != "" {
23		return parent
24	}
25	return ""
26}
27
28// createsParentCycle reports whether linking bugFullID under parentFullID
29// would close a dependency cycle (i.e. bugFullID is already an ancestor
30// of parentFullID). Self-links are also reported as cycles.
31func createsParentCycle(issues []BugIssue, bugFullID, parentFullID string) bool {
32	if bugFullID == parentFullID {
33		return true
34	}
35	byID := make(map[string]BugIssue, len(issues))
36	for _, issue := range issues {
37		byID[issue.FullID] = issue
38	}
39	for current := parentFullID; current != ""; {
40		if current == bugFullID {
41			return true
42		}
43		ancestor, ok := byID[current]
44		if !ok {
45			return false
46		}
47		current = ancestor.ParentID
48	}
49	return false
50}
51
52// setBugParentLink records parentFullID on the bug's create operation.
53// The caller must ensure the bug has no parent yet: overwriting an
54// existing key is silently ignored by git-bug.
55func setBugParentLink(repo repository.ClockedRepo, b *bug.Bug, createOpID entity.Id, parentFullID string) error {
56	cacheInstance, err := cache.NewRepoCacheNoEvents(repo)
57	if err != nil {
58		return fmt.Errorf("failed to create cache: %w", err)
59	}
60	defer cacheInstance.Close()
61	bc, err := cacheInstance.Bugs().Resolve(b.Id())
62	if err != nil {
63		return fmt.Errorf("failed to resolve bug: %w", err)
64	}
65	if _, err := bc.SetMetadata(createOpID, map[string]string{parentMetadataKey: parentFullID}); err != nil {
66		return fmt.Errorf("failed to set parent: %w", err)
67	}
68	if err := bc.CommitAsNeeded(); err != nil {
69		return fmt.Errorf("failed to commit parent link: %w", err)
70	}
71	return nil
72}