From 13f5220f90ff59fb5936f5c159c6baea478cb098 Mon Sep 17 00:00:00 2001 From: josh-hemphill Date: Tue, 11 Nov 2025 19:13:56 -0800 Subject: [PATCH] feat: support limiting analyzed commits by path Passing paths to the end of the git log call, allows analyzing commits on a package-by-package basis in a monorepo --- src/cli.ts | 1 + src/config.ts | 3 +++ src/generate.ts | 2 +- src/git.ts | 34 ++++++++++++++++++++++++++++++++++ src/types.ts | 6 ++++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/cli.ts b/src/cli.ts index 74993b4..6def3c6 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -28,6 +28,7 @@ cli .option('--group', 'Nest commit messages under their scopes') .option('--dry', 'Dry run') .option('--assets ', 'Files to upload as assets to the release. Use quotes to prevent shell glob expansion, e.g., "--assets \'dist/*.js\'"') + .option('--commit-paths [paths...]', 'Paths to filter commits by. If true, CWD will be used as the path') .help() async function readTokenFromGitHubCli() { diff --git a/src/config.ts b/src/config.ts index dd596b0..5e89eca 100644 --- a/src/config.ts +++ b/src/config.ts @@ -45,6 +45,9 @@ export async function resolveConfig(options: ChangelogOptions) { // @ts-expect-error backward compatibility config.releaseRepo = config.releaseRepo || config.releaseGithub || config.repo config.prerelease = config.prerelease ?? isPrerelease(config.to) + config.commitPaths = config.commitPaths ?? [] + if (config.commitPaths === true) + config.commitPaths = [config.cwd ?? process.cwd()] if (typeof config.repo !== 'string') throw new Error(`Invalid GitHub repository, expected a string but got ${JSON.stringify(config.repo)}`) diff --git a/src/generate.ts b/src/generate.ts index f908728..8e14b6f 100644 --- a/src/generate.ts +++ b/src/generate.ts @@ -1,5 +1,5 @@ import type { ChangelogOptions } from './types' -import { getGitDiff } from 'changelogen' +import { getGitDiff } from './git' import { resolveConfig } from './config' import { resolveAuthors } from './github' import { generateMarkdown } from './markdown' diff --git a/src/git.ts b/src/git.ts index c530262..785242a 100644 --- a/src/git.ts +++ b/src/git.ts @@ -1,3 +1,4 @@ +import { RawGitCommit } from 'changelogen' import semver from 'semver' export async function getGitHubRepo(baseUrl: string) { @@ -79,6 +80,39 @@ export async function getLastMatchingTag( return tag } +export async function getGitDiff( + from: string, + to: string, + paths: string[] = [], +): Promise { + // https://git-scm.com/docs/pretty-formats + const r = await execCommand( + 'git', + [ + '--no-pager', 'log', + `${from ? `${from}...` : ''}${to}`, + '--pretty="----%n%s|%h|%an|%ae%n%b"', + '--name-status', + ...(paths.length > 0 ? ['--', ...paths] : []), + ], + ); + return r + .split("----\n") + .splice(1) + .map((line) => { + const [firstLine, ..._body] = line.split("\n"); + const [message, shortHash, authorName, authorEmail] = + firstLine.split("|"); + const r: RawGitCommit = { + message, + shortHash, + author: { name: authorName, email: authorEmail }, + body: _body.join("\n"), + }; + return r; + }); +} + export async function isRefGitTag(to: string) { const { execa } = await import('execa') try { diff --git a/src/types.ts b/src/types.ts index ed6e261..fa1b83c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -100,6 +100,12 @@ export interface ChangelogOptions extends Partial { * `--assets path1,path2` or `--assets path1 --assets path2` */ assets?: string[] | string + + /** + * Paths to filter commits by + * If true, CWD will be used as the path + */ + commitPaths?: string[] | true } export type ResolvedChangelogOptions = Required