Skip to content

fix: map kebab-case first-parent arg to camelCase for git-client v5 (#4704)#4706

Open
llamington wants to merge 1 commit into
conventional-changelog:masterfrom
llamington:fix/map-git-log-args-to-camel-case
Open

fix: map kebab-case first-parent arg to camelCase for git-client v5 (#4704)#4706
llamington wants to merge 1 commit into
conventional-changelog:masterfrom
llamington:fix/map-git-log-args-to-camel-case

Conversation

@llamington

@llamington llamington commented Apr 7, 2026

Copy link
Copy Markdown

Description

This PR was made to fix #4704

When --first-parent is passed via --git-log-args, minimist parses it into { 'first-parent': true, _: [] }. However, @conventional-changelog/git-client v5 expects the camelCase key firstParent. This change:

  1. Destructures and discards the _ array that minimist always adds, so it isn't spread into the git client options.
  2. Maps the kebab-case first-parent key to the camelCase firstParent key expected by the git client.

Motivation and Context

@conventional-changelog/git-client re-added firstParent support in conventional-changelog/conventional-changelog#1450, but it expects a camelCase key. Since minimist parses --first-parent into kebab-case ({ 'first-parent': true }), commitlint needs to map it to firstParent for the git client to recognise it.

Usage examples

// commitlint.config.js
module.exports = {};
echo "your commit message here" | commitlint --from=main --git-log-args="--first-parent" # now correctly passes firstParent to git log

How Has This Been Tested?

  • Verified locally that --first-parent is correctly mapped to firstParent and passed through to the git client.
  • Confirmed the _ array from minimist is no longer spread into git options.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

…onventional-changelog#4704)

minimist parses `--first-parent` into `{ 'first-parent': true }`, but
@conventional-changelog/git-client expects camelCase (`firstParent`)
as of conventional-changelog/conventional-changelog#1450

Also strips the `_` array that minimist always produces, preventing it
from being passed through to the git client.
@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Map kebab-case first-parent to camelCase for git-client v5

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Maps kebab-case first-parent argument to camelCase firstParent
• Removes minimist's _ array from git client options
• Ensures compatibility with @conventional-changelog/git-client v5
Diagram
flowchart LR
  A["minimist parses --first-parent"] -->|"kebab-case: first-parent"| B["Destructure and map"]
  B -->|"Remove _ array"| C["Extract first-parent"]
  C -->|"Convert to camelCase"| D["firstParent for git-client"]
Loading

Grey Divider

File Changes

1. @commitlint/read/src/read.ts 🐞 Bug fix +3/-1

Map git-log-args to camelCase format

• Destructures minimist result to separate _ array from parsed arguments
• Maps kebab-case first-parent key to camelCase firstParent for git client compatibility
• Prevents minimist's _ array from being spread into git options

@commitlint/read/src/read.ts


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Apr 7, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2)   📘 Rule violations (0)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (1) ⚙ Maintainability (1)

Grey Divider


Action required

1. firstParent overwritten to undefined 🐞
Description
In getCommitMessages(), firstParent is always assigned from parsedArgs["first-parent"] after
spreading parsedArgs, which overwrites any parsedArgs.firstParent value (e.g., from
--firstParent) with undefined. This makes --git-log-args parsing silently drop a valid
firstParent setting and always injects a firstParent property when gitLogArgs is set.
Code

@commitlint/read/src/read.ts[R74-78]

+		const { _, ...parsedArgs } = minimist(gitLogArgs.split(" "));
		gitOptions = {
-			...minimist(gitLogArgs.split(" ")),
+			...parsedArgs,
+			firstParent: parsedArgs["first-parent"],
			from,
Evidence
The code spreads all minimist-parsed keys into gitOptions and then unconditionally reassigns
firstParent from the kebab-case key; if the kebab-case key is absent, it overwrites any existing
camelCase firstParent value with undefined. The CLI explicitly allows passing arbitrary
space-separated git log arguments, so different flag spellings can realistically occur.

@commitlint/read/src/read.ts[72-80]
docs/reference/cli.md[31-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`firstParent` is always overwritten with `parsedArgs["first-parent"]`, which clobbers an existing `parsedArgs.firstParent` value and also adds `firstParent: undefined` whenever `gitLogArgs` is provided.

### Issue Context
`--git-log-args` is an arbitrary space-separated string. The code should map kebab-case `first-parent` to camelCase `firstParent` **only when present**, and should not overwrite an existing `firstParent` value.

### Fix Focus Areas
- @commitlint/read/src/read.ts[72-81]

### Suggested approach
- Extract `first-parent` via object rest (`const { ['first-parent']: firstParentKebab, ...rest } = parsedArgs`) so the kebab key is removed from the spread.
- Build `gitOptions` with:
 - `...rest`
 - `firstParent` set **only if** `firstParentKebab !== undefined`, otherwise preserve any existing `rest.firstParent`.
 - `from`/`to` last as today.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Missing coverage for mapping 🐞
Description
There is no test asserting that --git-log-args="--first-parent" results in firstParent: true
being passed into history reading, nor that minimist’s _ positional array is excluded. This leaves
the new behavior (and the regression described above) unprotected.
Code

@commitlint/read/src/read.ts[R74-80]

+		const { _, ...parsedArgs } = minimist(gitLogArgs.split(" "));
		gitOptions = {
-			...minimist(gitLogArgs.split(" ")),
+			...parsedArgs,
+			firstParent: parsedArgs["first-parent"],
			from,
			to,
		};
Evidence
The only test exercising gitLogArgs uses --skip 1; no test covers --first-parent/firstParent
mapping or verifies that _ is not forwarded.

@commitlint/read/src/read.test.ts[56-74]
@commitlint/read/src/read.ts[72-81]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
No tests cover the new `first-parent` → `firstParent` mapping or the stripping of minimist’s `_` positional array.

### Issue Context
A lightweight way to test this without relying on git history shape is to mock `getHistoryCommits` and assert the options object it receives.

### Fix Focus Areas
- @commitlint/read/src/read.test.ts[1-200]
- @commitlint/read/src/read.ts[72-81]

### Suggested tests
- Mock `./get-history-commits.js` and capture the `gitOptions` argument.
- Call `read({ cwd, gitLogArgs: "--first-parent" })` and assert:
 - `gitOptions.firstParent === true`
 - `gitOptions` does not contain the `_` key
 - (after fixing mapping) `gitOptions` does not contain the `first-parent` key
- Add a regression test for `gitLogArgs: "--firstParent"` asserting `gitOptions.firstParent === true` (prevents clobbering).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +74 to 78
const { _, ...parsedArgs } = minimist(gitLogArgs.split(" "));
gitOptions = {
...minimist(gitLogArgs.split(" ")),
...parsedArgs,
firstParent: parsedArgs["first-parent"],
from,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Firstparent overwritten to undefined 🐞 Bug ≡ Correctness

In getCommitMessages(), firstParent is always assigned from parsedArgs["first-parent"] after
spreading parsedArgs, which overwrites any parsedArgs.firstParent value (e.g., from
--firstParent) with undefined. This makes --git-log-args parsing silently drop a valid
firstParent setting and always injects a firstParent property when gitLogArgs is set.
Agent Prompt
### Issue description
`firstParent` is always overwritten with `parsedArgs["first-parent"]`, which clobbers an existing `parsedArgs.firstParent` value and also adds `firstParent: undefined` whenever `gitLogArgs` is provided.

### Issue Context
`--git-log-args` is an arbitrary space-separated string. The code should map kebab-case `first-parent` to camelCase `firstParent` **only when present**, and should not overwrite an existing `firstParent` value.

### Fix Focus Areas
- @commitlint/read/src/read.ts[72-81]

### Suggested approach
- Extract `first-parent` via object rest (`const { ['first-parent']: firstParentKebab, ...rest } = parsedArgs`) so the kebab key is removed from the spread.
- Build `gitOptions` with:
  - `...rest`
  - `firstParent` set **only if** `firstParentKebab !== undefined`, otherwise preserve any existing `rest.firstParent`.
  - `from`/`to` last as today.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes commitlint’s --git-log-args="--first-parent" behavior by ensuring options parsed from minimist are compatible with @conventional-changelog/git-client v5 (camelCase firstParent) and by preventing minimist’s _ array from being spread into git options.

Changes:

  • Parse gitLogArgs with minimist and exclude the _ positional-args array from the options spread.
  • Map --first-parent (kebab-case) to firstParent (camelCase) when building git options.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


let gitOptions: GitOptions = { from, to };
if (gitLogArgs) {
const { _, ...parsedArgs } = minimist(gitLogArgs.split(" "));

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const { _, ...parsedArgs } = … introduces an unused local (_). With noUnusedLocals: true in the repo TS config, this will fail type-check/build unless _ is used or removed. Consider deleting ._ from the minimist result instead of destructuring it, or explicitly marking the binding as used (e.g. void _) so the compiler doesn’t flag it.

Suggested change
const { _, ...parsedArgs } = minimist(gitLogArgs.split(" "));
const parsedArgs = minimist(gitLogArgs.split(" "));
delete parsedArgs._;

Copilot uses AI. Check for mistakes.
Comment on lines +77 to 80
firstParent: parsedArgs["first-parent"],
from,
to,
};

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

firstParent is always set from parsedArgs["first-parent"]. If the user passes the already-camel-cased --firstParent, minimist will set parsedArgs.firstParent = true but parsedArgs["first-parent"] will be undefined, and this code will overwrite firstParent to undefined in gitOptions. Update the mapping so firstParent is only derived from the kebab-case key when it’s actually present, and avoid clobbering an existing firstParent value.

Suggested change
firstParent: parsedArgs["first-parent"],
from,
to,
};
from,
to,
};
if (Object.prototype.hasOwnProperty.call(parsedArgs, "first-parent")) {
gitOptions.firstParent = parsedArgs["first-parent"];
}

Copilot uses AI. Check for mistakes.
Comment on lines 75 to +77
gitOptions = {
...minimist(gitLogArgs.split(" ")),
...parsedArgs,
firstParent: parsedArgs["first-parent"],

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After mapping "first-parent" to firstParent, parsedArgs still contains the original "first-parent" key and it will be spread into gitOptions. Since git-raw-commits/git-client v5 dropped support for arbitrary git log args, it’s better to remove the kebab-case key after mapping to avoid passing unknown options downstream.

Copilot uses AI. Check for mistakes.
Comment on lines 73 to 80
if (gitLogArgs) {
const { _, ...parsedArgs } = minimist(gitLogArgs.split(" "));
gitOptions = {
...minimist(gitLogArgs.split(" ")),
...parsedArgs,
firstParent: parsedArgs["first-parent"],
from,
to,
};

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change adds new behavior for gitLogArgs parsing (dropping minimist’s _ and mapping --first-parent to firstParent), but @commitlint/read/src/read.test.ts currently only covers --skip. Add a test case that verifies --git-log-args='--first-parent' results in first-parent history only (or at least that the correct option is passed through), to prevent regressions like #4704.

Copilot uses AI. Check for mistakes.
@escapedcat

Copy link
Copy Markdown
Member

Thanks! Please have a look at the AI feedback, tackle if valid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

--git-log-args='--first-parent' silently ignored since v20

3 participants