Tucker McKnight
Allow all branch names to be glob patterns Change the way that branches are matched to those patterns so that, if multiple patterns match a branch name, the most specific pattern is the one that is used. Also allows new fields on a branch: description and compareTo. Prints out the branch description on the branches page. Also makes the max value work -- kicks out branches from the list if there are already the max number of branches in the list. TODO: need to make sure this only kicks out the branches that are the oldest (that is, not recently committed to).
19
<div class="card-body">Data about branch goes here</div>
19
<div class="card-body">${branch.description || ''}</div>
14 15 16
"glob": {
"glob",
"max"
14 15 16
"compareTo": {
"type": "string"
},
"description": {
},
"pattern": {
"type": "string"
"pattern"
10
return {
10
const result = {
if (branch.description) { result['description'] = branch.description }
if (branch.compareTo) { result['compareTo'] = branch.compareTo }
return result
11
* branches: ['main', 'develop']
11
* branchesToPull: ['main', 'develop']
53
branchesToPull: Array<string | {glob: string, max: number}>,
53
branchesToPull: Array<string | {pattern: string, max?: number, compareTo?: string, description?: string}>,
5
5
description?: string,
compareTo?: string,
14 15
const branchesForReposMap: Map<string, string[]> = new Map()
const getBranchNames = async (repoConfig: GitConfig, repoName: string): Promise<Array<string>> => {
14 15
const branchesForReposMap: Map<string, Array<{name: string, description?: string, compareTO?: string}>> = new Map()
const getBranches = async (repoConfig: GitConfig, repoName: string): Promise<Array<{name: string, description?: string, compareTo?: string}>> => {
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
const matchingBranchesFromGlobs: Map<string, string[]> = new Map()
const literalBranchNames: string[] = []
repoConfig.branchesToPull.forEach((branch) => {
if (typeof branch === "string") {
literalBranchNames.push(branch)
else {
// If we're here, then the "branch" was an object like:
// { glob: string, max: number }
// TODO: figure out which branch is the newest/oldest, and
// keep them less than `max` by kicking out the oldest from the list.
const glob = branch['glob']
const matching = allBranches.filter((possibleBranch) => {
const match = path.matchesGlob(possibleBranch, glob)
return match
})
matchingBranchesFromGlobs.set(
glob,
(matchingBranchesFromGlobs.get(glob) || []).concat(matching)
)
const matchedBranchesFromGlobs = Array.from(matchingBranchesFromGlobs.values()).flat()
const branchNames: string[] = Array.from(new Set(matchedBranchesFromGlobs.concat(literalBranchNames)))
branchesForReposMap.set(repoName, branchNames)
return branchNames
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
// Sort the list of branch descriptions from branchesToPull by the length
// of their patterns.
// Then, for each branch from the repository, see if it matches a pattern.
// If that pattern has rules associated with it (like a description or a max),
// apply those.
type RulesObject = {max?: number, description?: string, compareTo?: string}
let branchRules: Array<{pattern: string, matches: Array<string>, rules: RulesObject}> = repoConfig.branchesToPull.map((branchDescription) => {
const rules: RulesObject = {}
if (typeof branchDescription !== 'string') {
if (branchDescription.max) { rules.max = branchDescription.max }
if (branchDescription.description) { rules.description = branchDescription.description }
if (branchDescription.compareTo) { rules.compareTo = branchDescription.compareTo }
return {
pattern: (typeof branchDescription === 'string' ? branchDescription : branchDescription.pattern),
matches: [],
rules,
}
})
branchRules = branchRules.toSorted((a, b) => b.pattern.length - a.pattern.length)
allBranches.forEach((branchName) => {
const matchingPatternIndex = branchRules.findIndex((branchRule) => {
return path.matchesGlob(branchName, branchRule.pattern)
})
if (matchingPatternIndex === -1) { return }
const matchedRule = branchRules[matchingPatternIndex]
matchedRule.matches.push(branchName)
if (
matchedRule.rules?.max
&& matchedRule.rules?.max < matchedRule.matches.length
) {
matchedRule.matches.pop()
const branches: Array<{
name: string,
description?: string,
compareTo?: string,
}> = branchRules.map((branchRule) => {
return branchRule.matches.map((match) => {
const result = {name: match}
if (branchRule.rules.description) { result['description'] = branchRule.rules.description }
if (branchRule.rules.compareTo) { result['compareTo'] = branchRule.rules.compareTo }
return result
}).flat()
}).flat()
branchesForReposMap.set(repoName, branches)
return branches
}
const getBranchNames = async (repoConfig, repoName) => {
return (await getBranches(repoConfig, repoName)).map(branch => branch.name)
68 69 70 71 72 73 74
const branchNames = await getBranchNames(reposConfig.repos[repoName], repoName)
for (const branchName of branchNames) {
const branches = await Promise.all(branchNames.map(async (branchName) => {
const branchHeadRes = await exec(`git -C ${repoLocation} show-ref refs/heads/${branchName}`)
return {
name: branchName,
fileList: await getFileList(branchName, repoLocation)
68 69 70 71 72 73 74
const branchesToAdd = await getBranches(reposConfig.repos[repoName], repoName)
for (const branchName of await getBranchNames(reposConfig.repos[repoName], repoName)) {
const branches = await Promise.all(branchesToAdd.map(async (branch) => {
const branchHeadRes = await exec(`git -C ${repoLocation} show-ref refs/heads/${branch.name}`)
const result = {
name: branch.name,
fileList: await getFileList(branch.name, repoLocation)
if (branch.description) { result['description'] = branch.description }
if (branch.compareTo) { result['compareTo'] = branch.compareTo }
return result
42
42
## Jan 18, 2026
*An idea:* allow the object with a glob in it to also specify things like the description
and which parent branch the branch should be compared to.
Multiple branch patterns (let's use the word "pattern" instead of "glob") can match
the same branch -- the more specific one should be used.
E.g.:
```
branchesToPull: [
{
pattern: "deploy/**",
description: "A deployed branch",
},
{
pattern: "deploy/v2**",
description: "A version 2 deployed branch",
}
]
```
In the above example, branch `deploy/v1.0.1` should have the description "A deployed
branch," but branch `deploy/v2.0.1` should have the description "A version 2
deployed branch."
Is the "more specific pattern" just always the one that is longer? Try this for now.