diff --git a/lib/views/push-pull-menu-view.js b/lib/views/push-pull-menu-view.js
index 6999055cb8..8dad6be555 100644
--- a/lib/views/push-pull-menu-view.js
+++ b/lib/views/push-pull-menu-view.js
@@ -6,37 +6,62 @@ import etch from 'etch'
export default class PushPullMenuView {
constructor (props) {
this.props = props
+ this.checkRemote()
+ this.push = this.push.bind(this)
etch.initialize(this)
}
update (props) {
- this.props = props
+ this.props = Object.assign({}, this.props, props)
+ this.checkRemote()
return etch.update(this)
}
+ checkRemote () {
+ if (!this.props.remoteName) {
+ this.errorMessage = `Note: No remote detected for branch ${this.props.branchName}. Pushing will set up a remote tracking branch on remote repo 'origin'`
+ } else {
+ this.errorMessage = ''
+ }
+ }
+
render () {
return (
)
}
+
+ async push (event) {
+ try {
+ await this.props.push({force: event.metaKey, setUpstream: !this.props.remoteName})
+ } catch (error) {
+ if (error.stdErr.match(/rejected[\s\S]*failed to push/)) {
+ this.errorMessage = 'Push rejected.\nTry pulling before pushing again. Or to force push hold `cmd` while clicking'
+ } else {
+ this.errorMessage = error.stdErr
+ }
+ return etch.update(this)
+ }
+ }
}
class Tooltip {
diff --git a/lib/views/push-pull-view.js b/lib/views/push-pull-view.js
index e0705b349d..aed14fdacc 100644
--- a/lib/views/push-pull-view.js
+++ b/lib/views/push-pull-view.js
@@ -16,11 +16,11 @@ export default class PushPullView {
render () {
return (
-
+
- {this.props.behindCount !== 0 ? `${this.props.behindCount}` : ''}
+ {this.props.behindCount ? `${this.props.behindCount}` : ''}
- {this.props.aheadCount !== 0 ? `${this.props.aheadCount}` : ''}
+ {this.props.aheadCount ? `${this.props.aheadCount}` : ''}
)
}
diff --git a/styles/push-pull-menu-view.less b/styles/push-pull-menu-view.less
index b8e96c2475..7a7ba9a3c6 100644
--- a/styles/push-pull-menu-view.less
+++ b/styles/push-pull-menu-view.less
@@ -31,4 +31,21 @@
.btn-tooltip-wrapper .icon {
pointer-events: none;
}
+
+ &-message {
+ max-height: 4.8em;
+ margin-top: @component-padding;
+ padding: @component-padding / 2;
+ line-height: 1.4;
+ white-space: normal;
+ text-align: left;
+ overflow-y: auto;
+ border: 1px solid @base-border-color;
+ border-radius: @component-border-radius;
+ background-color: @inset-panel-background-color;
+
+ &:empty {
+ display: none;
+ }
+ }
}
diff --git a/test/controllers/status-bar-tile-controller.test.js b/test/controllers/status-bar-tile-controller.test.js
index 55f2a3db55..f9d784670b 100644
--- a/test/controllers/status-bar-tile-controller.test.js
+++ b/test/controllers/status-bar-tile-controller.test.js
@@ -234,6 +234,57 @@ describe('StatusBarTileController', () => {
await controller.getLastModelDataRefreshPromise()
assert.isFalse(pullButton.disabled)
})
+
+ it('disables the fetch and pull buttons when there is no remote tracking branch and displays informative message', async () => {
+ const {localRepoPath} = await setUpLocalAndRemoteRepositories()
+ const repository = await buildRepository(localRepoPath)
+ await repository.git.exec(['checkout', '-b', 'new-branch'])
+
+ const controller = new StatusBarTileController({workspace, repository})
+ await controller.getLastModelDataRefreshPromise()
+
+ const pushPullMenuView = controller.pushPullMenuView
+ const {pushButton, pullButton, fetchButton, message} = pushPullMenuView.refs
+
+ assert.isTrue(pullButton.disabled)
+ assert.isTrue(fetchButton.disabled)
+ assert.match(message.innerHTML, /No remote detected.*Pushing will set up a remote tracking branch/)
+
+ pushButton.dispatchEvent(new MouseEvent('click'))
+ await repository.refresh()
+ await controller.getLastModelDataRefreshPromise()
+
+ assert.isFalse(pullButton.disabled)
+ assert.isFalse(fetchButton.disabled)
+ assert.equal(message.textContent, '')
+ })
+
+ it('displays an error message if push fails and allows force pushing if meta key is pressed', async () => {
+ const {localRepoPath} = await setUpLocalAndRemoteRepositories()
+ const repository = await buildRepository(localRepoPath)
+ await repository.git.exec(['reset', '--hard', 'head~2'])
+ await repository.git.commit('another commit', {allowEmpty: true})
+
+ const controller = new StatusBarTileController({workspace, repository})
+ await controller.getLastModelDataRefreshPromise()
+
+ const pushPullMenuView = controller.pushPullMenuView
+ const {pushButton, pullButton, message} = pushPullMenuView.refs
+
+ assert.equal(pushButton.textContent, 'Push (1)')
+ assert.equal(pullButton.textContent, 'Pull (2)')
+
+ pushButton.dispatchEvent(new MouseEvent('click'))
+ await etch.getScheduler().getNextUpdatePromise()
+ assert.match(message.innerHTML, /Push rejected/)
+
+ pushButton.dispatchEvent(new MouseEvent('click', {metaKey: true}))
+ await repository.refresh()
+ await controller.getLastModelDataRefreshPromise()
+
+ assert.equal(pushButton.textContent, 'Push ')
+ assert.equal(pullButton.textContent, 'Pull ')
+ })
})
})
diff --git a/test/git-shell-out-strategy.test.js b/test/git-shell-out-strategy.test.js
index 7d85731fc9..b917f5dea0 100644
--- a/test/git-shell-out-strategy.test.js
+++ b/test/git-shell-out-strategy.test.js
@@ -370,15 +370,15 @@ describe('Git commands', () => {
})
})
- describe('getRemote(branchName)', () => {
+ describe('getRemoteForBranch(branchName)', () => {
it('returns the name of the remote associated with the branch, and null if none exists', async () => {
const workingDirPath = await cloneRepository('three-files')
const git = new GitShellOutStrategy(workingDirPath)
- assert.equal(await git.getRemote('master'), 'origin')
+ assert.equal(await git.getRemoteForBranch('master'), 'origin')
await git.exec(['remote', 'rename', 'origin', 'foo'])
- assert.equal(await git.getRemote('master'), 'foo')
+ assert.equal(await git.getRemoteForBranch('master'), 'foo')
await git.exec(['remote', 'rm', 'foo'])
- assert.isNull(await git.getRemote('master'))
+ assert.isNull(await git.getRemoteForBranch('master'))
})
})
diff --git a/test/models/repository.test.js b/test/models/repository.test.js
index b507a73029..8ce4319375 100644
--- a/test/models/repository.test.js
+++ b/test/models/repository.test.js
@@ -476,17 +476,17 @@ describe('Repository', function () {
})
})
- describe('getRemote(branchName)', () => {
+ describe('getRemoteForBranch(branchName)', () => {
it('returns the remote name associated to the supplied branch name, null if none exists', async () => {
const {localRepoPath} = await setUpLocalAndRemoteRepositories({remoteAhead: true})
const localRepo = await buildRepository(localRepoPath)
- assert.equal(await localRepo.getRemote('master'), 'origin')
+ assert.equal(await localRepo.getRemoteForBranch('master'), 'origin')
await localRepo.git.exec(['remote', 'rename', 'origin', 'foo'])
- assert.equal(await localRepo.getRemote('master'), 'foo')
+ assert.equal(await localRepo.getRemoteForBranch('master'), 'foo')
await localRepo.git.exec(['remote', 'rm', 'foo'])
- assert.isNull(await localRepo.getRemote('master'))
+ assert.isNull(await localRepo.getRemoteForBranch('master'))
})
})