Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Number issues and change requests for human references

e05f1c48d4ee55635cd7b77889f0545137198106 · Keenan Nemetz · 13d ago · parent 41d1fee9

Number issues and change requests for human references

A ULID is an unreadable handle, and the 8-character prefix the list showed
is mostly timestamp: this repository's 27 issues shared seven distinct
six-character prefixes and two pairs already collided at eight. The part
people actually type carried almost no information.

Issues and change requests now share one gap-free sequence (#1, #42, ...)
derived from the creation order in the metadata branch's history, the way
one forge counts both in a single run.

The number is not stored. It is the rank of the entity's creation commit,
so nothing is written when an entity is created, a repository needs no
backfill, the same history gives the same numbers on every clone, and
`vole admin index rebuild` reproduces them from git. A ULID stays the
identity: paths, front matter, `closes:` and audit entries are unchanged,
and data/index.db only caches the mapping so a page render does not walk
history.

Every id-taking command accepts a number, `#42` links in a body through a
kind-agnostic /r/<repo>/n/42 route, and --closes resolves an alias to the
ULID before writing.

Closes #45

forge/index/index.go +21−1

@@ -96,6 +96,22 @@ CREATE INDEX IF NOT EXISTS issue_assignees_lookup ON issue_assignees(repo_id, as
96 96 db.Close()
97 97 return nil, err
98 98 }
99 // entity_numbers is the `#42` alias. It is derived from history
100 // (metadata.LoadNumbers), never written back to the metadata branch, and the
101 // primary key on the number is what makes a duplicate impossible rather than
102 // merely unlikely.
103 if _, err := db.Exec(`
104 CREATE TABLE IF NOT EXISTS entity_numbers (
105 repo_id TEXT NOT NULL,
106 number INTEGER NOT NULL,
107 kind TEXT NOT NULL,
108 id TEXT NOT NULL,
109 PRIMARY KEY (repo_id, number)
110 );
111 CREATE INDEX IF NOT EXISTS entity_numbers_id ON entity_numbers(repo_id, id);`); err != nil {
112 db.Close()
113 return nil, err
114 }
99 115 if err := s.migrate(); err != nil {
100 116 db.Close()
101 117 return nil, err
@@ -108,7 +124,7 @@ CREATE INDEX IF NOT EXISTS issue_assignees_lookup ON issue_assignees(repo_id, as
108 124 // leaves every repo with an empty issues_sha, which makes the next read rebuild
109 125 // that repo from git. A stale row is worse than a rebuild, and a rebuild is the
110 126 // operation the index already has to survive.
111 const schemaVersion = 2
127 const schemaVersion = 3
112 128
113 129 func (s *Store) migrate() error {
114 130 var v int
@@ -122,6 +138,7 @@ func (s *Store) migrate() error {
122 138 DELETE FROM issues;
123 139 DELETE FROM issue_labels;
124 140 DELETE FROM issue_assignees;
141 DELETE FROM entity_numbers;
125 142 UPDATE repos SET issues_sha = '';`); err != nil {
126 143 return err
127 144 }
@@ -140,6 +157,9 @@ type IssueRow struct {
140 157 Labels []string
141 158 Assignees []string
142 159 Comments int
160 // Number is the `#42` alias from entity_numbers, or 0 when the branch has not
161 // been indexed yet. It is a display alias; ID remains the identity.
162 Number int
143 163 }
144 164
145 165 func (s *Store) Close() error { return s.db.Close() }