Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Commit 81c6bf2

Browse files
authored
Merge pull request #1500 from atom/aw/commit-message-wrapping
Revisit commit message processing
2 parents f7d5687 + 99519fd commit 81c6bf2

File tree

7 files changed

+183
-61
lines changed

7 files changed

+183
-61
lines changed

lib/controllers/commit-controller.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,18 @@ export default class CommitController extends React.Component {
122122
this.subscriptions.dispose();
123123
}
124124

125-
commit(message, coAuthors = [], amend) {
126-
let msg;
125+
commit(message, coAuthors = [], amend = false) {
126+
let msg, verbatim;
127127
if (this.isCommitMessageEditorExpanded()) {
128128
msg = this.getCommitMessageEditors()[0].getText();
129+
verbatim = false;
129130
} else {
130131
const wrapMessage = this.props.config.get('github.automaticCommitMessageWrapping');
131132
msg = wrapMessage ? wrapCommitMessage(message) : message;
133+
verbatim = true;
132134
}
133135

134-
return this.props.commit(msg.trim(), {amend, coAuthors});
136+
return this.props.commit(msg.trim(), {amend, coAuthors, verbatim});
135137
}
136138

137139
setCommitMessage(message) {

lib/git-shell-out-strategy.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -437,19 +437,33 @@ export default class GitShellOutStrategy {
437437
return this.exec(args, {stdin: patch, writeOperation: true});
438438
}
439439

440-
async commit(rawMessage, {allowEmpty, amend, coAuthors} = {}) {
441-
const args = ['commit', '--cleanup=strip'];
442-
440+
async commit(rawMessage, {allowEmpty, amend, coAuthors, verbatim} = {}) {
441+
const args = ['commit'];
443442
let msg;
444443

445-
// if amending and no new message is passed, use last commit's message
444+
// if amending and no new message is passed, use last commit's message. Ensure that we don't
445+
// mangle it in the process.
446446
if (amend && rawMessage.length === 0) {
447447
const {unbornRef, messageBody, messageSubject} = await this.getHeadCommit();
448-
msg = unbornRef ? rawMessage : `${messageSubject}\n\n${messageBody}`.trim();
448+
if (unbornRef) {
449+
msg = rawMessage;
450+
} else {
451+
msg = `${messageSubject}\n\n${messageBody}`.trim();
452+
verbatim = true;
453+
}
449454
} else {
450455
msg = rawMessage;
451456
}
452457

458+
// Determine the cleanup mode.
459+
if (verbatim) {
460+
args.push('--cleanup=verbatim');
461+
} else {
462+
const configured = await this.getConfig('commit.cleanup');
463+
const mode = (configured && configured !== 'default') ? configured : 'strip';
464+
args.push(`--cleanup=${mode}`);
465+
}
466+
453467
// add co-author commit trailers if necessary
454468
if (coAuthors && coAuthors.length > 0) {
455469
msg = await this.addCoAuthorsToMessage(msg, coAuthors);

lib/helpers.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -332,19 +332,21 @@ export function destroyEmptyFilePatchPaneItems(workspace) {
332332
}
333333

334334
export function extractCoAuthorsAndRawCommitMessage(commitMessage) {
335-
let rawMessage = '';
336-
const coAuthors = commitMessage.split(LINE_ENDING_REGEX).reduce((authors, line) => {
335+
const messageLines = [];
336+
const coAuthors = [];
337+
338+
for (const line of commitMessage.split(LINE_ENDING_REGEX)) {
337339
const match = line.match(CO_AUTHOR_REGEX);
338340
if (match) {
339341
// eslint-disable-next-line no-unused-vars
340342
const [_, name, email] = match;
341-
authors.push({name, email});
343+
coAuthors.push({name, email});
342344
} else {
343-
rawMessage += line;
345+
messageLines.push(line);
344346
}
345-
return authors;
346-
}, []);
347-
return {message: rawMessage, coAuthors};
347+
}
348+
349+
return {message: messageLines.join('\n'), coAuthors};
348350
}
349351

350352
export function createItem(node, componentHolder = null, uri = null, extra = {}) {

lib/models/repository.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export default class Repository {
132132
async getMergeMessage() {
133133
try {
134134
const contents = await fs.readFile(path.join(this.getGitDirectoryPath(), 'MERGE_MSG'), {encoding: 'utf8'});
135-
return contents;
135+
return contents.split(/\n/).filter(line => line.length > 0 && !line.startsWith('#')).join('\n');
136136
} catch (e) {
137137
return null;
138138
}

test/controllers/commit-controller.test.js

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,12 @@ describe('CommitController', function() {
117117
});
118118

119119
describe('committing', function() {
120-
let workdirPath, repository;
120+
let workdirPath, repository, commit;
121121

122122
beforeEach(async function() {
123123
workdirPath = await cloneRepository('three-files');
124124
repository = await buildRepositoryWithPipeline(workdirPath, {confirm, notificationManager, workspace});
125-
const commit = message => repository.commit(message);
125+
commit = sinon.stub().callsFake((...args) => repository.commit(...args));
126126

127127
app = React.cloneElement(app, {repository, commit});
128128
});
@@ -139,6 +139,20 @@ describe('CommitController', function() {
139139
assert.strictEqual(repository.getCommitMessage(), '');
140140
});
141141

142+
it('sets the verbatim flag when committing from the mini editor', async function() {
143+
await fs.writeFile(path.join(workdirPath, 'a.txt'), 'some changes', {encoding: 'utf8'});
144+
await repository.git.exec(['add', '.']);
145+
146+
const wrapper = shallow(app, {disableLifecycleMethods: true});
147+
await wrapper.instance().commit('message\n\n#123 do some things');
148+
149+
assert.isTrue(commit.calledWith('message\n\n#123 do some things', {
150+
amend: false,
151+
coAuthors: [],
152+
verbatim: true,
153+
}));
154+
});
155+
142156
it('issues a notification on failure', async function() {
143157
repository.setCommitMessage('some message');
144158

@@ -159,44 +173,58 @@ describe('CommitController', function() {
159173
});
160174

161175
describe('message formatting', function() {
162-
let commitSpy;
176+
let commitSpy, wrapper;
177+
163178
beforeEach(function() {
164179
commitSpy = sinon.stub().returns(Promise.resolve());
165180
app = React.cloneElement(app, {commit: commitSpy});
181+
wrapper = shallow(app, {disableLifecycleMethods: true});
166182
});
167183

168-
it('wraps the commit message body at 72 characters if github.automaticCommitMessageWrapping is true', async function() {
169-
config.set('github.automaticCommitMessageWrapping', false);
184+
describe('with automatic wrapping disabled', function() {
185+
beforeEach(function() {
186+
config.set('github.automaticCommitMessageWrapping', false);
187+
});
170188

171-
const wrapper = shallow(app, {disableLifecycleMethods: true});
189+
it('passes commit messages through unchanged', async function() {
190+
await wrapper.instance().commit([
191+
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
192+
'',
193+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
194+
].join('\n'));
195+
196+
assert.strictEqual(commitSpy.args[0][0], [
197+
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
198+
'',
199+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
200+
].join('\n'));
201+
});
202+
});
172203

173-
await wrapper.instance().commit([
174-
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
175-
'',
176-
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
177-
].join('\n'));
178-
179-
assert.deepEqual(commitSpy.args[0][0].split('\n'), [
180-
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
181-
'',
182-
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
183-
]);
184-
185-
commitSpy.reset();
186-
config.set('github.automaticCommitMessageWrapping', true);
187-
188-
await wrapper.instance().commit([
189-
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
190-
'',
191-
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
192-
].join('\n'));
193-
194-
assert.deepEqual(commitSpy.args[0][0].split('\n'), [
195-
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
196-
'',
197-
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ',
198-
'ut aliquip ex ea commodo consequat.',
199-
]);
204+
describe('with automatic wrapping enabled', function() {
205+
beforeEach(function() {
206+
config.set('github.automaticCommitMessageWrapping', true);
207+
});
208+
209+
it('wraps lines within the commit body at 72 characters', async function() {
210+
await wrapper.instance().commit([
211+
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
212+
'',
213+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
214+
].join('\n'));
215+
216+
assert.strictEqual(commitSpy.args[0][0], [
217+
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
218+
'',
219+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ',
220+
'ut aliquip ex ea commodo consequat.',
221+
].join('\n'));
222+
});
223+
224+
it('preserves existing line wraps within the commit body', async function() {
225+
await wrapper.instance().commit('a\n\nb\n\nc');
226+
assert.strictEqual(commitSpy.args[0][0], 'a\n\nb\n\nc');
227+
});
200228
});
201229
});
202230

@@ -292,10 +320,14 @@ describe('CommitController', function() {
292320
const editor = workspace.getActiveTextEditor();
293321
editor.setText('message in editor');
294322
await editor.save();
295-
wrapper.find('CommitView').prop('commit')('message in box');
296323

297-
await assert.async.strictEqual((await repository.getLastCommit()).getMessageSubject(), 'message in editor');
298-
await assert.async.isFalse(fs.existsSync(wrapper.instance().getCommitMessagePath()));
324+
await wrapper.find('CommitView').prop('commit')('message in box');
325+
326+
assert.strictEqual((await repository.getLastCommit()).getMessageSubject(), 'message in editor');
327+
assert.isFalse(fs.existsSync(wrapper.instance().getCommitMessagePath()));
328+
assert.isTrue(commit.calledWith('message in editor', {
329+
amend: false, coAuthors: [], verbatim: false,
330+
}));
299331
});
300332

301333
it('asks user to confirm if commit editor has unsaved changes', async function() {

test/controllers/git-tab-controller.test.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,10 @@ describe('GitTabController', function() {
572572
assert.strictEqual(wrapper.find('CommitView').instance().editor.getText(), '');
573573

574574
commandRegistry.dispatch(workspaceElement, 'github:amend-last-commit');
575-
await assert.async.deepEqual(repository.commit.args[0][1], {amend: true, coAuthors: []});
575+
await assert.async.deepEqual(
576+
repository.commit.args[0][1],
577+
{amend: true, coAuthors: [], verbatim: true},
578+
);
576579

577580
// amending should commit all unstaged changes
578581
await updateWrapper(repository, wrapper);
@@ -594,7 +597,10 @@ describe('GitTabController', function() {
594597
assert.lengthOf(wrapper.find('GitTabView').prop('stagedChanges'), 0);
595598

596599
commandRegistry.dispatch(workspaceElement, 'github:amend-last-commit');
597-
await assert.async.deepEqual(repository.commit.args[0][1], {amend: true, coAuthors: []});
600+
await assert.async.deepEqual(
601+
repository.commit.args[0][1],
602+
{amend: true, coAuthors: [], verbatim: true},
603+
);
598604
await updateWrapper(repository, wrapper);
599605

600606
// new commit message is used
@@ -617,7 +623,10 @@ describe('GitTabController', function() {
617623

618624
commandRegistry.dispatch(workspaceElement, 'github:amend-last-commit');
619625
// verify that coAuthor was passed
620-
await assert.async.deepEqual(repository.commit.args[0][1], {amend: true, coAuthors: [author]});
626+
await assert.async.deepEqual(
627+
repository.commit.args[0][1],
628+
{amend: true, coAuthors: [author], verbatim: true},
629+
);
621630
await repository.commit.returnValues[0];
622631
await updateWrapper(repository, wrapper);
623632

@@ -640,7 +649,10 @@ describe('GitTabController', function() {
640649
commandRegistry.dispatch(workspaceElement, 'github:amend-last-commit');
641650

642651
// verify that coAuthor was passed
643-
await assert.async.deepEqual(repository.commit.args[0][1], {amend: true, coAuthors: [author]});
652+
await assert.async.deepEqual(
653+
repository.commit.args[0][1],
654+
{amend: true, coAuthors: [author], verbatim: true},
655+
);
644656
await repository.commit.returnValues[0];
645657
await updateWrapper(repository, wrapper);
646658

@@ -673,7 +685,10 @@ describe('GitTabController', function() {
673685
// amend again
674686
commandRegistry.dispatch(workspaceElement, 'github:amend-last-commit');
675687
// verify that NO coAuthor was passed
676-
await assert.async.deepEqual(repository.commit.args[0][1], {amend: true, coAuthors: []});
688+
await assert.async.deepEqual(
689+
repository.commit.args[0][1],
690+
{amend: true, coAuthors: [], verbatim: true},
691+
);
677692
await repository.commit.returnValues[0];
678693
await updateWrapper(repository, wrapper);
679694

0 commit comments

Comments
 (0)