Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/extractors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,18 @@ describe("extractPullRequestNumbersForCommit", () => {
expect(result).toEqual([]);
});

// A squash revert-of-revert re-applies the change, so its PR is attributed like a normal
// merge; a plain (or triple) revert removes it and is skipped. Keyed on revert depth parity,
// since squash messages (`Revert "Revert "…"" (#N)`) carry the nesting in the title, not a branch.
it.each([
['Revert "Title" (#79504)', [], "plain squash revert (odd depth)"],
['Revert "Revert "Title"" (#79620)', [79620], "squash revert-of-revert (even depth)"],
['Revert "Revert "Revert "Title""" (#5)', [], "triple squash revert (odd depth)"],
])("message %j should yield %j (%s)", (message, expected) => {
const result = extractPullRequestNumbersForCommit({ sha: "abc", message });
expect(result).toEqual(expected);
});

// Numbers above the GraphQL Int (32-bit) bound cannot be GitHub PR numbers
// and must be filtered so they don't poison the release sync mutation.
it.each([
Expand Down
22 changes: 19 additions & 3 deletions src/extractors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,10 @@ export function extractPullRequestNumbersForCommit(commit: CommitContext): numbe

const rawMessage = commit.message ?? "";

// Reverts reference the original PR, not a new one.
if (/^Revert "/i.test(rawMessage)) {
verbose(`Skipping revert commit ${commit.sha} with message: "${rawMessage}"`);
// Odd depth = a net revert; its `(#N)` is the revert's own PR, undoing prior work, so skip it.
// Even depth (revert-of-revert) re-applies the change and must attribute its PR like a normal merge.
if (leadingRevertDepth(rawMessage) % 2 === 1) {
verbose(`Skipping odd-depth revert commit ${commit.sha} with message: "${rawMessage}"`);
return [];
}
if (getRevertBranchDepth(commit.branchName) % 2 === 1) {
Expand Down Expand Up @@ -453,6 +454,21 @@ export function getRevertMessageDepth(message: string | null | undefined): numbe
return parseRevertMessage(message).depth;
}

/**
* Count leading `Revert "` wrappers. Unlike {@link getRevertMessageDepth} this does not
* require balanced quotes on the subject line, so a revert whose quoted title spans
* multiple lines (e.g. reverting a GitLab merge commit) still counts as depth 1.
*/
function leadingRevertDepth(message: string): number {
let text = message;
let depth = 0;
while (/^Revert "/i.test(text)) {
depth++;
text = text.replace(/^Revert "/i, "");
}
return depth;
}

/** Extract identifiers being reverted. Returns [] if not an odd-depth revert. */
export function extractRevertedIssueIdentifiersForCommit(commit: CommitContext): ExtractedIdentifier[] {
if (!commit) return [];
Expand Down
Loading