feat(cli): show which upstream versions are already merged in list-versions

Uses 'git merge-base --is-ancestor' per tag (parallelized) to mark
upstream versions reachable from HEAD with a '✓ merged' annotation.
Kilo's own package version no longer tracks upstream, so semver
comparison isn't reliable; the ancestor check is the source of truth
and handles non-linear merge history correctly.
This commit is contained in:
Mark IJbema
2026-04-28 11:46:43 +02:00
parent 8078944add
commit 38cc0297a8
2 changed files with 18 additions and 4 deletions
+8 -4
View File
@@ -7,7 +7,7 @@
*/
import { getAvailableUpstreamVersions, getCurrentKiloVersion } from "./utils/version"
import { fetchUpstream, hasUpstreamRemote } from "./utils/git"
import { fetchUpstream, hasUpstreamRemote, isAncestor } from "./utils/git"
import { header, info, success, warn, error } from "./utils/logger"
async function main() {
@@ -34,11 +34,15 @@ async function main() {
console.log()
const limit = process.argv.includes("--all") ? versions.length : 20
const shown = versions.slice(0, Math.min(limit, versions.length))
for (let i = 0; i < Math.min(limit, versions.length); i++) {
const v = versions[i]
// Check merge status in parallel — fast because is-ancestor short-circuits.
const merged = await Promise.all(shown.map((v) => isAncestor(v.commit, "HEAD")))
for (let i = 0; i < shown.length; i++) {
const v = shown[i]
if (!v) continue
const marker = i === 0 ? " (latest)" : ""
const marker = merged[i] ? " ✓ merged" : i === 0 ? " (latest)" : ""
console.log(` ${v.tag.padEnd(12)} ${v.commit.slice(0, 8)}${marker}`)
}
+10
View File
@@ -172,6 +172,16 @@ export async function getCommitHash(ref: string): Promise<string> {
return result.trim()
}
/**
* Check if `commit` is an ancestor of `ref` (i.e. reachable from `ref`).
* Uses `git merge-base --is-ancestor`, which exits 0 for yes, 1 for no.
* Any other exit code (e.g. unknown commit) is treated as "not an ancestor".
*/
export async function isAncestor(commit: string, ref = "HEAD"): Promise<boolean> {
const result = await $`git merge-base --is-ancestor ${commit} ${ref}`.quiet().nothrow()
return result.exitCode === 0
}
export async function getTagsForCommit(commit: string): Promise<string[]> {
const result = await $`git tag --points-at ${commit}`.text()
return result