stacks

package
v0.1.0-rc.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 4 Imported by: 0

README

stacks

Detects stacked pull requests: a chain or tree of a repository's pull requests, each based on another's head branch (A ← B ← C). Plan is a pure function from the candidate pull requests and the stored stacks to the changes that bring the stacks in line. The hosted platform stores stacks in PostgreSQL; the local app keeps them as review groups (internal/reviewgroups, "Stack groups").

Detection

  • B's parent is A when both are in the same repository, neither comes from a fork (is_cross_repository), both have the same author, and B's base_ref equals A's head_ref. The author rule keeps a shared branch's own pull request (develop into main) from gathering everyone's pull requests based on that branch. Branch chaining is the only signal: no Graphite, ghstack or spr markers, and stacks that each target the default branch are not detected.
  • A stack is a connected group of at least two such pull requests. A new stack forms only when at least two of them are open. Two children of one parent give one stack shaped as a tree.
  • A recompute considers the repository's open pull requests and the current members of its stacks that have not ended, or that have an open member. A merged or closed pull request that is not already a member never takes part, so a single open pull request based on the branch of one merged before detection forms no stack.
  • When several pull requests share the head branch an open one is based on, its stored parent wins, then the open one with the lowest number, then the lowest number. Branches naming each other in a loop lose the parent link of the loop's lowest-numbered pull request.
  • plan.go holds the rules as a pure function (Plan): from the candidates and the stored stacks to the rows to write. Planning again after applying them gives no change.

Identity and membership

As the hosted platform stores stacks, with every member and its parent. The local app plans only open pull requests over its stack groups, which store no parents, and dissolves a group where a hosted stack ends.

  • Members are recorded by pull request number with their parent's number (NULL for the root). A pull request is a current member (left_at IS NULL) of at most one stack; leaving keeps the row, and joining again clears left_at and restarts joined_at.
  • A merged or closed member stays in its original position, with its parent, so membership is never recomputed from the open pull requests alone. A member closed without merging stays as closed, not as merged: the consumers tell them apart by the pull request's state. When the bottom pull request merges and GitHub retargets the next one to the bottom's base, the next one keeps its merged parent and stays in the stack. If several descendants are automatically retargeted to the merged ancestor's base, they keep their stored chain, even when the descendant updates arrive before their parents' updates.
  • Any other retarget of an open member loses its parent: without a merged ancestor explaining the new base, after the parent was closed without merging, or onto a branch other than the merged ancestor's base. If that leaves the member outside the part that keeps the stack, it gets left_at; the rest go on.
  • Each stack goes to at most one recomputed group, oldest stack first:
    • a group joining several stacks keeps the oldest id, and the others end with every member left;
    • a stack that split goes to the part sharing the most of its members (then the larger part, then the part with the lowest number). The other part leaves it and forms a new stack if at least two of its pull requests are open;
    • a stack left with fewer than two members ends with every member left.
  • ended_at is set when every current member is merged or closed and cleared when one reopens. Such a stack keeps its members, so its page stays readable.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Candidate

type Candidate struct {
	Number          int
	Open            bool
	Merged          bool
	CrossRepository bool
	BaseRef         string
	HeadRef         string
	// Author is the author's login. A pull request is stacked only on one by
	// the same author, so a shared branch's own pull request (develop into
	// main) does not gather everyone's pull requests based on that branch.
	Author string
}

Candidate is a pull request a recompute considers: one that is open, or a current member of a stack the recompute can change.

type Changes

type Changes struct {
	Created []NewStack
	Updated []StackChange
}

Changes is what a recompute writes. Leaves are applied before joins, so a member moving between stacks is never current in two.

func Plan

func Plan(candidates []Candidate, stored []StoredStack) Changes

Plan computes the changes that bring a repository's stored stacks in line with its pull requests (README.md, "Detection" and "Identity"). It is pure: the same input always gives the same changes, and applying them and planning again gives none.

func (Changes) Empty

func (c Changes) Empty() bool

type NewStack

type NewStack struct {
	Members []Placement
}

type Placement

type Placement struct {
	Number int
	Parent *int
}

Placement is a member's place in a stack: its number and its parent's, nil for the root.

type StackChange

type StackChange struct {
	ID    string
	Join  []Placement
	Leave []int
	Ended *bool
}

StackChange changes one stored stack. Join adds members, returns members that had left and changes parents. Ended, when set, is the new ended state.

type StoredStack

type StoredStack struct {
	ID        string
	CreatedAt time.Time
	Ended     bool
	Members   []Placement
}

StoredStack is a stack as stored, with its current members only.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL