vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0: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
core/git/git.go +44
| @@ -710,6 +710,50 @@ func (r *Repo) LogPath(rev, path string, n int, follow bool) ([]Commit, error) { | |||
| 710 | 710 | return parseCommits(out), nil | |
| 711 | 711 | } | |
| 712 | 712 | ||
| 713 | // addMarker starts each commit's block in AddedPathsPerCommit's output. It is a | ||
| 714 | // control character no path in a metadata tree can contain, so a path can never | ||
| 715 | // be mistaken for the separator. git takes it as a %x escape because a pretty | ||
| 716 | // format rejects a literal control byte. | ||
| 717 | const ( | ||
| 718 | addMarker = "\x01" | ||
| 719 | addMarkerSpec = "%x01" | ||
| 720 | ) | ||
| 721 | |||
| 722 | // AddedPathsPerCommit lists, oldest first, the paths each commit added, limited | ||
| 723 | // to the given pathspecs. A commit that added nothing under them is absent from | ||
| 724 | // the result, so a caller counting blocks is counting only commits that changed | ||
| 725 | // the tree it asked about. | ||
| 726 | func (r *Repo) AddedPathsPerCommit(rev string, pathspecs ...string) ([][]string, error) { | ||
| 727 | args := []string{"log", "--reverse", "--diff-filter=A", "--name-only", "--no-renames", | ||
| 728 | "--format=" + addMarkerSpec, rev, "--"} | ||
| 729 | args = append(args, pathspecs...) | ||
| 730 | out, errs, err := r.run(nil, args...) | ||
| 731 | if err != nil { | ||
| 732 | return nil, fmt.Errorf("log %q added paths: %w: %s", rev, err, errs) | ||
| 733 | } | ||
| 734 | var groups [][]string | ||
| 735 | var cur []string | ||
| 736 | flush := func() { | ||
| 737 | if len(cur) > 0 { | ||
| 738 | groups = append(groups, cur) | ||
| 739 | cur = nil | ||
| 740 | } | ||
| 741 | } | ||
| 742 | for _, line := range strings.Split(string(out), "\n") { | ||
| 743 | line = strings.TrimRight(line, "\r") | ||
| 744 | switch line { | ||
| 745 | case addMarker: | ||
| 746 | flush() | ||
| 747 | case "": | ||
| 748 | // git separates the format line and the names with blank lines. | ||
| 749 | default: | ||
| 750 | cur = append(cur, line) | ||
| 751 | } | ||
| 752 | } | ||
| 753 | flush() | ||
| 754 | return groups, nil | ||
| 755 | } | ||
| 756 | |||
| 713 | 757 | // BlameLine is one line of `git blame` output with the commit that last touched | |
| 714 | 758 | // it. Line is the line number in the blamed revision. | |
| 715 | 759 | type BlameLine struct { | |