Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/controllers/commit-controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'path';

import React from 'react';
import ReactDom from 'react-dom';
import PropTypes from 'prop-types';
import {autobind} from 'core-decorators';
import {CompositeDisposable} from 'event-kit';
Expand Down Expand Up @@ -79,6 +80,10 @@ export default class CommitController extends React.Component {
}
}

componentDidMount() {
this.domNode = ReactDom.findDOMNode(this);
}

render() {
const message = this.getCommitMessage();
const operationStates = this.props.repository.getOperationStates();
Expand Down Expand Up @@ -132,15 +137,15 @@ export default class CommitController extends React.Component {
}

@autobind
async commit(message) {
async commit(message, options) {
if (this.getCommitMessageEditors().length > 0) {
await this.props.commit({filePath: this.getCommitMessagePath()});
await this.props.commit({filePath: this.getCommitMessagePath()}, options);
} else {
let formattedMessage = message;
if (this.props.config.get('github.automaticCommitMessageWrapping')) {
formattedMessage = wrapCommitMessage(formattedMessage);
}
await this.props.commit(formattedMessage);
await this.props.commit(formattedMessage, options);
}
}

Expand Down Expand Up @@ -269,7 +274,7 @@ export default class CommitController extends React.Component {
}

hasFocus() {
return this.element.contains(document.activeElement);
return this.domNode && this.domNode.contains(document.activeElement);
}
}

Expand Down
15 changes: 13 additions & 2 deletions lib/controllers/git-tab-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export default class GitTabController extends React.Component {
attemptStageAllOperation={this.attemptStageAllOperation}
prepareToCommit={this.prepareToCommit}
commit={this.commit}
undoLastCommit={this.undoLastCommit}
push={this.push}
pull={this.pull}
fetch={this.fetch}
Expand Down Expand Up @@ -305,8 +306,18 @@ export default class GitTabController extends React.Component {
}

@autobind
commit(message) {
return this.props.repository.commit(message);
commit(message, options) {
return this.props.repository.commit(message, options);
}

@autobind
async undoLastCommit() {
const repo = this.props.repository;
const lastCommit = await repo.getLastCommit();
if (lastCommit) {
repo.setRegularCommitMessage(lastCommit.message);
return this.props.repository.git.exec(['reset', '--soft', 'HEAD~']);
}
}

@autobind
Expand Down
2 changes: 2 additions & 0 deletions lib/controllers/recent-commits-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ export default class RecentCommitsController extends React.Component {
static propTypes = {
commits: PropTypes.arrayOf(PropTypes.object).isRequired,
isLoading: PropTypes.bool.isRequired,
undoLastCommit: PropTypes.func.isRequired,
}

render() {
return (
<RecentCommitsView
commits={this.props.commits}
isLoading={this.props.isLoading}
undoLastCommit={this.props.undoLastCommit}
/>
);
}
Expand Down
10 changes: 10 additions & 0 deletions lib/git-shell-out-strategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,12 +575,22 @@ export default class GitShellOutStrategy {
const fields = output.trim().split('\0');
const commits = [];
for (let i = 0; i < fields.length; i += 5) {
const body = fields[i + 4];

// There's probably a better way. I tried finding a regex to do it in one fell swoop but had no luck
const coAuthors = body.split(LINE_ENDING_REGEX).reduce((emails, line) => {
const match = line.match(/\s*Co-authored-by: .*<(.*)>\s*/);
if (match && match[1]) { emails.push(match[1]); }
return emails;
}, []);

commits.push({
sha: fields[i] && fields[i].trim(),
authorEmail: fields[i + 1] && fields[i + 1].trim(),
authorDate: parseInt(fields[i + 2], 10),
message: fields[i + 3],
body: fields[i + 4],
coAuthors,
});
}
return commits;
Expand Down
7 changes: 6 additions & 1 deletion lib/models/commit.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ export default class Commit {
return new Commit({unbornRef: UNBORN});
}

constructor({sha, authorEmail, authorDate, message, body, unbornRef}) {
constructor({sha, authorEmail, coAuthors, authorDate, message, body, unbornRef}) {
this.sha = sha;
this.authorEmail = authorEmail;
this.coAuthors = coAuthors;
this.authorDate = authorDate;
this.message = message;
this.body = body;
Expand All @@ -26,6 +27,10 @@ export default class Commit {
return this.authorDate;
}

getCoAuthorEmails() {
return this.coAuthors;
}

getMessage() {
return this.message;
}
Expand Down
3 changes: 1 addition & 2 deletions lib/models/repository-states/present.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,7 @@ export default class Present extends State {
commit(message, options) {
// eslint-disable-next-line no-shadow
return this.executePipelineAction('COMMIT', (message, options) => {
const opts = {...options, amend: this.isAmending()};
return this.git().commit(message, opts);
return this.git().commit(message, options);
}, message, options);
}

Expand Down
15 changes: 13 additions & 2 deletions lib/models/workdir-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,19 @@ export default class WorkdirCache {
async revParse(startPath) {
try {
const startDir = (await fsStat(startPath)).isDirectory() ? startPath : path.dirname(startPath);
const workDir = await CompositeGitStrategy.create(startDir).exec(['rev-parse', '--show-toplevel']);
return toNativePathSep(workDir.trim());

// Within a git worktree, return a non-empty string containing the path to the worktree root.
// Within a gitdir or outside of a worktree, return an empty string.
// Throw if startDir does not exist.
const topLevel = await CompositeGitStrategy.create(startDir).exec(['rev-parse', '--show-toplevel']);
if (/\S/.test(topLevel)) {
return toNativePathSep(topLevel.trim());
}

// Within a gitdir, return the absolute path to the gitdir.
// Outside of a gitdir or worktree, throw.
const gitDir = await CompositeGitStrategy.create(startDir).exec(['rev-parse', '--absolute-git-dir']);
return this.revParse(path.resolve(gitDir, '..'));
} catch (e) {
return null;
}
Expand Down
24 changes: 9 additions & 15 deletions lib/views/commit-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export default class CommitView extends React.Component {
this.subscriptions = new CompositeDisposable(
this.props.commandRegistry.add('atom-workspace', {
'github:commit': this.commit,
'github:amend-last-commit': this.commit.bind(this, {amend: true}),
'github:toggle-expanded-commit-message-editor': this.toggleExpandedCommitMessageEditor,
}),
this.props.config.onDidChange('github.automaticCommitMessageWrapping', () => this.forceUpdate()),
Expand Down Expand Up @@ -129,18 +130,6 @@ export default class CommitView extends React.Component {
className="btn github-CommitView-button github-CommitView-abortMerge is-secondary"
onClick={this.abortMerge}>Abort Merge</button>
}
{showAmendBox &&
<label className="github-CommitView-label input-label">
<input
ref={c => { this.refAmendCheckbox = c; }}
className="input-checkbox github-CommitView-amend"
type="checkbox"
onChange={this.handleAmendBoxChange}
checked={this.props.isAmending}
/>
Amend
</label>
}
<button
ref={c => { this.refCommitButton = c; }}
className="btn github-CommitView-button github-CommitView-commit"
Expand Down Expand Up @@ -232,10 +221,15 @@ export default class CommitView extends React.Component {
}

@autobind
async commit() {
if (await this.props.prepareToCommit() && this.isCommitButtonEnabled()) {
async commit(options) {
let message = this.editor.getText();
if (options.amend && !message) {
message = this.props.lastCommit.getMessage();
}
const readyToCommit = await this.props.prepareToCommit() && this.isCommitButtonEnabled();
if (readyToCommit || options.amend) {
try {
await this.props.commit(this.editor.getText());
await this.props.commit(message, options);
} catch (e) {
// do nothing
}
Expand Down
2 changes: 2 additions & 0 deletions lib/views/git-tab-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default class GitTabView extends React.Component {
initializeRepo: PropTypes.func.isRequired,
abortMerge: PropTypes.func.isRequired,
commit: PropTypes.func.isRequired,
undoLastCommit: PropTypes.func.isRequired,
prepareToCommit: PropTypes.func.isRequired,
resolveAsOurs: PropTypes.func.isRequired,
resolveAsTheirs: PropTypes.func.isRequired,
Expand Down Expand Up @@ -188,6 +189,7 @@ export default class GitTabView extends React.Component {
ref={c => { this.refRecentCommitController = c; }}
commits={this.props.recentCommits}
isLoading={this.props.isLoading}
undoLastCommit={this.props.undoLastCommit}
/>
</div>
);
Expand Down
45 changes: 38 additions & 7 deletions lib/views/recent-commits-view.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import cx from 'classnames';

import Timeago from './timeago';

class RecentCommitView extends React.Component {
static propTypes = {
commit: PropTypes.object.isRequired,
isMostRecent: PropTypes.bool,
undoLastCommit: PropTypes.func.isRequired,
};

render() {
const authorMoment = moment(this.props.commit.getAuthorDate() * 1000);

return (
<li className="github-RecentCommit">
<img className="github-RecentCommit-avatar"
src={'https://avatars.githubusercontent.com/u/e?email=' + encodeURIComponent(this.props.commit.getAuthorEmail()) + '&s=32'}
title={`${this.props.commit.getAuthorEmail()}`}
/>
<li className={cx('github-RecentCommit', {'most-recent': this.props.isMostRecent})}>
{this.renderAuthors()}
<span
className="github-RecentCommit-message"
title={this.props.commit.getMessage() + '\n\n' + this.props.commit.getBody()}>
{this.props.commit.getMessage()}
</span>
{this.props.isMostRecent && (
<button
className="btn github-RecentCommit-undoButton"
onClick={this.props.undoLastCommit}>
Undo
</button>
)}
<Timeago
className="github-RecentCommit-time"
type="time"
Expand All @@ -33,12 +40,31 @@ class RecentCommitView extends React.Component {
</li>
);
}

renderAuthors() {
const authorEmails = [this.props.commit.getAuthorEmail(), ...this.props.commit.getCoAuthorEmails()];

return (
<span className="github-RecentCommit-authors">
{authorEmails.map(authorEmail => {
return (
<img className="github-RecentCommit-avatar"
key={authorEmail}
src={'https://avatars.githubusercontent.com/u/e?email=' + encodeURIComponent(authorEmail) + '&s=32'}
title={authorEmail}
/>
);
})}
</span>
);
}
}

export default class RecentCommitsView extends React.Component {
static propTypes = {
commits: PropTypes.arrayOf(PropTypes.object).isRequired,
isLoading: PropTypes.bool.isRequired,
undoLastCommit: PropTypes.func.isRequired,
};

render() {
Expand Down Expand Up @@ -67,9 +93,14 @@ export default class RecentCommitsView extends React.Component {
} else {
return (
<ul className="github-RecentCommits-list">
{this.props.commits.map(commit => {
{this.props.commits.map((commit, i) => {
return (
<RecentCommitView key={commit.getSha()} commit={commit} />
<RecentCommitView
isMostRecent={i === 0}
key={commit.getSha()}
commit={commit}
undoLastCommit={this.props.undoLastCommit}
/>
);
})}
</ul>
Expand Down
12 changes: 9 additions & 3 deletions menus/git.cson
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
'label': 'Packages'
'submenu': [
{
'label': 'GitHub',
'label': 'GitHub'
'submenu': [
{
'label': 'Toggle Git Tab'
Expand Down Expand Up @@ -107,11 +107,17 @@
]
'atom-text-editor': [
{
'label': 'View Unstaged Changes',
'label': 'View Unstaged Changes'
'command': 'github:view-unstaged-changes-for-current-file'
}
{
'label': 'View Staged Changes',
'label': 'View Staged Changes'
'command': 'github:view-staged-changes-for-current-file'
}
]
'.most-recent': [
{
'label': 'Amend'
'command': 'github:amend-last-commit'
}
]
Loading