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

Commit 2c9ffe3

Browse files
authored
Merge pull request #2564 from atom/read-config-without-repo
Read and write git configuration without repository
2 parents fb57194 + f466251 commit 2c9ffe3

File tree

10 files changed

+293
-184
lines changed

10 files changed

+293
-184
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ jobs:
3939
- name: install dependencies
4040
shell: bash
4141
run: ${APM} ci
42+
- name: configure git
43+
shell: bash
44+
run: |
45+
git config --global user.name Hubot
46+
git config --global user.email [email protected]
4247
- name: run tests
4348
shell: bash
4449
run: ${ATOM} --test test/
@@ -79,6 +84,11 @@ jobs:
7984
- name: install dependencies
8085
shell: bash
8186
run: sh -c "${APM} ci"
87+
- name: configure git
88+
shell: bash
89+
run: |
90+
git config --global user.name Hubot
91+
git config --global user.email [email protected]
8292
- name: run tests
8393
shell: bash
8494
run: sh -c "${ATOM} --test test/"

lib/controllers/git-tab-controller.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,19 @@ export default class GitTabController extends React.Component {
371371

372372
closeIdentityEditor = () => this.setState({editingIdentity: false})
373373

374-
setUsername = () => this.props.repository.setConfig('user.name', this.usernameBuffer.getText(), {global: true})
374+
setUsername = () => {
375+
const newUsername = this.usernameBuffer.getText();
376+
if (newUsername !== this.props.username) {
377+
this.props.repository.setConfig('user.name', newUsername, {global: true});
378+
}
379+
}
375380

376-
setEmail = () => this.props.repository.setConfig('user.email', this.emailBuffer.getText(), {global: true})
381+
setEmail = () => {
382+
const newEmail = this.emailBuffer.getText();
383+
if (newEmail !== this.props.email) {
384+
this.props.repository.setConfig('user.email', newEmail, {global: true});
385+
}
386+
}
377387

378388
restoreFocus() {
379389
this.refView.map(view => view.setFocus(this.lastFocus));

lib/git-shell-out-strategy.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,12 +1007,12 @@ export default class GitShellOutStrategy {
10071007
let output;
10081008
try {
10091009
let args = ['config'];
1010-
if (local || atom.inSpecMode()) { args.push('--local'); }
1010+
if (local) { args.push('--local'); }
10111011
args = args.concat(option);
10121012
output = await this.exec(args);
10131013
} catch (err) {
1014-
if (err.code === 1) {
1015-
// No matching config found
1014+
if (err.code === 1 || err.code === 128) {
1015+
// No matching config found OR --local can only be used inside a git repository
10161016
return null;
10171017
} else {
10181018
throw err;
@@ -1025,7 +1025,7 @@ export default class GitShellOutStrategy {
10251025
setConfig(option, value, {replaceAll, global} = {}) {
10261026
let args = ['config'];
10271027
if (replaceAll) { args.push('--replace-all'); }
1028-
if (global && !atom.inSpecMode()) { args.push('--global'); }
1028+
if (global) { args.push('--global'); }
10291029
args = args.concat(option, value);
10301030
return this.exec(args, {writeOperation: true});
10311031
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
class CacheKey {
2+
constructor(primary, groups = []) {
3+
this.primary = primary;
4+
this.groups = groups;
5+
}
6+
7+
getPrimary() {
8+
return this.primary;
9+
}
10+
11+
getGroups() {
12+
return this.groups;
13+
}
14+
15+
removeFromCache(cache, withoutGroup = null) {
16+
cache.removePrimary(this.getPrimary());
17+
18+
const groups = this.getGroups();
19+
for (let i = 0; i < groups.length; i++) {
20+
const group = groups[i];
21+
if (group === withoutGroup) {
22+
continue;
23+
}
24+
25+
cache.removeFromGroup(group, this);
26+
}
27+
}
28+
29+
/* istanbul ignore next */
30+
toString() {
31+
return `CacheKey(${this.primary})`;
32+
}
33+
}
34+
35+
class GroupKey {
36+
constructor(group) {
37+
this.group = group;
38+
}
39+
40+
removeFromCache(cache) {
41+
for (const matchingKey of cache.keysInGroup(this.group)) {
42+
matchingKey.removeFromCache(cache, this.group);
43+
}
44+
}
45+
46+
/* istanbul ignore next */
47+
toString() {
48+
return `GroupKey(${this.group})`;
49+
}
50+
}
51+
52+
export const Keys = {
53+
statusBundle: new CacheKey('status-bundle'),
54+
55+
stagedChanges: new CacheKey('staged-changes'),
56+
57+
filePatch: {
58+
_optKey: ({staged}) => (staged ? 's' : 'u'),
59+
60+
oneWith: (fileName, options) => { // <-- Keys.filePatch
61+
const optKey = Keys.filePatch._optKey(options);
62+
const baseCommit = options.baseCommit || 'head';
63+
64+
const extraGroups = [];
65+
if (options.baseCommit) {
66+
extraGroups.push(`file-patch:base-nonhead:path-${fileName}`);
67+
extraGroups.push('file-patch:base-nonhead');
68+
} else {
69+
extraGroups.push('file-patch:base-head');
70+
}
71+
72+
return new CacheKey(`file-patch:${optKey}:${baseCommit}:${fileName}`, [
73+
'file-patch',
74+
`file-patch:opt-${optKey}`,
75+
`file-patch:opt-${optKey}:path-${fileName}`,
76+
...extraGroups,
77+
]);
78+
},
79+
80+
eachWithFileOpts: (fileNames, opts) => {
81+
const keys = [];
82+
for (let i = 0; i < fileNames.length; i++) {
83+
for (let j = 0; j < opts.length; j++) {
84+
keys.push(new GroupKey(`file-patch:opt-${Keys.filePatch._optKey(opts[j])}:path-${fileNames[i]}`));
85+
}
86+
}
87+
return keys;
88+
},
89+
90+
eachNonHeadWithFiles: fileNames => {
91+
return fileNames.map(fileName => new GroupKey(`file-patch:base-nonhead:path-${fileName}`));
92+
},
93+
94+
allAgainstNonHead: new GroupKey('file-patch:base-nonhead'),
95+
96+
eachWithOpts: (...opts) => opts.map(opt => new GroupKey(`file-patch:opt-${Keys.filePatch._optKey(opt)}`)),
97+
98+
all: new GroupKey('file-patch'),
99+
},
100+
101+
index: {
102+
oneWith: fileName => new CacheKey(`index:${fileName}`, ['index']),
103+
104+
all: new GroupKey('index'),
105+
},
106+
107+
lastCommit: new CacheKey('last-commit'),
108+
109+
recentCommits: new CacheKey('recent-commits'),
110+
111+
authors: new CacheKey('authors'),
112+
113+
branches: new CacheKey('branches'),
114+
115+
headDescription: new CacheKey('head-description'),
116+
117+
remotes: new CacheKey('remotes'),
118+
119+
config: {
120+
_optKey: options => (options.local ? 'l' : ''),
121+
122+
oneWith: (setting, options) => {
123+
const optKey = Keys.config._optKey(options);
124+
return new CacheKey(`config:${optKey}:${setting}`, ['config', `config:${optKey}`]);
125+
},
126+
127+
eachWithSetting: setting => [
128+
Keys.config.oneWith(setting, {local: true}),
129+
Keys.config.oneWith(setting, {local: false}),
130+
],
131+
132+
all: new GroupKey('config'),
133+
},
134+
135+
blob: {
136+
oneWith: sha => new CacheKey(`blob:${sha}`, ['blob']),
137+
},
138+
139+
// Common collections of keys and patterns for use with invalidate().
140+
141+
workdirOperationKeys: fileNames => [
142+
Keys.statusBundle,
143+
...Keys.filePatch.eachWithFileOpts(fileNames, [{staged: false}]),
144+
],
145+
146+
cacheOperationKeys: fileNames => [
147+
...Keys.workdirOperationKeys(fileNames),
148+
...Keys.filePatch.eachWithFileOpts(fileNames, [{staged: true}]),
149+
...fileNames.map(Keys.index.oneWith),
150+
Keys.stagedChanges,
151+
],
152+
153+
headOperationKeys: () => [
154+
Keys.headDescription,
155+
Keys.branches,
156+
...Keys.filePatch.eachWithOpts({staged: true}),
157+
Keys.filePatch.allAgainstNonHead,
158+
Keys.stagedChanges,
159+
Keys.lastCommit,
160+
Keys.recentCommits,
161+
Keys.authors,
162+
Keys.statusBundle,
163+
],
164+
};

0 commit comments

Comments
 (0)