Skip to content

Commit ec1b95e

Browse files
Copilotalexr00
andauthored
Trim Co-authored-by trailers from default PR description (microsoft#8749)
* Initial plan * Strip Co-authored-by trailers from default PR description Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent e8d7ee9 commit ec1b95e

3 files changed

Lines changed: 64 additions & 2 deletions

File tree

src/common/gitUtils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ import * as vscode from 'vscode';
77
import { Repository } from '../api/api';
88
import { GitApiImpl } from '../api/api1';
99

10+
/**
11+
* Removes `Co-authored-by:` trailer lines from a commit message body.
12+
*
13+
* These lines are typically added at the bottom of a commit message (for example by
14+
* the Copilot CLI or `git commit --trailer`) and are not useful when the commit body
15+
* is reused as the default PR description.
16+
*/
17+
export function stripCoAuthoredByTrailers(body: string): string {
18+
if (!body) {
19+
return body;
20+
}
21+
const lines = body.split('\n');
22+
const filtered = lines.filter(line => !/^\s*Co-authored-by:\s/i.test(line));
23+
// Remove any trailing blank lines that may have been left behind once the
24+
// trailer block is gone, but preserve internal blank lines / paragraph breaks.
25+
let end = filtered.length;
26+
while (end > 0 && filtered[end - 1].trim() === '') {
27+
end--;
28+
}
29+
return filtered.slice(0, end).join('\n');
30+
}
31+
1032
/**
1133
* Unwraps lines that were wrapped for conventional commit message formatting (typically at 72 characters).
1234
* Similar to GitHub's behavior when converting commit messages to PR descriptions.

src/github/folderRepositoryManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { AuthProvider, GitHubServerType } from '../common/authentication';
3434
import { commands, contexts } from '../common/executeCommands';
3535
import { InMemFileChange, SlimFileChange } from '../common/file';
3636
import { findLocalRepoRemoteFromGitHubRef } from '../common/githubRef';
37-
import { unwrapCommitMessageBody } from '../common/gitUtils';
37+
import { stripCoAuthoredByTrailers, unwrapCommitMessageBody } from '../common/gitUtils';
3838
import { Disposable, disposeAll } from '../common/lifecycle';
3939
import Logger from '../common/logger';
4040
import { Protocol, ProtocolType } from '../common/protocol';
@@ -3213,7 +3213,7 @@ export const titleAndBodyFrom = async (promise: Promise<string | undefined>): Pr
32133213
}
32143214
const idxLineBreak = message.indexOf('\n');
32153215
const hasBody = idxLineBreak !== -1;
3216-
const rawBody = hasBody ? message.slice(idxLineBreak + 1).trim() : '';
3216+
const rawBody = hasBody ? stripCoAuthoredByTrailers(message.slice(idxLineBreak + 1)).trim() : '';
32173217
return {
32183218
title: hasBody ? message.slice(0, idxLineBreak) : message,
32193219

src/test/github/folderRepositoryManager.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,4 +342,44 @@ describe('titleAndBodyFrom', function () {
342342
assert.strictEqual(result?.title, 'This is a test');
343343
assert.strictEqual(result?.body, '- A fslilenfilnf flen felslnf lsefl fnels Leknef Lkdfnle lfkenSlefn Lnkef LefnLienf LIfnels\n- B\n- C');
344344
});
345+
346+
it('strips Co-authored-by trailer lines from body', async function () {
347+
const message = Promise.resolve('title\n\nSome description.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>');
348+
349+
const result = await titleAndBodyFrom(message);
350+
assert.strictEqual(result?.title, 'title');
351+
assert.strictEqual(result?.body, 'Some description.');
352+
});
353+
354+
it('strips multiple Co-authored-by trailer lines', async function () {
355+
const message = Promise.resolve('title\n\nSome description.\n\nCo-authored-by: Alice <alice@example.com>\nCo-authored-by: Bob <bob@example.com>');
356+
357+
const result = await titleAndBodyFrom(message);
358+
assert.strictEqual(result?.title, 'title');
359+
assert.strictEqual(result?.body, 'Some description.');
360+
});
361+
362+
it('strips Co-authored-by case-insensitively', async function () {
363+
const message = Promise.resolve('title\n\nSome description.\n\nco-authored-by: Alice <alice@example.com>');
364+
365+
const result = await titleAndBodyFrom(message);
366+
assert.strictEqual(result?.title, 'title');
367+
assert.strictEqual(result?.body, 'Some description.');
368+
});
369+
370+
it('preserves other trailers when stripping Co-authored-by', async function () {
371+
const message = Promise.resolve('title\n\nSome description.\n\nSigned-off-by: Alice <alice@example.com>\nCo-authored-by: Bob <bob@example.com>');
372+
373+
const result = await titleAndBodyFrom(message);
374+
assert.strictEqual(result?.title, 'title');
375+
assert.strictEqual(result?.body, 'Some description.\n\nSigned-off-by: Alice <alice@example.com>');
376+
});
377+
378+
it('returns empty body when only Co-authored-by trailers are present', async function () {
379+
const message = Promise.resolve('title\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>');
380+
381+
const result = await titleAndBodyFrom(message);
382+
assert.strictEqual(result?.title, 'title');
383+
assert.strictEqual(result?.body, '');
384+
});
345385
});

0 commit comments

Comments
 (0)