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

Commit 7764134

Browse files
authored
Merge pull request #2587 from atom/consent-is-important
Accept and cancel buttons on Git identity panel
2 parents a747a1b + c5cd601 commit 7764134

File tree

7 files changed

+114
-30
lines changed

7 files changed

+114
-30
lines changed

lib/controllers/git-tab-controller.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import path from 'path';
33
import React from 'react';
44
import PropTypes from 'prop-types';
55
import {TextBuffer} from 'atom';
6-
import {CompositeDisposable} from 'event-kit';
76

87
import GitTabView from '../views/git-tab-view';
98
import UserStore from '../models/user-store';
@@ -87,11 +86,6 @@ export default class GitTabController extends React.Component {
8786
login: this.props.loginModel,
8887
config: this.props.config,
8988
});
90-
91-
this.subs = new CompositeDisposable(
92-
this.usernameBuffer.onDidStopChanging(this.setUsername),
93-
this.emailBuffer.onDidStopChanging(this.setEmail),
94-
);
9589
}
9690

9791
static getDerivedStateFromProps(props, state) {
@@ -142,6 +136,8 @@ export default class GitTabController extends React.Component {
142136

143137
toggleIdentityEditor={this.toggleIdentityEditor}
144138
closeIdentityEditor={this.closeIdentityEditor}
139+
setLocalIdentity={this.setLocalIdentity}
140+
setGlobalIdentity={this.setGlobalIdentity}
145141
openInitializeDialog={this.props.openInitializeDialog}
146142
openFiles={this.props.openFiles}
147143
discardWorkDirChangesForPaths={this.props.discardWorkDirChangesForPaths}
@@ -371,18 +367,26 @@ export default class GitTabController extends React.Component {
371367

372368
closeIdentityEditor = () => this.setState({editingIdentity: false})
373369

374-
setUsername = () => {
370+
setLocalIdentity = () => this.setIdentity({});
371+
372+
setGlobalIdentity = () => this.setIdentity({global: true});
373+
374+
async setIdentity(options) {
375375
const newUsername = this.usernameBuffer.getText();
376-
if (newUsername !== this.props.username) {
377-
this.props.repository.setConfig('user.name', newUsername, {global: true});
376+
const newEmail = this.emailBuffer.getText();
377+
378+
if (newUsername.length > 0 || options.global) {
379+
await this.props.repository.setConfig('user.name', newUsername, options);
380+
} else {
381+
await this.props.repository.unsetConfig('user.name');
378382
}
379-
}
380383

381-
setEmail = () => {
382-
const newEmail = this.emailBuffer.getText();
383-
if (newEmail !== this.props.email) {
384-
this.props.repository.setConfig('user.email', newEmail, {global: true});
384+
if (newEmail.length > 0 || options.global) {
385+
await this.props.repository.setConfig('user.email', newEmail, options);
386+
} else {
387+
await this.props.repository.unsetConfig('user.email');
385388
}
389+
this.closeIdentityEditor();
386390
}
387391

388392
restoreFocus() {

lib/views/git-identity-view.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ export default class GitIdentityView extends React.Component {
77
// Model
88
usernameBuffer: PropTypes.object.isRequired,
99
emailBuffer: PropTypes.object.isRequired,
10+
canWriteLocal: PropTypes.bool.isRequired,
1011

1112
// Action methods
13+
setLocal: PropTypes.func.isRequired,
14+
setGlobal: PropTypes.func.isRequired,
1215
close: PropTypes.func.isRequired,
1316
};
1417

@@ -19,19 +22,29 @@ export default class GitIdentityView extends React.Component {
1922
Git Identity
2023
</h1>
2124
<p className="github-GitIdentity-explanation">
22-
Please set the username and email address that you wish to use to
23-
author git commits.
25+
Please set the username and email address that you wish to use to author git commits. This will write to the
26+
<code>user.name</code> and <code>user.email</code> values in your git configuration at the chosen scope.
2427
</p>
2528
<div className="github-GitIdentity-text">
2629
<AtomTextEditor mini placeholderText="name" buffer={this.props.usernameBuffer} />
2730
<AtomTextEditor mini placeholderText="email address" buffer={this.props.emailBuffer} />
2831
</div>
2932
<div className="github-GitIdentity-buttons">
33+
<button className="btn" onClick={this.props.close}>
34+
Cancel
35+
</button>
36+
<button
37+
className="btn btn-primary"
38+
title="Configure git for this repository"
39+
onClick={this.props.setLocal}
40+
disabled={!this.props.canWriteLocal}>
41+
Use for this repository
42+
</button>
3043
<button
3144
className="btn btn-primary"
32-
onClick={this.props.close}
33-
disabled={this.props.usernameBuffer.isEmpty() || this.props.emailBuffer.isEmpty()}>
34-
Continue
45+
title="Configure git globally for your operating system user account"
46+
onClick={this.props.setGlobal}>
47+
Use for all repositories
3548
</button>
3649
</div>
3750
</div>

lib/views/git-tab-view.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export default class GitTabView extends React.Component {
5454
tooltips: PropTypes.object.isRequired,
5555

5656
toggleIdentityEditor: PropTypes.func.isRequired,
57+
setLocalIdentity: PropTypes.func.isRequired,
58+
setGlobalIdentity: PropTypes.func.isRequired,
5759
closeIdentityEditor: PropTypes.func.isRequired,
5860
openInitializeDialog: PropTypes.func.isRequired,
5961
abortMerge: PropTypes.func.isRequired,
@@ -267,6 +269,9 @@ export default class GitTabView extends React.Component {
267269
<GitIdentityView
268270
usernameBuffer={this.props.usernameBuffer}
269271
emailBuffer={this.props.emailBuffer}
272+
canWriteLocal={this.props.repository.isPresent()}
273+
setLocal={this.props.setLocalIdentity}
274+
setGlobal={this.props.setGlobalIdentity}
270275
close={this.props.closeIdentityEditor}
271276
/>
272277
);

styles/git-identity.less

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
}
2727

2828
&-buttons {
29-
//
29+
.btn {
30+
margin: @component-padding/2;
31+
}
3032
}
3133
}

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,51 @@ describe('GitTabController', function() {
230230
assert.strictEqual(wrapper.find('GitTabView').prop('emailBuffer'), emailBuffer);
231231
assert.strictEqual(emailBuffer.getText(), '[email protected]');
232232
});
233+
234+
it('sets repository-local identity', async function() {
235+
const repository = await buildRepository(await cloneRepository('three-files'));
236+
const setConfig = sinon.stub(repository, 'setConfig');
237+
238+
const wrapper = mount(await buildApp(repository));
239+
240+
wrapper.find('GitTabView').prop('usernameBuffer').setText('changed');
241+
wrapper.find('GitTabView').prop('emailBuffer').setText('[email protected]');
242+
243+
await wrapper.find('GitTabView').prop('setLocalIdentity')();
244+
245+
assert.isTrue(setConfig.calledWith('user.name', 'changed', {}));
246+
assert.isTrue(setConfig.calledWith('user.email', '[email protected]', {}));
247+
});
248+
249+
it('sets account-global identity', async function() {
250+
const repository = await buildRepository(await cloneRepository('three-files'));
251+
const setConfig = sinon.stub(repository, 'setConfig');
252+
253+
const wrapper = mount(await buildApp(repository));
254+
255+
wrapper.find('GitTabView').prop('usernameBuffer').setText('changed');
256+
wrapper.find('GitTabView').prop('emailBuffer').setText('[email protected]');
257+
258+
await wrapper.find('GitTabView').prop('setGlobalIdentity')();
259+
260+
assert.isTrue(setConfig.calledWith('user.name', 'changed', {global: true}));
261+
assert.isTrue(setConfig.calledWith('user.email', '[email protected]', {global: true}));
262+
});
263+
264+
it('unsets config values when empty', async function() {
265+
const repository = await buildRepository(await cloneRepository('three-files'));
266+
const unsetConfig = sinon.stub(repository, 'unsetConfig');
267+
268+
const wrapper = mount(await buildApp(repository));
269+
270+
wrapper.find('GitTabView').prop('usernameBuffer').setText('');
271+
wrapper.find('GitTabView').prop('emailBuffer').setText('');
272+
273+
await wrapper.find('GitTabView').prop('setLocalIdentity')();
274+
275+
assert.isTrue(unsetConfig.calledWith('user.name'));
276+
assert.isTrue(unsetConfig.calledWith('user.email'));
277+
});
233278
});
234279

235280
describe('abortMerge()', function() {

test/fixtures/props/git-tab-props.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export async function gitTabViewProps(atomEnv, repository, overrides = {}) {
101101

102102
toggleIdentityEditor: () => {},
103103
closeIdentityEditor: () => {},
104+
setLocalIdentity: () => {},
105+
setGlobalIdentity: () => {},
104106
openInitializeDialog: () => {},
105107
abortMerge: () => {},
106108
commit: () => {},

test/views/git-identity-view.test.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ describe('GitIdentityView', function() {
1010
<GitIdentityView
1111
usernameBuffer={new TextBuffer()}
1212
emailBuffer={new TextBuffer()}
13+
canWriteLocal={true}
14+
setLocal={() => {}}
15+
setGlobal={() => {}}
1316
close={() => {}}
1417
{...override}
1518
/>
@@ -30,27 +33,37 @@ describe('GitIdentityView', function() {
3033
assert.strictEqual(getEditor('email address').prop('buffer'), emailBuffer);
3134
});
3235

33-
it('disables the "Continue" button if the name is blank', function() {
34-
const usernameBuffer = new TextBuffer();
35-
const wrapper = mount(buildApp({usernameBuffer}));
36+
it('disables the local repo button when canWriteLocal is false', function() {
37+
const wrapper = mount(buildApp({canWriteLocal: false}));
3638

37-
assert.isTrue(wrapper.find('.btn').prop('disabled'));
39+
assert.isTrue(wrapper.find('.btn').filterWhere(each => /this repository/.test(each.text())).prop('disabled'));
3840
});
3941

40-
it('disables the "Continue" button if the email is blank', function() {
41-
const emailBuffer = new TextBuffer();
42-
const wrapper = mount(buildApp({emailBuffer}));
42+
it('triggers a callback when "Use for this repository" is clicked', function() {
43+
const setLocal = sinon.spy();
44+
const wrapper = mount(buildApp({setLocal}));
45+
46+
wrapper.find('.btn').filterWhere(each => /this repository/.test(each.text())).simulate('click');
47+
48+
assert.isTrue(setLocal.called);
49+
});
50+
51+
it('triggers a callback when "Use for all repositories" is clicked', function() {
52+
const setGlobal = sinon.spy();
53+
const wrapper = mount(buildApp({setGlobal}));
54+
55+
wrapper.find('.btn').filterWhere(each => /all repositories/.test(each.text())).simulate('click');
4356

44-
assert.isTrue(wrapper.find('.btn').prop('disabled'));
57+
assert.isTrue(setGlobal.called);
4558
});
4659

47-
it('triggers a callback when "Continue" is clicked', function() {
60+
it('triggers a callback when "Cancel" is clicked', function() {
4861
const usernameBuffer = new TextBuffer({text: 'Me'});
4962
const emailBuffer = new TextBuffer({text: '[email protected]'});
5063
const close = sinon.spy();
5164
const wrapper = mount(buildApp({usernameBuffer, emailBuffer, close}));
5265

53-
wrapper.find('.btn').simulate('click');
66+
wrapper.find('.btn').filterWhere(each => /Cancel/.test(each.text())).simulate('click');
5467

5568
assert.isTrue(close.called);
5669
});

0 commit comments

Comments
 (0)