build: exclude redundant cherry-picked commits when generating changelog

If generating a changelog between tags that are on different branches (e.g., 9.0.3 on 9.0.x and 9.1.0-next.0 on master), commits that were cherry-picked and present in the previous version would also show in the newer version's changelog.  This update analyzes the commits and excludes any that fit this scenario.  Any commits that had conflicts will not be able to be matched authoritatively.  Manual review of the generated changelog may still be needed for attempted cherry-pick commits that had conflicts.
This commit is contained in:
Charles Lyding 2020-02-21 18:13:54 -05:00 committed by Douglas Parker
parent 14dc4fb818
commit 9a6b05d56b

View File

@ -8,6 +8,7 @@
// tslint:disable:no-console
// tslint:disable:no-implicit-dependencies
import { JsonObject, logging } from '@angular-devkit/core';
import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
@ -29,7 +30,16 @@ export interface ChangelogOptions {
stdout?: boolean;
}
export default function(args: ChangelogOptions, logger: logging.Logger) {
function exec(command: string, input?: string): string {
return execSync(command, {
encoding: 'utf8',
stdio: 'pipe',
input,
maxBuffer: 10 * 1024 * 1024,
}).trim();
}
export default async function(args: ChangelogOptions, logger: logging.Logger) {
const commits: JsonObject[] = [];
let toSha: string | null = null;
@ -39,6 +49,35 @@ export default function(args: ChangelogOptions, logger: logging.Logger) {
''
).trim();
// Validate and scrub commit range options
const from = exec(`git rev-parse --verify "${args.from.replace(/"/g, '')}"`);
if (!from) {
logger.error(`"from" value [${args.from}] is invalid.`);
return;
}
const to = exec(`git rev-parse --verify "${args.to?.replace(/"/g, '') || 'HEAD'}"`);
if (!to) {
logger.error(`"to" value [${args.to}] is invalid.`);
return;
}
// Collect patch identifiers for cherry-pick exclusion
const cherryPicked = new Set<string>();
const patchIds = new Map<string, string>();
const hashes = exec(`git rev-list ${from}...${to}`).split(/\s+/);
for (const hash of hashes) {
const [patchId] = exec('git patch-id', exec('git show ' + hash)).split(/\s+/);
const existing = patchIds.get(patchId);
if (existing) {
cherryPicked.add(existing);
cherryPicked.add(hash);
} else {
patchIds.set(patchId, hash);
}
}
return new Promise(resolve => {
(gitRawCommits({
from: args.from,
@ -78,8 +117,9 @@ export default function(args: ChangelogOptions, logger: logging.Logger) {
if (tags && tags.find(x => x == args.to)) {
toSha = chunk.hash as string;
}
commits.push(chunk);
if (!cherryPicked.has(chunk.hash as string)) {
commits.push(chunk);
}
cb();
} catch (err) {
cb(err);