-
Notifications
You must be signed in to change notification settings - Fork 407
Re-introduce commit message template #1756
Changes from all commits
f5ccd16
0a24012
49a1d31
3ebda2f
b6ef6d2
d895691
bcbbff8
b8e55ab
ca10d13
99fe542
40b015e
ade7de8
7c18ddb
7a73138
b16525c
b3cc2c3
5a7b2d4
b3588ef
4a1525b
2c2857e
f780ad1
5ee83db
6e45f0c
56d9f5e
d145084
8a40a74
63ffa69
c15f1d0
fd1d1cf
3d94405
acf502c
9b606ba
6379f30
d331372
13e7342
218f193
e2cc423
b3c9ec7
37fbb44
6299ae2
8aaf2ac
e8e39db
3e1cb40
a87b3a4
6f88f76
4e400bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,6 +53,17 @@ const DISABLE_COLOR_FLAGS = [ | |
| return acc; | ||
| }, []); | ||
|
|
||
| /** | ||
| * Expand config path name per | ||
| * https://git-scm.com/docs/git-config#git-config-pathname | ||
| * this regex attempts to get the specified user's home directory | ||
| * Ex: on Mac ~kuychaco/ is expanded to the specified user’s home directory (/Users/kuychaco) | ||
| * Regex translation: | ||
| * ^~ line starts with tilde | ||
| * ([^/]*)/ captures non-forwardslash characters before first slash | ||
| */ | ||
| const EXPAND_TILDE_REGEX = new RegExp('^~([^/]*)/'); | ||
|
|
||
| export default class GitShellOutStrategy { | ||
| static defaultExecArgs = { | ||
| stdin: null, | ||
|
|
@@ -422,6 +433,29 @@ export default class GitShellOutStrategy { | |
| return this.exec(args, {writeOperation: true}); | ||
| } | ||
|
|
||
| async fetchCommitMessageTemplate() { | ||
| let templatePath = await this.getConfig('commit.template'); | ||
| if (!templatePath) { | ||
| return null; | ||
| } | ||
|
|
||
| const homeDir = os.homedir(); | ||
|
|
||
| templatePath = templatePath.trim().replace(EXPAND_TILDE_REGEX, (_, user) => { | ||
| // if no user is specified, fall back to using the home directory. | ||
| return `${user ? path.join(path.dirname(homeDir), user) : homeDir}/`; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @smashwilson: can you test this on non-Mac operating systems? We want to make sure if a username is specified, it does the right thing for our Linux and or Windows users.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can do, and will report back 🙇 One thing that I can see right away is that this won't handle There are a few tilde-expansion libraries on npm, but unfortunately looking at their source it doesn't look like many of them handle all of this correctly either! I found one that uses etc-passwd to read its info, but I don't believe it has any accommodations for Windows. This may be a sign that this isn't important enough to hold up the 🚢 for 😉 |
||
| }); | ||
|
|
||
| if (!path.isAbsolute(templatePath)) { | ||
| templatePath = path.join(this.workingDir, templatePath); | ||
kuychaco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (!await fileExists(templatePath)) { | ||
| throw new Error(`Invalid commit template path set in Git config: ${templatePath}`); | ||
| } | ||
| return await fs.readFile(templatePath, {encoding: 'utf8'}); | ||
| } | ||
|
|
||
| unstageFiles(paths, commit = 'HEAD') { | ||
| if (paths.length === 0) { return Promise.resolve(null); } | ||
| const args = ['reset', commit, '--'].concat(paths.map(toGitPathSep)); | ||
|
|
@@ -889,7 +923,7 @@ export default class GitShellOutStrategy { | |
| let output; | ||
| try { | ||
| let args = ['config']; | ||
| if (local) { args.push('--local'); } | ||
| if (local || atom.inSpecMode()) { args.push('--local'); } | ||
| args = args.concat(option); | ||
| output = await this.exec(args); | ||
| } catch (err) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ import RemoteSet from '../remote-set'; | |
| import Commit from '../commit'; | ||
| import OperationStates from '../operation-states'; | ||
| import {addEvent} from '../../reporter-proxy'; | ||
| import {filePathEndsWith} from '../../helpers'; | ||
|
|
||
| /** | ||
| * State used when the working directory contains a valid git repository and can be interacted with. Performs | ||
|
|
@@ -39,20 +40,46 @@ export default class Present extends State { | |
| this.operationStates = new OperationStates({didUpdate: this.didUpdate.bind(this)}); | ||
|
|
||
| this.commitMessage = ''; | ||
| this.commitMessageTemplate = null; | ||
| this.fetchInitialMessage(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if the commit message, commit message template, and merge message tracking belong more naturally in With that said: we already have a bunch of behavior in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cool - should I open an issue to refactor this in the future? I'm on board with cleaning it up. |
||
|
|
||
| if (history) { | ||
| this.discardHistory.updateHistory(history); | ||
| } | ||
| } | ||
|
|
||
| setCommitMessage(message) { | ||
| setCommitMessage(message, {suppressUpdate} = {suppressUpdate: false}) { | ||
| this.commitMessage = message; | ||
| if (!suppressUpdate) { | ||
| this.didUpdate(); | ||
| } | ||
| } | ||
|
|
||
| setCommitMessageTemplate(template) { | ||
| this.commitMessageTemplate = template; | ||
| } | ||
|
|
||
| async fetchInitialMessage() { | ||
| const mergeMessage = await this.repository.getMergeMessage(); | ||
| const template = await this.fetchCommitMessageTemplate(); | ||
| if (template) { | ||
| this.commitMessageTemplate = template; | ||
| } | ||
| if (mergeMessage) { | ||
| this.setCommitMessage(mergeMessage); | ||
| } else if (template) { | ||
| this.setCommitMessage(template); | ||
| } | ||
| } | ||
|
|
||
| getCommitMessage() { | ||
| return this.commitMessage; | ||
| } | ||
|
|
||
| fetchCommitMessageTemplate() { | ||
| return this.git().fetchCommitMessageTemplate(); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's odd to me that Also: should the result of this git operation be cached? Fake edit: on reflection, caching this would cause some weird behavior because we don't have a filesystem watcher on the commit message template itself, so we wouldn't know when to invalidate that cache entry. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hahaha yup Katrina and I went through that exact conversation re caching. renamed the method to |
||
|
|
||
| getOperationStates() { | ||
| return this.operationStates; | ||
| } | ||
|
|
@@ -75,7 +102,8 @@ export default class Present extends State { | |
| this.didUpdate(); | ||
| } | ||
|
|
||
| observeFilesystemChange(paths) { | ||
| invalidateCacheAfterFilesystemChange(events) { | ||
| const paths = events.map(e => e.special || e.path); | ||
| const keys = new Set(); | ||
| for (let i = 0; i < paths.length; i++) { | ||
| const fullPath = paths[i]; | ||
|
|
@@ -88,18 +116,17 @@ export default class Present extends State { | |
| continue; | ||
| } | ||
|
|
||
| const endsWith = (...segments) => fullPath.endsWith(path.join(...segments)); | ||
| const includes = (...segments) => fullPath.includes(path.join(...segments)); | ||
|
|
||
| if (endsWith('.git', 'index')) { | ||
| if (filePathEndsWith(fullPath, '.git', 'index')) { | ||
| keys.add(Keys.stagedChangesSinceParentCommit); | ||
| keys.add(Keys.filePatch.all); | ||
| keys.add(Keys.index.all); | ||
| keys.add(Keys.statusBundle); | ||
| continue; | ||
| } | ||
|
|
||
| if (endsWith('.git', 'HEAD')) { | ||
| if (filePathEndsWith(fullPath, '.git', 'HEAD')) { | ||
| keys.add(Keys.branches); | ||
| keys.add(Keys.lastCommit); | ||
| keys.add(Keys.recentCommits); | ||
|
|
@@ -125,7 +152,7 @@ export default class Present extends State { | |
| continue; | ||
| } | ||
|
|
||
| if (endsWith('.git', 'config')) { | ||
| if (filePathEndsWith(fullPath, '.git', 'config')) { | ||
| keys.add(Keys.remotes); | ||
| keys.add(Keys.config.all); | ||
| keys.add(Keys.statusBundle); | ||
|
|
@@ -144,6 +171,47 @@ export default class Present extends State { | |
| } | ||
| } | ||
|
|
||
| isCommitMessageClean() { | ||
| if (this.commitMessage.trim() === '') { | ||
| return true; | ||
| } else if (this.commitMessageTemplate) { | ||
| return this.commitMessage === this.commitMessageTemplate; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| async updateCommitMessageAfterFileSystemChange(events) { | ||
| for (let i = 0; i < events.length; i++) { | ||
| const event = events[i]; | ||
|
|
||
| if (filePathEndsWith(event.path, '.git', 'MERGE_HEAD')) { | ||
| if (event.action === 'created') { | ||
| if (this.isCommitMessageClean()) { | ||
| this.setCommitMessage(await this.repository.getMergeMessage()); | ||
| } | ||
| } else if (event.action === 'deleted') { | ||
| this.setCommitMessage(this.commitMessageTemplate || ''); | ||
| } | ||
| } | ||
|
|
||
| if (filePathEndsWith(event.path, '.git', 'config')) { | ||
| // this won't catch changes made to the template file itself... | ||
| const template = await this.fetchCommitMessageTemplate(); | ||
| if (template === null) { | ||
| this.setCommitMessage(''); | ||
| } else if (this.commitMessageTemplate !== template) { | ||
| this.setCommitMessage(template); | ||
| } | ||
| this.setCommitMessageTemplate(template); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| observeFilesystemChange(events) { | ||
| this.invalidateCacheAfterFilesystemChange(events); | ||
| this.updateCommitMessageAfterFileSystemChange(events); | ||
| } | ||
|
|
||
| refresh() { | ||
annthurium marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| this.cache.clear(); | ||
| this.didUpdate(); | ||
|
|
@@ -280,7 +348,10 @@ export default class Present extends State { | |
| Keys.filePatch.all, | ||
| Keys.index.all, | ||
| ], | ||
| () => this.git().abortMerge(), | ||
| async () => { | ||
| await this.git().abortMerge(); | ||
| this.setCommitMessage(this.commitMessageTemplate || ''); | ||
annthurium marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| ); | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.