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

Commit 9b94879

Browse files
committed
Merge pull request #1500 from atom/aw/commit-message-wrapping
Revisit commit message processing
1 parent 710c020 commit 9b94879

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
@@ -131,7 +131,7 @@ export default class Repository {
131131
async getMergeMessage() {
132132
try {
133133
const contents = await fs.readFile(path.join(this.getGitDirectoryPath(), 'MERGE_MSG'), {encoding: 'utf8'});
134-
return contents;
134+
return contents.split(/\n/).filter(line => line.length > 0 && !line.startsWith('#')).join('\n');
135135
} catch (e) {
136136
return null;
137137
}

test/controllers/commit-controller.test.js

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

116116
describe('committing', function() {
117-
let workdirPath, repository;
117+
let workdirPath, repository, commit;
118118

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

124124
app = React.cloneElement(app, {repository, commit});
125125
});
@@ -136,6 +136,20 @@ describe('CommitController', function() {
136136
assert.strictEqual(repository.getCommitMessage(), '');
137137
});
138138

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

@@ -156,44 +170,58 @@ describe('CommitController', function() {
156170
});
157171

158172
describe('message formatting', function() {
159-
let commitSpy;
173+
let commitSpy, wrapper;
174+
160175
beforeEach(function() {
161176
commitSpy = sinon.stub().returns(Promise.resolve());
162177
app = React.cloneElement(app, {commit: commitSpy});
178+
wrapper = shallow(app, {disableLifecycleMethods: true});
163179
});
164180

165-
it('wraps the commit message body at 72 characters if github.automaticCommitMessageWrapping is true', async function() {
166-
config.set('github.automaticCommitMessageWrapping', false);
181+
describe('with automatic wrapping disabled', function() {
182+
beforeEach(function() {
183+
config.set('github.automaticCommitMessageWrapping', false);
184+
});
167185

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

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

@@ -289,10 +317,14 @@ describe('CommitController', function() {
289317
const editor = workspace.getActiveTextEditor();
290318
editor.setText('message in editor');
291319
await editor.save();
292-
wrapper.find('CommitView').prop('commit')('message in box');
293320

294-
await assert.async.strictEqual((await repository.getLastCommit()).getMessageSubject(), 'message in editor');
295-
await assert.async.isFalse(fs.existsSync(wrapper.instance().getCommitMessagePath()));
321+
await wrapper.find('CommitView').prop('commit')('message in box');
322+
323+
assert.strictEqual((await repository.getLastCommit()).getMessageSubject(), 'message in editor');
324+
assert.isFalse(fs.existsSync(wrapper.instance().getCommitMessagePath()));
325+
assert.isTrue(commit.calledWith('message in editor', {
326+
amend: false, coAuthors: [], verbatim: false,
327+
}));
296328
});
297329

298330
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)